diff --git a/3d-viewer/3d_aux.cpp b/3d-viewer/3d_aux.cpp deleted file mode 100644 index 1fed39acf9..0000000000 --- a/3d-viewer/3d_aux.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2012 Wayne Stambaugh - * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_aux.cpp - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include <3d_viewer.h> -#include <3d_canvas.h> -#include -#include - - -void S3D_MASTER::ObjectCoordsTo3DUnits( std::vector< S3D_VERTEX >& aVertices ) -{ - /* adjust object scale, rotation and offset position */ - for( unsigned ii = 0; ii < aVertices.size(); ii++ ) - { - aVertices[ii].x *= m_MatScale.x; - aVertices[ii].y *= m_MatScale.y; - aVertices[ii].z *= m_MatScale.z; - - // adjust rotation - if( m_MatRotation.x ) - { - double a = aVertices[ii].y; - double b = aVertices[ii].z; - RotatePoint( &a, &b, m_MatRotation.x * 10 ); - aVertices[ii].y = (float)a; - aVertices[ii].z = (float)b; - } - - if( m_MatRotation.y ) - { - double a = aVertices[ii].z; - double b = aVertices[ii].x; - RotatePoint( &a, &b, m_MatRotation.x * 10 ); - aVertices[ii].z = (float)a; - aVertices[ii].x = (float)b; - } - - if( m_MatRotation.z ) - { - double a = aVertices[ii].x; - double b = aVertices[ii].y; - RotatePoint( &a, &b, m_MatRotation.x * 10 ); - aVertices[ii].x = (float)a; - aVertices[ii].y = (float)b; - } - - /* adjust offset position (offset is given in UNIT 3D (0.1 inch) */ - aVertices[ii].x += m_MatPosition.x * SCALE_3D_CONV; - aVertices[ii].y += m_MatPosition.y * SCALE_3D_CONV; - aVertices[ii].z += m_MatPosition.z * SCALE_3D_CONV; - } -} - - -void TransfertToGLlist( std::vector< S3D_VERTEX >& aVertices, double aBiuTo3DUnits ) -{ - unsigned ii; - GLfloat ax, ay, az, bx, by, bz, nx, ny, nz, r; - - /* ignore faces with less than 3 points */ - if( aVertices.size() < 3 ) - return; - - /* calculate normal direction */ - ax = aVertices[1].x - aVertices[0].x; - ay = aVertices[1].y - aVertices[0].y; - az = aVertices[1].z - aVertices[0].z; - - bx = aVertices[2].x - aVertices[0].x; - by = aVertices[2].y - aVertices[0].y; - bz = aVertices[2].z - aVertices[0].z; - - nx = ay * bz - az * by; - ny = az * bx - ax * bz; - nz = ax * by - ay * bx; - - r = sqrt( nx * nx + ny * ny + nz * nz ); - - if( r >= 0.000001 ) /* avoid division by zero */ - { - nx /= r; - ny /= r; - nz /= r; - glNormal3f( nx, ny, nz ); - } - - /* glBegin/glEnd */ - switch( aVertices.size() ) - { - case 3: - glBegin( GL_TRIANGLES ); - break; - - case 4: - glBegin( GL_QUADS ); - break; - - default: - glBegin( GL_POLYGON ); - break; - } - - /* draw polygon/triangle/quad */ - for( ii = 0; ii < aVertices.size(); ii++ ) - { - glVertex3d( aVertices[ii].x * aBiuTo3DUnits, - aVertices[ii].y * aBiuTo3DUnits, - aVertices[ii].z * aBiuTo3DUnits ); - } - - glEnd(); -} - -S3DPOINT_VALUE_CTRL::S3DPOINT_VALUE_CTRL( wxWindow* aParent, wxBoxSizer* aBoxSizer ) -{ - wxString text; - - wxFlexGridSizer* gridSizer = new wxFlexGridSizer( 0, 2, 0, 0 ); - gridSizer->AddGrowableCol( 1 ); - gridSizer->SetFlexibleDirection( wxHORIZONTAL ); - gridSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - aBoxSizer->Add( gridSizer, 0, wxEXPAND, 5 ); - - wxStaticText* msgtitle = new wxStaticText( aParent, wxID_ANY, wxT( "X:" ) ); - gridSizer->Add( msgtitle, 0, wxALL , 5 ); - - m_XValueCtrl = new wxTextCtrl( aParent, wxID_ANY, wxEmptyString, - wxDefaultPosition,wxDefaultSize, 0 ); - gridSizer->Add( m_XValueCtrl, 0, wxALL|wxEXPAND, 5 ); - - msgtitle = new wxStaticText( aParent, wxID_ANY, wxT( "Y:" ), wxDefaultPosition, - wxDefaultSize, 0 ); - gridSizer->Add( msgtitle, 0, wxALL, 5 ); - - m_YValueCtrl = new wxTextCtrl( aParent, wxID_ANY, wxEmptyString, - wxDefaultPosition, wxDefaultSize, 0 ); - gridSizer->Add( m_YValueCtrl, 0, wxALL|wxEXPAND, 5 ); - - msgtitle = new wxStaticText( aParent, wxID_ANY, wxT( "Z:" ), wxDefaultPosition, - wxDefaultSize, 0 ); - gridSizer->Add( msgtitle, 0, wxALL, 5 ); - - m_ZValueCtrl = new wxTextCtrl( aParent, wxID_ANY, wxEmptyString, - wxDefaultPosition, wxDefaultSize, 0 ); - gridSizer->Add( m_ZValueCtrl, 0, wxALL|wxEXPAND, 5 ); -} - - -S3DPOINT_VALUE_CTRL::~S3DPOINT_VALUE_CTRL() -{ - // Nothing to delete: all items are managed by the parent window. -} - - -S3DPOINT S3DPOINT_VALUE_CTRL::GetValue() -{ - S3DPOINT value; - double dtmp; - - m_XValueCtrl->GetValue().ToDouble( &dtmp ); - value.x = dtmp; - m_YValueCtrl->GetValue().ToDouble( &dtmp ); - value.y = dtmp; - m_ZValueCtrl->GetValue().ToDouble( &dtmp ); - value.z = dtmp; - return value; -} - - -void S3DPOINT_VALUE_CTRL::SetValue( S3DPOINT vertex ) -{ - wxString text; - - text.Printf( wxT( "%f" ), vertex.x ); - m_XValueCtrl->Clear(); - m_XValueCtrl->AppendText( text ); - - text.Printf( wxT( "%f" ), vertex.y ); - m_YValueCtrl->Clear(); - m_YValueCtrl->AppendText( text ); - - text.Printf( wxT( "%f" ), vertex.z ); - m_ZValueCtrl->Clear(); - m_ZValueCtrl->AppendText( text ); -} - - -void S3DPOINT_VALUE_CTRL::Enable( bool onoff ) -{ - m_XValueCtrl->Enable( onoff ); - m_YValueCtrl->Enable( onoff ); - m_ZValueCtrl->Enable( onoff ); -} diff --git a/3d-viewer/3d_cache/3d_cache.cpp b/3d-viewer/3d_cache/3d_cache.cpp index 2d53f748cc..a75bc63877 100644 --- a/3d-viewer/3d_cache/3d_cache.cpp +++ b/3d-viewer/3d_cache/3d_cache.cpp @@ -224,10 +224,10 @@ SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, S3D_CACHE_ENTRY** aCach if( mi != m_CacheMap.end() ) { wxFileName fname( full3Dpath ); - bool reload = false; - if( fname.FileExists() ) - { + if( fname.FileExists() ) // Only check if file exists. If not, it will + { // use the same model in cache. + bool reload = false; wxDateTime fmdate = fname.GetModificationTime(); if( fmdate != mi->second->modTime ) @@ -242,20 +242,20 @@ SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, S3D_CACHE_ENTRY** aCach reload = true; } } - } - if( reload ) - { - if( NULL != mi->second->sceneData ) + if( reload ) { - S3D::DestroyNode( mi->second->sceneData ); - mi->second->sceneData = NULL; + if( NULL != mi->second->sceneData ) + { + S3D::DestroyNode( mi->second->sceneData ); + mi->second->sceneData = NULL; + } + + if( NULL != mi->second->renderData ) + S3D::Destroy3DModel( &mi->second->renderData ); + + mi->second->sceneData = m_Plugins->Load3DModel( full3Dpath, mi->second->pluginInfo ); } - - if( NULL != mi->second->renderData ) - S3D::Destroy3DModel( &mi->second->renderData ); - - mi->second->sceneData = m_Plugins->Load3DModel( full3Dpath, mi->second->pluginInfo ); } if( NULL != aCachePtr ) diff --git a/3d-viewer/3d_cache/3d_info.h b/3d-viewer/3d_cache/3d_info.h index c0ddd4bfde..ff9910a8c5 100644 --- a/3d-viewer/3d_cache/3d_info.h +++ b/3d-viewer/3d_cache/3d_info.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015 Cirilo Bernardo - * Copyright (C) 2015 Mario Luzeiro + * Copyright (C) 2015 Mario Luzeiro * Copyright (C) 2011 Wayne Stambaugh * Copyright (C) 2004 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. @@ -39,14 +39,10 @@ struct S3D_INFO { - SGPOINT scale; ///< scaling factors for the 3D footprint shape - SGPOINT rotation; ///< an X,Y,Z rotation (unit = degrees) for the 3D shape - SGPOINT offset; ///< an offset (unit = inch) for the 3D shape - // note: the models are treated in a peculiar fashion since it is the - // SCALE which is applied first, followed by the ROTATION and finally - // the TRANSLATION/Offset (S-R-T). The usual order of operations is T-R-S. - - wxString filename; ///< The 3D shape filename in 3D library + SGPOINT m_Scale; ///< scaling factors for the 3D footprint shape + SGPOINT m_Rotation; ///< an X,Y,Z rotation (unit = degrees) for the 3D shape + SGPOINT m_Offset; ///< an offset (unit = inch) for the 3D shape + wxString m_Filename; ///< The 3D shape filename in 3D library }; #endif // INFO_3D_H diff --git a/3d-viewer/3d_cache/dialogs/dlg_select_3dmodel.cpp b/3d-viewer/3d_cache/dialogs/dlg_select_3dmodel.cpp index 4c75d81389..aba261308b 100644 --- a/3d-viewer/3d_cache/dialogs/dlg_select_3dmodel.cpp +++ b/3d-viewer/3d_cache/dialogs/dlg_select_3dmodel.cpp @@ -1,6 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * + * Copyright (C) 2016 Mario Luzeiro * Copyright (C) 2016 Cirilo Bernardo * * This program is free software; you can redistribute it and/or @@ -27,8 +28,9 @@ #include "project.h" #include "3d_cache/3d_info.h" #include "3d_cache/3d_cache.h" -#include "3d_cache/dialogs/panel_prev_model.h" #include "3d_cache_dialogs.h" +#include <3d_model_viewer/c3d_model_viewer.h> +#include #define ID_FILE_TREE ( wxID_LAST + 1 ) #define ID_SET_DIR ( ID_FILE_TREE + 1 ) @@ -60,15 +62,13 @@ DLG_SELECT_3DMODEL::DLG_SELECT_3DMODEL( wxWindow* aParent, S3D_CACHE* aCacheMana wxBoxSizer* bSizer0 = new wxBoxSizer( wxVERTICAL ); - wxBoxSizer* bSizer1; - bSizer1 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer1 = new wxBoxSizer( wxHORIZONTAL ); - wxBoxSizer* bSizer2; - bSizer2 = new wxBoxSizer( wxVERTICAL ); + wxBoxSizer* bSizer2 = new wxBoxSizer( wxVERTICAL ); // set to NULL to avoid segfaults when m_FileTree is instantiated // and wxGenericDirCtrl events are posted - m_preview = NULL; + m_modelViewer = NULL; dirChoices = NULL; m_FileTree = new wxGenericDirCtrl( this, ID_FILE_TREE, prevModelSelectDir, wxDefaultPosition, @@ -83,14 +83,12 @@ DLG_SELECT_3DMODEL::DLG_SELECT_3DMODEL( wxWindow* aParent, S3D_CACHE* aCacheMana bSizer2->Add( m_FileTree, 1, wxEXPAND | wxALL, 5 ); bSizer1->Add( bSizer2, 1, wxEXPAND, 5 ); - // m_preview must me instantiated after m_FileTree or else it will not - // function as desired due to the constructor depending on the existence - // of m_FileTree to determine the previewer's configuration - wxBoxSizer* previewSizer; - previewSizer = new wxBoxSizer( wxVERTICAL ); - m_preview = new PANEL_PREV_3D( this, m_cache ); - previewSizer->Add( m_preview, 1, wxEXPAND | wxALL, 5 ); - bSizer1->Add( previewSizer, 0, wxEXPAND, 5 ); + m_modelViewer = new C3D_MODEL_VIEWER( this, + COGL_ATT_LIST::GetAttributesList( true ), + m_cache ); + m_modelViewer->SetMinSize( wxSize( 512, 384 ) ); + + bSizer1->Add( m_modelViewer, 0, wxCENTER, 5 ); // create the filter list if( NULL != m_cache ) @@ -133,8 +131,7 @@ DLG_SELECT_3DMODEL::DLG_SELECT_3DMODEL( wxWindow* aParent, S3D_CACHE* aCacheMana // Add the path choice box and config button wxBoxSizer* hboxDirChoice = new wxBoxSizer( wxHORIZONTAL ); - dirChoices = new wxChoice( this, ID_SET_DIR, wxDefaultPosition, - wxSize( 320, 20 ) ); + dirChoices = new wxChoice( this, ID_SET_DIR, wxDefaultPosition, wxSize( 320, 20 ) ); dirChoices->SetMinSize( wxSize( 320, 12 ) ); wxStaticText* stDirChoice = new wxStaticText( this, -1, _( "Paths:" ) ); @@ -150,6 +147,7 @@ DLG_SELECT_3DMODEL::DLG_SELECT_3DMODEL( wxWindow* aParent, S3D_CACHE* aCacheMana hSizer1->AddButton( btn_OK ); hSizer1->AddButton( btn_Cancel ); hSizer1->Realize(); + bSizer0->Add( bSizer1, 1, wxALL | wxEXPAND, 5 ); bSizer0->Add( hboxDirChoice, 0, wxALL | wxEXPAND, 5 ); bSizer0->Add( hSizer1, 0, wxALL | wxEXPAND, 5 ); @@ -159,6 +157,9 @@ DLG_SELECT_3DMODEL::DLG_SELECT_3DMODEL( wxWindow* aParent, S3D_CACHE* aCacheMana this->SetSizerAndFit( bSizer0 ); this->Layout(); this->Centre( wxBOTH ); + + m_modelViewer->Refresh(); + m_modelViewer->SetFocus(); } @@ -167,26 +168,30 @@ bool DLG_SELECT_3DMODEL::TransferDataFromWindow() if( NULL == m_model || NULL == m_FileTree ) return true; - m_model->scale.x = 1.0; - m_model->scale.y = 1.0; - m_model->scale.z = 1.0; + m_model->m_Scale.x = 1.0; + m_model->m_Scale.y = 1.0; + m_model->m_Scale.z = 1.0; - m_model->rotation.x = 0.0; - m_model->rotation.y = 0.0; - m_model->rotation.z = 0.0; + m_model->m_Rotation.x = 0.0; + m_model->m_Rotation.y = 0.0; + m_model->m_Rotation.z = 0.0; - m_model->offset = m_model->rotation; - m_model->filename.clear(); + m_model->m_Offset = m_model->m_Rotation; + m_model->m_Filename.clear(); - wxString fname = m_FileTree->GetFilePath(); + wxString name = m_FileTree->GetFilePath(); - if( fname.empty() ) + if( name.empty() ) return true; m_previousDir = m_FileTree->GetPath(); m_previousFilterIndex = m_FileTree->GetFilterIndex(); - m_preview->GetModelData( m_model ); + // file selection mode: retrieve the filename and specify a + // path relative to one of the config paths + wxFileName fname = m_FileTree->GetFilePath(); + fname.Normalize(); + m_model->m_Filename = m_resolver->ShortenPath( fname.GetFullPath() ); return true; } @@ -194,8 +199,8 @@ bool DLG_SELECT_3DMODEL::TransferDataFromWindow() void DLG_SELECT_3DMODEL::OnSelectionChanged( wxTreeEvent& event ) { - if( NULL != m_preview ) - m_preview->UpdateModelName( m_FileTree->GetFilePath() ); + if( m_modelViewer ) + m_modelViewer->Set3DModel( m_FileTree->GetFilePath() ); event.Skip(); return; @@ -204,8 +209,8 @@ void DLG_SELECT_3DMODEL::OnSelectionChanged( wxTreeEvent& event ) void DLG_SELECT_3DMODEL::OnFileActivated( wxTreeEvent& event ) { - if( NULL != m_preview ) - m_preview->UpdateModelName( m_FileTree->GetFilePath() ); + if( m_modelViewer ) + m_modelViewer->Set3DModel( m_FileTree->GetFilePath() ); event.Skip(); SetEscapeId( wxID_OK ); @@ -217,10 +222,8 @@ void DLG_SELECT_3DMODEL::OnFileActivated( wxTreeEvent& event ) void DLG_SELECT_3DMODEL::SetRootDir( wxCommandEvent& event ) { - if( !m_FileTree ) - return; - - m_FileTree->SetPath( dirChoices->GetString( dirChoices->GetSelection() ) ); + if( m_FileTree ) + m_FileTree->SetPath( dirChoices->GetString( dirChoices->GetSelection() ) ); return; } diff --git a/3d-viewer/3d_cache/dialogs/dlg_select_3dmodel.h b/3d-viewer/3d_cache/dialogs/dlg_select_3dmodel.h index 2d32088498..48033991dd 100644 --- a/3d-viewer/3d_cache/dialogs/dlg_select_3dmodel.h +++ b/3d-viewer/3d_cache/dialogs/dlg_select_3dmodel.h @@ -41,7 +41,7 @@ struct S3D_INFO; class S3D_CACHE; class S3D_FILENAME_RESOLVER; -class PANEL_PREV_3D; +class C3D_MODEL_VIEWER; //class wxGenericDirCtrl; class DLG_SELECT_3DMODEL : public wxDialog @@ -55,7 +55,7 @@ private: int& m_previousFilterIndex; wxGenericDirCtrl* m_FileTree; - PANEL_PREV_3D* m_preview; + C3D_MODEL_VIEWER* m_modelViewer; wxChoice* dirChoices; void updateDirChoiceList( void ); diff --git a/3d-viewer/3d_cache/dialogs/panel_prev_model.cpp b/3d-viewer/3d_cache/dialogs/panel_prev_model.cpp index 5d83d45c19..e02da100c8 100644 --- a/3d-viewer/3d_cache/dialogs/panel_prev_model.cpp +++ b/3d-viewer/3d_cache/dialogs/panel_prev_model.cpp @@ -1,6 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * + * Copyright (C) 2016 Mario Luzeiro * Copyright (C) 2015 Cirilo Bernardo * * This program is free software; you can redistribute it and/or @@ -21,29 +22,26 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <3d_model_viewer/c3d_model_viewer.h> -#include <3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.h> +/** + * @file panel_prev_model.cpp + */ + +#include <3d_canvas/eda_3d_canvas.h> #include #include -#include -#include + #include -#include -#include -#include -#include -#include -#include +#include #include "project.h" -#include "3d_cache.h" -#include "3d_info.h" -#include "3d_filename_resolver.h" -#include "plugins/3dapi/ifsg_api.h" #include "panel_prev_model.h" +#include -// ensure -360 < rotation < 360 +/** + * @brief checkRotation - ensure -360 < rotation < 360 + * @param rot: in out parameter + */ static void checkRotation( double& rot ) { if( rot >= 360.0 ) @@ -62,7 +60,7 @@ static void checkRotation( double& rot ) enum { - ID_SCALEX = wxID_LAST + 1, + ID_SCALEX = ID_KICAD_PANEL_PREV_MODEL_START, ID_SCALEY, ID_SCALEZ, ID_ROTX, @@ -78,20 +76,23 @@ enum { ID_3D_FRONT, ID_3D_BACK, ID_3D_TOP, - ID_3D_BOTTOM + ID_3D_BOTTOM, + ID_3D_END = ID_KICAD_PANEL_PREV_MODEL_END }; + wxBEGIN_EVENT_TABLE( PANEL_PREV_3D, wxPanel) - EVT_TEXT_ENTER( ID_SCALEX, PANEL_PREV_3D::updateOrientation ) - EVT_TEXT_ENTER( ID_SCALEY, PANEL_PREV_3D::updateOrientation ) - EVT_TEXT_ENTER( ID_SCALEZ, PANEL_PREV_3D::updateOrientation ) - EVT_TEXT_ENTER( ID_ROTX, PANEL_PREV_3D::updateOrientation ) - EVT_TEXT_ENTER( ID_ROTY, PANEL_PREV_3D::updateOrientation ) - EVT_TEXT_ENTER( ID_ROTZ, PANEL_PREV_3D::updateOrientation ) - EVT_TEXT_ENTER( ID_OFFX, PANEL_PREV_3D::updateOrientation ) - EVT_TEXT_ENTER( ID_OFFY, PANEL_PREV_3D::updateOrientation ) - EVT_TEXT_ENTER( ID_OFFZ, PANEL_PREV_3D::updateOrientation ) - EVT_BUTTON( ID_3D_ISO, PANEL_PREV_3D::View3DISO ) + EVT_TEXT( ID_SCALEX, PANEL_PREV_3D::updateOrientation ) + EVT_TEXT( ID_SCALEY, PANEL_PREV_3D::updateOrientation ) + EVT_TEXT( ID_SCALEZ, PANEL_PREV_3D::updateOrientation ) + EVT_TEXT( ID_ROTX, PANEL_PREV_3D::updateOrientation ) + EVT_TEXT( ID_ROTY, PANEL_PREV_3D::updateOrientation ) + EVT_TEXT( ID_ROTZ, PANEL_PREV_3D::updateOrientation ) + EVT_TEXT( ID_OFFX, PANEL_PREV_3D::updateOrientation ) + EVT_TEXT( ID_OFFY, PANEL_PREV_3D::updateOrientation ) + EVT_TEXT( ID_OFFZ, PANEL_PREV_3D::updateOrientation ) + + EVT_TOGGLEBUTTON( ID_3D_ISO, PANEL_PREV_3D::View3DISO ) EVT_BUTTON( ID_3D_UPDATE, PANEL_PREV_3D::View3DUpdate ) EVT_BUTTON( ID_3D_LEFT, PANEL_PREV_3D::View3DLeft ) EVT_BUTTON( ID_3D_RIGHT, PANEL_PREV_3D::View3DRight ) @@ -99,19 +100,24 @@ wxBEGIN_EVENT_TABLE( PANEL_PREV_3D, wxPanel) EVT_BUTTON( ID_3D_BACK, PANEL_PREV_3D::View3DBack ) EVT_BUTTON( ID_3D_TOP, PANEL_PREV_3D::View3DTop ) EVT_BUTTON( ID_3D_BOTTOM, PANEL_PREV_3D::View3DBottom ) + EVT_CLOSE( PANEL_PREV_3D::OnCloseWindow ) wxEND_EVENT_TABLE() -PANEL_PREV_3D::PANEL_PREV_3D( wxWindow* aParent, S3D_CACHE* aCacheManager ) : - wxPanel( aParent, -1 ), m_ModelManager( aCacheManager ) +PANEL_PREV_3D::PANEL_PREV_3D( wxWindow* aParent, + S3D_CACHE* aCacheManager, + MODULE* aModuleCopy, + std::vector *aParentInfoList ) : + wxPanel( aParent, -1 ) { - if( NULL != m_ModelManager ) - m_resolver = m_ModelManager->GetResolver(); + if( NULL != aCacheManager ) + m_resolver = aCacheManager->GetResolver(); else m_resolver = NULL; - canvas = NULL; - model = NULL; + m_currentSelectedIdx = -1; + m_parentInfoList = aParentInfoList; + m_previewPane = NULL; xscale = NULL; yscale = NULL; zscale = NULL; @@ -121,54 +127,55 @@ PANEL_PREV_3D::PANEL_PREV_3D( wxWindow* aParent, S3D_CACHE* aCacheManager ) : xoff = NULL; yoff = NULL; zoff = NULL; + currentModelFile.clear(); wxBoxSizer* mainBox = new wxBoxSizer( wxVERTICAL ); wxStaticBoxSizer* vbox = new wxStaticBoxSizer( wxVERTICAL, this, _( "3D Preview" ) ); - m_FileTree = NULL; - - if( NULL != aParent ) - m_FileTree = (wxGenericDirCtrl*) - aParent->FindWindowByLabel( wxT( "3D_MODEL_SELECTOR" ), aParent ); wxFloatingPointValidator< float > valScale( 4 ); - valScale.SetRange( 0.0001, 9999.0 ); + valScale.SetRange( 0.0001f, 9999.0f ); wxFloatingPointValidator< float > valRotate( 2 ); - valRotate.SetRange( -180.0, 180.0 ); + valRotate.SetRange( -180.0f, 180.0f ); wxFloatingPointValidator< float > valOffset( 4 ); - valOffset.SetRange( -9999.0, 9999.0 ); + valOffset.SetRange( -9999.0f, 9999.0f ); - wxStaticBoxSizer* vbScale = new wxStaticBoxSizer( wxVERTICAL, this, _( "Scale" ) ); - wxStaticBoxSizer* vbRotate = new wxStaticBoxSizer( wxVERTICAL, this, _( "Rotation" ) ); - wxStaticBoxSizer* vbOffset = new wxStaticBoxSizer( wxVERTICAL, this, _( "Offset (inches)" ) ); + wxStaticBoxSizer* vbScale = new wxStaticBoxSizer( wxVERTICAL, this, _( "Scale" ) ); + wxStaticBoxSizer* vbRotate = new wxStaticBoxSizer( wxVERTICAL, this, _( "Rotation (degrees)" ) ); - wxStaticBox* modScale = vbScale->GetStaticBox(); + const wxString offsetString = _( "Offset " ) + "(" + GetUnitsLabel( g_UserUnit ) + ")"; + wxStaticBoxSizer* vbOffset = new wxStaticBoxSizer( wxVERTICAL, this, offsetString ); + + wxStaticBox* modScale = vbScale->GetStaticBox(); wxStaticBox* modRotate = vbRotate->GetStaticBox(); wxStaticBox* modOffset = vbOffset->GetStaticBox(); wxBoxSizer* hbS1 = new wxBoxSizer( wxHORIZONTAL ); wxBoxSizer* hbS2 = new wxBoxSizer( wxHORIZONTAL ); wxBoxSizer* hbS3 = new wxBoxSizer( wxHORIZONTAL ); + wxStaticText* txtS1 = new wxStaticText( modScale, -1, wxT( "X:" ) ); wxStaticText* txtS2 = new wxStaticText( modScale, -1, wxT( "Y:" ) ); wxStaticText* txtS3 = new wxStaticText( modScale, -1, wxT( "Z:" ) ); + xscale = new wxTextCtrl( modScale, ID_SCALEX, "1.0", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER, valScale ); yscale = new wxTextCtrl( modScale, ID_SCALEY, "1.0", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER, valScale ); zscale = new wxTextCtrl( modScale, ID_SCALEZ, "1.0", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER, valScale ); + xscale->SetMaxLength( 9 ); yscale->SetMaxLength( 9 ); zscale->SetMaxLength( 9 ); - hbS1->Add( txtS1, 0, wxALL, 2 ); - hbS1->Add( xscale, 0, wxALL, 2 ); - hbS2->Add( txtS2, 0, wxALL, 2 ); - hbS2->Add( yscale, 0, wxALL, 2 ); - hbS3->Add( txtS3, 0, wxALL, 2 ); - hbS3->Add( zscale, 0, wxALL, 2 ); - vbScale->Add( hbS1, 0, wxEXPAND | wxALL, 0 ); - vbScale->Add( hbS2, 0, wxEXPAND | wxALL, 0 ); - vbScale->Add( hbS3, 0, wxEXPAND | wxALL, 0 ); + hbS1->Add( txtS1, 0, wxALIGN_CENTER, 2 ); + hbS1->Add( xscale, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + hbS2->Add( txtS2, 0, wxALIGN_CENTER, 2 ); + hbS2->Add( yscale, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + hbS3->Add( txtS3, 0, wxALIGN_CENTER, 2 ); + hbS3->Add( zscale, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + vbScale->Add( hbS1, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); + vbScale->Add( hbS2, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); + vbScale->Add( hbS3, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); wxBoxSizer* hbR1 = new wxBoxSizer( wxHORIZONTAL ); wxBoxSizer* hbR2 = new wxBoxSizer( wxHORIZONTAL ); @@ -176,24 +183,26 @@ PANEL_PREV_3D::PANEL_PREV_3D( wxWindow* aParent, S3D_CACHE* aCacheManager ) : wxStaticText* txtR1 = new wxStaticText( modRotate, -1, wxT( "X:" ) ); wxStaticText* txtR2 = new wxStaticText( modRotate, -1, wxT( "Y:" ) ); wxStaticText* txtR3 = new wxStaticText( modRotate, -1, wxT( "Z:" ) ); + xrot = new wxTextCtrl( modRotate, ID_ROTX, "0.0", wxDefaultPosition, wxDefaultSize, - wxTE_PROCESS_ENTER, valRotate ); + wxTE_PROCESS_ENTER, valRotate ); yrot = new wxTextCtrl( modRotate, ID_ROTY, "0.0", wxDefaultPosition, wxDefaultSize, - wxTE_PROCESS_ENTER, valRotate ); + wxTE_PROCESS_ENTER, valRotate ); zrot = new wxTextCtrl( modRotate, ID_ROTZ, "0.0", wxDefaultPosition, wxDefaultSize, - wxTE_PROCESS_ENTER, valRotate ); + wxTE_PROCESS_ENTER, valRotate ); + xrot->SetMaxLength( 9 ); yrot->SetMaxLength( 9 ); zrot->SetMaxLength( 9 ); - hbR1->Add( txtR1, 0, wxALL, 2 ); - hbR1->Add( xrot, 0, wxALL, 2 ); - hbR2->Add( txtR2, 0, wxALL, 2 ); - hbR2->Add( yrot, 0, wxALL, 2 ); - hbR3->Add( txtR3, 0, wxALL, 2 ); - hbR3->Add( zrot, 0, wxALL, 2 ); - vbRotate->Add( hbR1, 0, wxEXPAND | wxALL, 0 ); - vbRotate->Add( hbR2, 0, wxEXPAND | wxALL, 0 ); - vbRotate->Add( hbR3, 0, wxEXPAND | wxALL, 0 ); + hbR1->Add( txtR1, 1, wxALIGN_CENTER, 2 ); + hbR1->Add( xrot, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + hbR2->Add( txtR2, 1, wxALIGN_CENTER, 2 ); + hbR2->Add( yrot, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + hbR3->Add( txtR3, 1, wxALIGN_CENTER, 2 ); + hbR3->Add( zrot, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + vbRotate->Add( hbR1, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); + vbRotate->Add( hbR2, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); + vbRotate->Add( hbR3, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); wxBoxSizer* hbO1 = new wxBoxSizer( wxHORIZONTAL ); wxBoxSizer* hbO2 = new wxBoxSizer( wxHORIZONTAL ); @@ -201,24 +210,25 @@ PANEL_PREV_3D::PANEL_PREV_3D( wxWindow* aParent, S3D_CACHE* aCacheManager ) : wxStaticText* txtO1 = new wxStaticText( modOffset, -1, wxT( "X:" ) ); wxStaticText* txtO2 = new wxStaticText( modOffset, -1, wxT( "Y:" ) ); wxStaticText* txtO3 = new wxStaticText( modOffset, -1, wxT( "Z:" ) ); + xoff = new wxTextCtrl( modOffset, ID_OFFX, "0.0", wxDefaultPosition, wxDefaultSize, - wxTE_PROCESS_ENTER, valOffset ); + wxTE_PROCESS_ENTER, valOffset ); yoff = new wxTextCtrl( modOffset, ID_OFFY, "0.0", wxDefaultPosition, wxDefaultSize, - wxTE_PROCESS_ENTER, valOffset ); + wxTE_PROCESS_ENTER, valOffset ); zoff = new wxTextCtrl( modOffset, ID_OFFZ, "0.0", wxDefaultPosition, wxDefaultSize, - wxTE_PROCESS_ENTER, valOffset ); + wxTE_PROCESS_ENTER, valOffset ); xoff->SetMaxLength( 10 ); yoff->SetMaxLength( 10 ); zoff->SetMaxLength( 10 ); - hbO1->Add( txtO1, 0, wxALL, 2 ); - hbO1->Add( xoff, 0, wxALL, 2 ); - hbO2->Add( txtO2, 0, wxALL, 2 ); - hbO2->Add( yoff, 0, wxALL, 2 ); - hbO3->Add( txtO3, 0, wxALL, 2 ); - hbO3->Add( zoff, 0, wxALL, 2 ); - vbOffset->Add( hbO1, 0, wxEXPAND | wxALL, 0 ); - vbOffset->Add( hbO2, 0, wxEXPAND | wxALL, 0 ); - vbOffset->Add( hbO3, 0, wxEXPAND | wxALL, 0 ); + hbO1->Add( txtO1, 0, wxALIGN_CENTER, 2 ); + hbO1->Add( xoff, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + hbO2->Add( txtO2, 0, wxALIGN_CENTER, 2 ); + hbO2->Add( yoff, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + hbO3->Add( txtO3, 0, wxALIGN_CENTER, 2 ); + hbO3->Add( zoff, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + vbOffset->Add( hbO1, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); + vbOffset->Add( hbO2, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); + vbOffset->Add( hbO3, 1, wxEXPAND | wxLEFT | wxRIGHT, 6 ); // hbox holding orientation data and preview wxBoxSizer* hbox = new wxBoxSizer( wxHORIZONTAL ); @@ -227,215 +237,290 @@ PANEL_PREV_3D::PANEL_PREV_3D( wxWindow* aParent, S3D_CACHE* aCacheManager ) : // vbox holding the preview and view buttons wxBoxSizer* vboxPrev = new wxBoxSizer( wxVERTICAL ); - vboxOrient->Add( vbScale, 0, wxEXPAND | wxALL, 1 ); - vboxOrient->Add( vbRotate, 0, wxEXPAND | wxALL, 1 ); - vboxOrient->Add( vbOffset, 0, wxEXPAND | wxALL, 1 ); + vboxOrient->Add( vbScale, 1, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + vboxOrient->Add( vbRotate, 1, wxEXPAND | wxLEFT | wxRIGHT, 2 ); + vboxOrient->Add( vbOffset, 1, wxEXPAND | wxLEFT | wxRIGHT, 2 ); // add preview items - preview = new wxPanel( this, -1 ); + wxPanel*preview = new wxPanel( this, -1 ); preview->SetMinSize( wxSize( 400, 250 ) ); preview->SetBackgroundColour( wxColor( 0, 0, 0 )); - vboxPrev->Add( preview, 1, wxEXPAND | wxLEFT | wxRIGHT, 5 ); + vboxPrev->Add( preview, 1, wxEXPAND | wxALL, 5 ); + // buttons: - wxButton* vFront = new wxButton( this, ID_3D_FRONT, wxT( "F" ) ); - wxButton* vBack = new wxButton( this, ID_3D_BACK, wxT( "B" ) ); - wxButton* vLeft = new wxButton( this, ID_3D_LEFT, wxT( "L" ) ); - wxButton* vRight = new wxButton( this, ID_3D_RIGHT, wxT( "R" ) ); - wxButton* vTop = new wxButton( this, ID_3D_TOP, wxT( "T" ) ); - wxButton* vBottom = new wxButton( this, ID_3D_BOTTOM, wxT( "B" ) ); - wxButton* vISO = new wxButton( this, ID_3D_ISO, wxT( "I" ) ); - wxButton* vUpdate = new wxButton( this, ID_3D_UPDATE, wxT( "U" ) ); - wxBoxSizer* hbBT = new wxBoxSizer( wxHORIZONTAL ); - wxBoxSizer* hbBB = new wxBoxSizer( wxHORIZONTAL ); - hbBT->Add( vISO, 0, wxCENTER | wxALL, 3 ); - hbBT->Add( vLeft, 0, wxCENTER | wxALL, 3 ); - hbBT->Add( vFront, 0, wxCENTER | wxALL, 3 ); - hbBT->Add( vTop, 0, wxCENTER | wxALL, 3 ); - hbBT->AddSpacer( 17 ); - hbBB->Add( vUpdate, 0, wxCENTER | wxALL, 3 ); - hbBB->Add( vRight, 0, wxCENTER | wxALL, 3 ); - hbBB->Add( vBack, 0, wxCENTER | wxALL, 3 ); - hbBB->Add( vBottom, 0, wxCENTER | wxALL, 3 ); - hbBB->AddSpacer( 17 ); + wxButton* vFront = new wxButton( this, ID_3D_FRONT ); + vFront->SetBitmap( KiBitmap( axis3d_front_xpm ) ); + + wxButton* vBack = new wxButton( this, ID_3D_BACK ); + vBack->SetBitmap( KiBitmap( axis3d_back_xpm ) ); + + wxButton* vLeft = new wxButton( this, ID_3D_LEFT ); + vLeft->SetBitmap( KiBitmap( axis3d_left_xpm ) ); + + wxButton* vRight = new wxButton( this, ID_3D_RIGHT ); + vRight->SetBitmap( KiBitmap( axis3d_right_xpm ) ); + + wxButton* vTop = new wxButton( this, ID_3D_TOP ); + vTop->SetBitmap( KiBitmap( axis3d_top_xpm ) ); + + wxButton* vBottom = new wxButton( this, ID_3D_BOTTOM ); + vBottom->SetBitmap( KiBitmap( axis3d_bottom_xpm ) ); + + wxToggleButton* vISO = new wxToggleButton( this, ID_3D_ISO, wxT("") ); + vISO->SetBitmap( KiBitmap( ortho_xpm ) ); + vISO->SetToolTip( _("Change to isometric perspective") ); + + wxButton* vUpdate = new wxButton( this, ID_3D_UPDATE ); + vUpdate->SetBitmap( KiBitmap( reload_xpm ) ); + vUpdate->SetToolTip( _("Reload board and 3D models") ); + + wxGridSizer* gridSizer = new wxGridSizer( 2, 4, 0, 0 ); + + gridSizer->Add( vISO, 0, wxEXPAND, 3 ); + gridSizer->Add( vLeft, 0, wxEXPAND, 3 ); + gridSizer->Add( vFront, 0, wxEXPAND, 3 ); + gridSizer->Add( vTop, 0, wxEXPAND, 3 ); + + gridSizer->Add( vUpdate, 0, wxEXPAND, 3 ); + gridSizer->Add( vRight, 0, wxEXPAND, 3 ); + gridSizer->Add( vBack, 0, wxEXPAND, 3 ); + gridSizer->Add( vBottom, 0, wxEXPAND, 3 ); vboxPrev->AddSpacer( 7 ); - vboxPrev->Add( hbBT, 0 ); - vboxPrev->Add( hbBB, 0 ); + vboxPrev->Add( gridSizer, 0, wxCENTER, 0 ); - // XXX - Suppress the buttons until the Renderer code is ready. - // vboxPrev->Hide( preview, true ); - vboxPrev->Hide( hbBT, true ); - vboxPrev->Hide( hbBB, true ); - - hbox->Add( vboxOrient, 0, wxEXPAND | wxALL, 0 ); + hbox->Add( vboxOrient, 0, 0, 2 ); hbox->Add( vboxPrev, 1, wxEXPAND | wxALL, 12 ); - vbox->Add( hbox, 1, wxEXPAND ); + vbox->Add( hbox, 1, wxEXPAND | wxALL, 0 ); mainBox->Add( vbox, 1, wxEXPAND | wxALL, 5 ); - if( NULL != m_FileTree ) - { - // NOTE: if/when the File Selector preview is implemented - // we may need to hide the orientation boxes to ensure the - // users have sufficient display area for the browser. - // hbox->Hide( vboxOrient, true ); - // XXX - - // NOTE: for now we always suppress the preview and model orientation - // panels while in the file selector - //mainBox->Hide( vbox, true ); - - hbox->Hide( vboxOrient, true ); - vboxPrev->Hide( hbBT, true ); - vboxPrev->Hide( hbBB, true ); - } - SetSizerAndFit( mainBox ); - return; + // Create a dummy board + m_dummyBoard = new BOARD(); + m_dummyBoard->Add( (MODULE*)aModuleCopy ); + m_copyModule = aModuleCopy; + + // Set 3d viewer configuration for preview + m_settings3Dviewer = new CINFO3D_VISU(); + + // Create the 3D canvas + m_previewPane = new EDA_3D_CANVAS( preview, + COGL_ATT_LIST::GetAttributesList( true ), + m_dummyBoard, + *m_settings3Dviewer, + aCacheManager ); + + wxSizer* ws = new wxBoxSizer( wxHORIZONTAL ); + ws->Add( m_previewPane, 1, wxEXPAND ); + preview->SetSizer( ws ); + preview->Layout(); + ws->FitInside( preview ); + + m_previewPane->SetFocus(); // Need to catch mouse and keyboard events } PANEL_PREV_3D::~PANEL_PREV_3D() { - if( NULL != canvas ) - { - canvas->Clear3DModel(); - canvas->Refresh(); - canvas->Update(); - } + delete m_settings3Dviewer; + m_settings3Dviewer = NULL; - model = NULL; + delete m_dummyBoard; + m_dummyBoard = NULL; - return; + delete m_previewPane; + m_previewPane = NULL; +} + + +void PANEL_PREV_3D::OnCloseWindow( wxCloseEvent &event ) +{ + if( m_previewPane ) + m_previewPane->Close(); + + event.Skip(); } void PANEL_PREV_3D::View3DISO( wxCommandEvent& event ) { - // XXX - TO BE IMPLEMENTED - // std::cout << "Switch to Isometric View\n"; - return; + if( m_settings3Dviewer ) + { + m_settings3Dviewer->CameraGet().ToggleProjection(); + m_previewPane->Refresh(); + m_previewPane->SetFocus(); + } } void PANEL_PREV_3D::View3DUpdate( wxCommandEvent& event ) { - // XXX - TO BE IMPLEMENTED - // std::cout << "Update 3D View\n"; - - // update the model filename if appropriate - if( NULL != m_FileTree ) + if( m_previewPane ) { - wxString modelName = m_FileTree->GetFilePath(); - UpdateModelName( modelName ); + m_previewPane->ReloadRequest(); + m_previewPane->Refresh(); + m_previewPane->SetFocus(); } - - return; } void PANEL_PREV_3D::View3DLeft( wxCommandEvent& event ) { - // XXX - TO BE IMPLEMENTED - // std::cout << "Switch to Left View\n"; - return; + if( m_previewPane ) + { + m_previewPane->SetView3D( 'X' ); + m_previewPane->SetFocus(); + } } void PANEL_PREV_3D::View3DRight( wxCommandEvent& event ) { - // XXX - TO BE IMPLEMENTED - // std::cout << "Switch to Right View\n"; - return; + if( m_previewPane ) + { + m_previewPane->SetView3D( 'x' ); + m_previewPane->SetFocus(); + } } void PANEL_PREV_3D::View3DFront( wxCommandEvent& event ) { - // XXX - TO BE IMPLEMENTED - // std::cout << "Switch to Front View\n"; - return; + if( m_previewPane ) + { + m_previewPane->SetView3D( 'Y' ); + m_previewPane->SetFocus(); + } } void PANEL_PREV_3D::View3DBack( wxCommandEvent& event ) { - // XXX - TO BE IMPLEMENTED - // std::cout << "Switch to Back View\n"; - return; + if( m_previewPane ) + { + m_previewPane->SetView3D( 'y' ); + m_previewPane->SetFocus(); + } } void PANEL_PREV_3D::View3DTop( wxCommandEvent& event ) { - // XXX - TO BE IMPLEMENTED - // std::cout << "Switch to Top View\n"; - return; + if( m_previewPane ) + { + m_previewPane->SetView3D( 'z' ); + m_previewPane->SetFocus(); + } } void PANEL_PREV_3D::View3DBottom( wxCommandEvent& event ) { - // XXX - TO BE IMPLEMENTED - // std::cout << "Switch to Bottom View\n"; - return; + if( m_previewPane ) + { + m_previewPane->SetView3D( 'Z' ); + m_previewPane->SetFocus(); + } } -void PANEL_PREV_3D::GetModelData( S3D_INFO* aModel ) +void PANEL_PREV_3D::SetModelDataIdx( int idx, bool aReloadPreviewModule ) { - if( NULL == aModel ) - return; + wxASSERT( m_parentInfoList != NULL ); - // XXX - due to cross-platform differences in wxWidgets, extracting - // scale/rotation/offset only works as expected when the preview - // panel is not embedded in a file selector dialog. This conditional - // execution should be removed once the cross-platform issues are - // fixed. - if( NULL == m_FileTree ) + if( m_parentInfoList && (idx >= 0) ) { - SGPOINT scale; - SGPOINT rotation; - SGPOINT offset; + wxASSERT( (unsigned int)idx < (*m_parentInfoList).size() ); - getOrientationVars( scale, rotation, offset ); + if( (unsigned int)idx < (*m_parentInfoList).size() ) + { + m_currentSelectedIdx = -1; // In case that we receive events on the + // next updates, it will set first an + // invalid selection - aModel->scale = scale; - aModel->offset = offset; - aModel->rotation = rotation; + const S3D_INFO *aModel = (const S3D_INFO *)&((*m_parentInfoList)[idx]); + + xscale->SetValue( wxString::FromDouble( aModel->m_Scale.x ) ); + yscale->SetValue( wxString::FromDouble( aModel->m_Scale.y ) ); + zscale->SetValue( wxString::FromDouble( aModel->m_Scale.z ) ); + + xrot->SetValue( wxString::FromDouble( aModel->m_Rotation.x ) ); + yrot->SetValue( wxString::FromDouble( aModel->m_Rotation.y ) ); + zrot->SetValue( wxString::FromDouble( aModel->m_Rotation.z ) ); + + switch( g_UserUnit ) + { + case MILLIMETRES: + xoff->SetValue( wxString::FromDouble( aModel->m_Offset.x * 25.4 ) ); + yoff->SetValue( wxString::FromDouble( aModel->m_Offset.y * 25.4 ) ); + zoff->SetValue( wxString::FromDouble( aModel->m_Offset.z * 25.4 ) ); + break; + + case INCHES: + xoff->SetValue( wxString::FromDouble( aModel->m_Offset.x ) ); + yoff->SetValue( wxString::FromDouble( aModel->m_Offset.y ) ); + zoff->SetValue( wxString::FromDouble( aModel->m_Offset.z ) ); + break; + + case DEGREES: + case UNSCALED_UNITS: + default: + wxASSERT(0); + } + + UpdateModelName( aModel->m_Filename ); + + if( aReloadPreviewModule && m_previewPane ) + { + updateListOnModelCopy(); + + m_previewPane->ReloadRequest(); + m_previewPane->Request_refresh(); + } + + m_currentSelectedIdx = idx; + } } - // return if we are not in file selection mode - if( NULL == m_FileTree ) - return; - - // file selection mode: retrieve the filename and specify a - // path relative to one of the config paths - wxFileName fname = m_FileTree->GetFilePath(); - fname.Normalize(); - aModel->filename = m_resolver->ShortenPath( fname.GetFullPath() ); + if( m_previewPane ) + m_previewPane->SetFocus(); return; } -void PANEL_PREV_3D::SetModelData( S3D_INFO const* aModel ) +void PANEL_PREV_3D::ResetModelData( bool aReloadPreviewModule ) { - xscale->SetValue( wxString::FromDouble( aModel->scale.x ) ); - yscale->SetValue( wxString::FromDouble( aModel->scale.y ) ); - zscale->SetValue( wxString::FromDouble( aModel->scale.z ) ); + m_currentSelectedIdx = -1; - xrot->SetValue( wxString::FromDouble( aModel->rotation.x ) ); - yrot->SetValue( wxString::FromDouble( aModel->rotation.y ) ); - zrot->SetValue( wxString::FromDouble( aModel->rotation.z ) ); + xscale->SetValue( wxString::FromDouble( 1.0 ) ); + yscale->SetValue( wxString::FromDouble( 1.0 ) ); + zscale->SetValue( wxString::FromDouble( 1.0 ) ); - xoff->SetValue( wxString::FromDouble( aModel->offset.x ) ); - yoff->SetValue( wxString::FromDouble( aModel->offset.y ) ); - zoff->SetValue( wxString::FromDouble( aModel->offset.z ) ); + xrot->SetValue( wxString::FromDouble( 0.0 ) ); + yrot->SetValue( wxString::FromDouble( 0.0 ) ); + zrot->SetValue( wxString::FromDouble( 0.0 ) ); - UpdateModelName( aModel->filename ); + xoff->SetValue( wxString::FromDouble( 0.0 ) ); + yoff->SetValue( wxString::FromDouble( 0.0 ) ); + zoff->SetValue( wxString::FromDouble( 0.0 ) ); - return; + // This will update the model on the preview board with the current list of 3d shapes + if( aReloadPreviewModule ) + { + updateListOnModelCopy(); + + if( m_previewPane ) + { + m_previewPane->ReloadRequest(); + m_previewPane->Request_refresh(); + } + } + + if( m_previewPane ) + m_previewPane->SetFocus(); } @@ -443,102 +528,48 @@ void PANEL_PREV_3D::UpdateModelName( wxString const& aModelName ) { bool newModel = false; - modelInfo.filename = aModelName; + m_modelInfo.m_Filename = aModelName; // if the model name is a directory simply clear the current model if( aModelName.empty() || wxFileName::DirExists( aModelName ) ) { currentModelFile.clear(); - modelInfo.filename.clear(); + m_modelInfo.m_Filename.clear(); } else { wxString newModelFile; - newModelFile = m_resolver->ResolvePath( aModelName ); + + if( m_resolver ) + newModelFile = m_resolver->ResolvePath( aModelName ); if( !newModelFile.empty() && newModelFile.Cmp( currentModelFile ) ) newModel = true; currentModelFile = newModelFile; - modelInfo.filename = currentModelFile; } if( currentModelFile.empty() || newModel ) { - if( NULL != canvas ) - { - canvas->Clear3DModel(); - canvas->Refresh(); - canvas->Update(); - } + updateListOnModelCopy(); - model = NULL; + if( m_previewPane ) + { + m_previewPane->ReloadRequest(); + m_previewPane->Refresh(); + } if( currentModelFile.empty() ) return; } - - if( NULL != m_ModelManager ) - model = m_ModelManager->GetModel( modelInfo.filename ); else - model = NULL; - - if( NULL == model ) { - if( NULL != canvas ) - { - canvas->Refresh(); - canvas->Update(); - } - - return; + if( m_previewPane ) + m_previewPane->Refresh(); } - if( NULL == canvas ) - { - canvas = new C3D_MODEL_VIEWER( preview, - COGL_ATT_LIST::GetAttributesList( true ) ); - - wxSizer* ws = new wxBoxSizer( wxHORIZONTAL ); - canvas->Set3DModel( *model ); - ws->Add( canvas, 1, wxEXPAND ); - preview->SetSizer( ws ); - preview->Layout(); - ws->FitInside( preview ); - - // Fixes bug in Windows (XP and possibly others) where the canvas requires the focus - // in order to receive mouse events. Otherwise, the user has to click somewhere on - // the canvas before it will respond to mouse wheel events. - canvas->SetFocus(); - return; - } - - canvas->Set3DModel( *model ); - canvas->Refresh(); - canvas->Update(); - canvas->SetFocus(); - - return; -} - - -void PANEL_PREV_3D::UpdateWindowUI( long flags ) -{ - /* - XXX - - NOTE: until we figure out how to ensure that a Paint Event is - generated for the File Selector's UI, we cannot display any - preview within the file browser. - if( wxUPDATE_UI_RECURSE == flags && m_FileDlg && m_ModelManager ) - { - // check for a change in the current model file - S3D_INFO info; - modelInfo = info; - UpdateModelName( m_FileDlg->GetCurrentlySelectedFilename() ); - } - // */ - - wxPanel::UpdateWindowUI( flags ); + if( m_previewPane ) + m_previewPane->SetFocus(); return; } @@ -546,33 +577,41 @@ void PANEL_PREV_3D::UpdateWindowUI( long flags ) void PANEL_PREV_3D::updateOrientation( wxCommandEvent &event ) { - // note: process even if the canvas is NULL since the user may - // edit the filename to provide a valid file + wxTextCtrl *textCtrl = (wxTextCtrl *)event.GetEventObject(); + + if( textCtrl == NULL ) + return; + + if( textCtrl->GetLineLength(0) == 0 ) // This will skip the got and event with empty field + return; + + if( textCtrl->GetLineLength(0) == 1 ) + if( (textCtrl->GetLineText(0).compare( "." ) == 0) || + (textCtrl->GetLineText(0).compare( "," ) == 0) ) + return; + SGPOINT scale; SGPOINT rotation; SGPOINT offset; getOrientationVars( scale, rotation, offset ); - modelInfo.scale = scale; - modelInfo.offset = offset; - modelInfo.rotation = rotation; + m_modelInfo.m_Scale = scale; + m_modelInfo.m_Offset = offset; + m_modelInfo.m_Rotation = rotation; - if( NULL == canvas ) - return; - - canvas->Clear3DModel(); - - if( NULL != m_ModelManager ) - model = m_ModelManager->GetModel( modelInfo.filename ); - else - model = NULL; - - if( model ) + if( m_currentSelectedIdx >= 0 ) { - canvas->Set3DModel( *model ); - canvas->Refresh(); - canvas->Update(); + // This will update the parent list with the new data + (*m_parentInfoList)[m_currentSelectedIdx] = m_modelInfo; + + // It will update the copy model in the preview board + updateListOnModelCopy(); + + // Since the OpenGL render does not need to be reloaded to update the + // shapes position, we just request to redraw again the canvas + if( m_previewPane ) + m_previewPane->Refresh(); } event.Skip(); @@ -580,7 +619,7 @@ void PANEL_PREV_3D::updateOrientation( wxCommandEvent &event ) } -void PANEL_PREV_3D::getOrientationVars( SGPOINT& scale, SGPOINT& rotation, SGPOINT& offset ) +void PANEL_PREV_3D::getOrientationVars( SGPOINT& aScale, SGPOINT& aRotation, SGPOINT& aOffset ) { if( NULL == xscale || NULL == yscale || NULL == zscale || NULL == xrot || NULL == yrot || NULL == zrot @@ -589,30 +628,74 @@ void PANEL_PREV_3D::getOrientationVars( SGPOINT& scale, SGPOINT& rotation, SGPOI return; } - xscale->GetValue().ToDouble( &scale.x ); - yscale->GetValue().ToDouble( &scale.y ); - zscale->GetValue().ToDouble( &scale.z ); + xscale->GetValue().ToDouble( &aScale.x ); + yscale->GetValue().ToDouble( &aScale.y ); + zscale->GetValue().ToDouble( &aScale.z ); - if( 0.0001 > scale.x || 0.0001 > scale.y || 0.0001 > scale.z ) + xrot->GetValue().ToDouble( &aRotation.x ); + yrot->GetValue().ToDouble( &aRotation.y ); + zrot->GetValue().ToDouble( &aRotation.z ); + + checkRotation( aRotation.x ); + checkRotation( aRotation.y ); + checkRotation( aRotation.z ); + + xoff->GetValue().ToDouble( &aOffset.x ); + yoff->GetValue().ToDouble( &aOffset.y ); + zoff->GetValue().ToDouble( &aOffset.z ); + + switch( g_UserUnit ) { - wxString errmsg = _("[INFO] invalid scale values; setting all to 1.0"); - wxLogMessage( "%s", errmsg.ToUTF8() ); + case MILLIMETRES: + // Convert to Inches. Offset is stored in inches. + aOffset.x = aOffset.x / 25.4; + aOffset.y = aOffset.y / 25.4; + aOffset.z = aOffset.z / 25.4; + break; - scale.x = 1.0; - scale.y = 1.0; - scale.z = 1.0; + case INCHES: + // It is already in Inches + break; + + case DEGREES: + case UNSCALED_UNITS: + default: + wxASSERT(0); } - xrot->GetValue().ToDouble( &rotation.x ); - yrot->GetValue().ToDouble( &rotation.y ); - zrot->GetValue().ToDouble( &rotation.z ); - checkRotation( rotation.x ); - checkRotation( rotation.y ); - checkRotation( rotation.z ); - - xoff->GetValue().ToDouble( &offset.x ); - yoff->GetValue().ToDouble( &offset.y ); - zoff->GetValue().ToDouble( &offset.z ); - return; } + + +void PANEL_PREV_3D::updateListOnModelCopy() +{ + bool gotAnInvalidScale = false; + + for( unsigned int idx = 0; idx < m_parentInfoList->size(); ++idx ) + { + SGPOINT scale = (*m_parentInfoList)[idx].m_Scale; + + gotAnInvalidScale |= ( 0.0001 > scale.x || 0.0001 > scale.y || 0.0001 > scale.z ); + + if( 0.0001 > scale.x ) + scale.x = 1.0; + + if( 0.0001 > scale.y ) + scale.y = 1.0; + + if( 0.0001 > scale.y ) + scale.y = 1.0; + + (*m_parentInfoList)[idx].m_Scale = scale; + } + + if( gotAnInvalidScale ) + { + wxString errmsg = _("[INFO] invalid scale values; setting invalid to 1.0"); + wxLogMessage( "%s", errmsg.ToUTF8() ); + } + + std::list* draw3D = &m_copyModule->Models(); + draw3D->clear(); + draw3D->insert( draw3D->end(), m_parentInfoList->begin(), m_parentInfoList->end() ); +} diff --git a/3d-viewer/3d_cache/dialogs/panel_prev_model.h b/3d-viewer/3d_cache/dialogs/panel_prev_model.h index 6c403c60d3..4602c1f19c 100644 --- a/3d-viewer/3d_cache/dialogs/panel_prev_model.h +++ b/3d-viewer/3d_cache/dialogs/panel_prev_model.h @@ -1,6 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * + * Copyright (C) 2016 Mario Luzeiro * Copyright (C) 2015 Cirilo Bernardo * * This program is free software; you can redistribute it and/or @@ -23,35 +24,51 @@ /** * @file panel_prev_model.h - * defines a panel which is to be added to a wxFileDialog via SetExtraControl(); - * the panel shows a preview of the model being browsed (if preview is supported - * by a plugin) and provides controls to set the offset/rotation/scale of the - * model as per KiCad's current behavior. The panel may also be used in the 3D - * configuration dialog to tune the positioning of the models without invoking - * a file selector dialog. + * @brief Defines a panel which is to be added to a wxFileDialog via + * SetExtraControl(); + * The panel shows a preview of the module being edited and provides controls + * to set the offset/rotation/scale of each model 3d shape as per KiCad's + * current behavior. The panel may also be used in the 3D configuration dialog + * to tune the positioning of the models without invoking a file selector dialog. */ #ifndef PANEL_PREV_MODEL_H #define PANEL_PREV_MODEL_H -#include #include +#include "../3d_info.h" +#include -#include "plugins/3dapi/c3dmodel.h" -#include "3d_cache/3d_info.h" - +// Declared classes to create pointers class S3D_CACHE; class S3D_FILENAME_RESOLVER; -class C3D_MODEL_VIEWER; -class wxGenericDirCtrl; +class EDA_3D_CANVAS; +class BOARD; +class CINFO3D_VISU; +class MODULE; + class PANEL_PREV_3D : public wxPanel { public: - PANEL_PREV_3D( wxWindow* aParent, S3D_CACHE* aCacheManager ); + + /** + * @brief PANEL_PREV_3D - Creator + * @param aParent: the parent windows (or object) + * @param aCacheManager: the cache manager to use to resolve the 3D model files + * @param aModule: a copy of the original module that is edited. this is for preview propose and + * it will be changes, so dont use the original one. + * @param aParentInfoList: a pointer to the Info list managed by the parent. This list will be + * passed o the aModuleCopy so it will have the updated 3d model shapes list. + */ + PANEL_PREV_3D( wxWindow* aParent, + S3D_CACHE* aCacheManager, + MODULE* aModuleCopy, + std::vector *aParentInfoList = NULL ); + ~PANEL_PREV_3D(); - // 3D views + // 3D views buttons void View3DISO( wxCommandEvent& event ); void View3DUpdate( wxCommandEvent& event ); void View3DLeft( wxCommandEvent& event ); @@ -60,18 +77,28 @@ public: void View3DBack( wxCommandEvent& event ); void View3DTop( wxCommandEvent& event ); void View3DBottom( wxCommandEvent& event ); - // Set / Retrieve model data - void SetModelData( S3D_INFO const* aModel ); - void GetModelData( S3D_INFO* aModel ); + + /** + * @brief SetModelDataIdx - This will set the index of the INFO list that was set on the parent. + * So we will update our values to edit based on the index on that list. + * @param idx - The index that was selected + * @param aReloadPreviewModule: if need to update the preview module + */ + void SetModelDataIdx( int idx, bool aReloadPreviewModule = false ); + + /** + * @brief ResetModelData - Clear the values and reload the preview board + * @param aReloadPreviewModule: if need to update the preview module + */ + void ResetModelData( bool aReloadPreviewModule = false ); + void UpdateModelName( wxString const& aModel ); - // Update on change of FileDlg selection - virtual void UpdateWindowUI( long flags = wxUPDATE_UI_NONE ); private: - wxString currentModelFile; - S3D_CACHE* m_ModelManager; - S3D_FILENAME_RESOLVER* m_resolver; - wxGenericDirCtrl* m_FileTree; + wxString currentModelFile; ///< Used to check if the model file was changed + S3D_FILENAME_RESOLVER *m_resolver; ///< Used to get the full path name + + // Parameters wxTextCtrl* xscale; wxTextCtrl* yscale; wxTextCtrl* zscale; @@ -81,16 +108,54 @@ private: wxTextCtrl* xoff; wxTextCtrl* yoff; wxTextCtrl* zoff; - wxPanel* preview; - C3D_MODEL_VIEWER* canvas; - S3DMODEL* model; - S3D_INFO modelInfo; + EDA_3D_CANVAS *m_previewPane; + + /// The settings that will be used for this 3D viewer canvas + CINFO3D_VISU *m_settings3Dviewer; + + /// A dummy board used to store the copy moduled + BOARD *m_dummyBoard; + + /// A pointer to a new copy of the original module + MODULE *m_copyModule; + + /// A pointer to the parent S3D_INFO list that we will use to copy to the preview module + std::vector *m_parentInfoList; + + /// The current selected index of the S3D_INFO list + int m_currentSelectedIdx; + + /// Current S3D_INFO that is being edited + S3D_INFO m_modelInfo; private: + + /** + * @brief updateOrientation - it will receive the events from editing the fields + * @param event + */ void updateOrientation( wxCommandEvent &event ); - void getOrientationVars( SGPOINT& scale, SGPOINT& rotation, SGPOINT& offset ); + /** + * @brief getOrientationVars - gets the transformation from entries and validate it + * @param aScale: output scale var + * @param aRotation: output rotation var + * @param aOffset: output offset var + */ + void getOrientationVars( SGPOINT& aScale, SGPOINT& aRotation, SGPOINT& aOffset ); + + /** + * @brief updateListOnModelCopy - copy the current shape list to the copy of module that is on + * the preview dummy board + */ + void updateListOnModelCopy(); + + /** + * @brief OnCloseWindow - called when the frame is closed + * @param event + */ + void OnCloseWindow( wxCloseEvent &event ); wxDECLARE_EVENT_TABLE(); }; diff --git a/3d-viewer/3d_canvas.cpp b/3d-viewer/3d_canvas.cpp deleted file mode 100644 index cdd1abd49e..0000000000 --- a/3d-viewer/3d_canvas.cpp +++ /dev/null @@ -1,739 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_canvas.cpp -*/ -#include -#include -#include -#include - -#include - -#if !wxUSE_GLCANVAS -#error Please set wxUSE_GLCANVAS to 1 in setup.h. -#endif - -#include -#include -#include - -#ifdef __WINDOWS__ -#include // must be included before gl.h -#endif - -#include <3d_viewer.h> -#include <3d_canvas.h> -#include -#include -#include <3d_viewer_id.h> -#include - -#include -#include - - -static const double DELTA_MOVE_STEP = 0.7; - - -/* - * EDA_3D_CANVAS implementation - */ - -BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas ) - EVT_PAINT( EDA_3D_CANVAS::OnPaint ) - - // key event: - EVT_CHAR( EDA_3D_CANVAS::OnChar ) - - // mouse events - EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightClick ) - EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel ) -#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT ) - EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify ) -#endif - EVT_MOTION( EDA_3D_CANVAS::OnMouseMove ) - - // other events - EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground ) - EVT_MENU_RANGE( ID_POPUP_3D_VIEW_START, ID_POPUP_3D_VIEW_END, EDA_3D_CANVAS::OnPopUpMenu ) -END_EVENT_TABLE() - -// Define an invalid value for some unsigned int indexes -#define INVALID_INDEX GL_INVALID_VALUE - -EDA_3D_CANVAS::EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList ) : - wxGLCanvas( parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE ) -{ - m_init = false; - m_reportWarnings = true; - m_shadow_init = false; - // set an invalid value to not yet initialized indexes managing - // textures created to enhance 3D rendering - m_text_pcb = m_text_silk = INVALID_INDEX; - m_text_fake_shadow_front = INVALID_INDEX; - m_text_fake_shadow_back = INVALID_INDEX; - m_text_fake_shadow_board = INVALID_INDEX; - - // position of the front and back layers - // (will be initialized to a better value later) - m_ZBottom = 0.0; - m_ZTop = 0.0; - - m_lightPos = S3D_VERTEX(0.0f, 0.0f, 30.0f); - - // Clear all gl list identifiers: - for( int ii = GL_ID_BEGIN; ii < GL_ID_END; ii++ ) - m_glLists[ii] = 0; - - // Explicitly create a new rendering context instance for this canvas. - m_glRC = GL_CONTEXT_MANAGER::Get().CreateCtx( this ); - - DisplayStatus(); -} - - -EDA_3D_CANVAS::~EDA_3D_CANVAS() -{ - GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this ); - - ClearLists(); - m_init = false; - - // Free the list of parsers list - for( unsigned int i = 0; i < m_model_parsers_list.size(); i++ ) - delete m_model_parsers_list[i]; - - GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); - GL_CONTEXT_MANAGER::Get().DestroyCtx( m_glRC ); -} - - -void EDA_3D_CANVAS::ClearLists( int aGlList ) -{ - if( aGlList ) - { - if( m_glLists[aGlList] > 0 ) - glDeleteLists( m_glLists[aGlList], 1 ); - - m_glLists[aGlList] = 0; - - return; - } - - for( int ii = GL_ID_BEGIN; ii < GL_ID_END; ii++ ) - { - if( m_glLists[ii] > 0 ) - glDeleteLists( m_glLists[ii], 1 ); - - m_glLists[ii] = 0; - } - - // When m_text_fake_shadow_??? is set to INVALID_INDEX, textures are not yet created. - if( m_text_fake_shadow_front != INVALID_INDEX ) - glDeleteTextures( 1, &m_text_fake_shadow_front ); - - if( m_text_fake_shadow_back != INVALID_INDEX ) - glDeleteTextures( 1, &m_text_fake_shadow_back ); - - if( m_text_fake_shadow_board != INVALID_INDEX ) - glDeleteTextures( 1, &m_text_fake_shadow_board ); - - m_shadow_init = false; -} - - -void EDA_3D_CANVAS::OnChar( wxKeyEvent& event ) -{ - SetView3D( event.GetKeyCode() ); - event.Skip(); -} - - -void EDA_3D_CANVAS::SetView3D( int keycode ) -{ - int ii; - double delta_move = DELTA_MOVE_STEP * GetPrm3DVisu().m_Zoom; - - switch( keycode ) - { - case WXK_LEFT: - m_draw3dOffset.x -= delta_move; - break; - - case WXK_RIGHT: - m_draw3dOffset.x += delta_move; - break; - - case WXK_UP: - m_draw3dOffset.y += delta_move; - break; - - case WXK_DOWN: - m_draw3dOffset.y -= delta_move; - break; - - case WXK_HOME: - GetPrm3DVisu().m_Zoom = 1.0; - m_draw3dOffset.x = m_draw3dOffset.y = 0; - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - break; - - case WXK_END: - break; - - case WXK_F1: - GetPrm3DVisu().m_Zoom /= 1.4; - if( GetPrm3DVisu().m_Zoom <= 0.01 ) - GetPrm3DVisu().m_Zoom = 0.01; - break; - - case WXK_F2: - GetPrm3DVisu().m_Zoom *= 1.4; - break; - - case '+': - break; - - case '-': - break; - - case 'r': - case 'R': - m_draw3dOffset.x = m_draw3dOffset.y = 0; - for( ii = 0; ii < 4; ii++ ) - GetPrm3DVisu().m_Rot[ii] = 0.0; - - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - break; - - case 'x': - for( ii = 0; ii < 4; ii++ ) - GetPrm3DVisu().m_Rot[ii] = 0.0; - - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - GetPrm3DVisu().m_ROTZ = -90; - GetPrm3DVisu().m_ROTX = -90; - break; - - case 'X': - for( ii = 0; ii < 4; ii++ ) - GetPrm3DVisu().m_Rot[ii] = 0.0; - - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - GetPrm3DVisu().m_ROTZ = 90; - GetPrm3DVisu().m_ROTX = -90; - break; - - case 'y': - for( ii = 0; ii < 4; ii++ ) - GetPrm3DVisu().m_Rot[ii] = 0.0; - - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - GetPrm3DVisu().m_ROTX = -90; - break; - - case 'Y': - for( ii = 0; ii < 4; ii++ ) - GetPrm3DVisu().m_Rot[ii] = 0.0; - - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - GetPrm3DVisu().m_ROTX = -90; - GetPrm3DVisu().m_ROTZ = -180; - break; - - case 'z': - for( ii = 0; ii < 4; ii++ ) - GetPrm3DVisu().m_Rot[ii] = 0.0; - - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - break; - - case 'Z': - for( ii = 0; ii < 4; ii++ ) - GetPrm3DVisu().m_Rot[ii] = 0.0; - - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - GetPrm3DVisu().m_ROTX = -180; - break; - - default: - return; - } - - DisplayStatus(); - Refresh( false ); -} - - -void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event ) -{ - double delta = DELTA_MOVE_STEP * GetPrm3DVisu().m_Zoom; - if ( GetPrm3DVisu().GetFlag( FL_MOUSEWHEEL_PANNING ) ) - delta *= 0.05 * event.GetWheelRotation(); - else - if ( event.GetWheelRotation() < 0 ) - delta = -delta; - - if( GetPrm3DVisu().GetFlag( FL_MOUSEWHEEL_PANNING ) ) - { - if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL ) - m_draw3dOffset.x -= delta; - else - m_draw3dOffset.y -= delta; - } - else if( event.ShiftDown() ) - { - m_draw3dOffset.y -= delta; - } - else if( event.ControlDown() ) - { - m_draw3dOffset.x += delta; - } - else - { - if( event.GetWheelRotation() > 0 ) - { - GetPrm3DVisu().m_Zoom /= 1.4; - - if( GetPrm3DVisu().m_Zoom <= 0.01 ) - GetPrm3DVisu().m_Zoom = 0.01; - } - else - GetPrm3DVisu().m_Zoom *= 1.4; - - } - - DisplayStatus(); - Refresh( false ); - GetPrm3DVisu().m_Beginx = event.GetX(); - GetPrm3DVisu().m_Beginy = event.GetY(); -} - - -#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT ) -void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event ) -{ - double magnification = ( event.GetMagnification() + 1.0f ); - - GetPrm3DVisu().m_Zoom /= magnification; - - if( GetPrm3DVisu().m_Zoom <= 0.01 ) - { - GetPrm3DVisu().m_Zoom = 0.01; - } - - DisplayStatus(); - Refresh( false ); -} -#endif - - -void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event ) -{ - wxSize size( GetClientSize() ); - double spin_quat[4]; - - if( event.Dragging() ) - { - if( event.LeftIsDown() ) - { - /* drag in progress, simulate trackball */ - trackball( spin_quat, - (2.0 * GetPrm3DVisu().m_Beginx - size.x) / size.x, - (size.y - 2.0 * GetPrm3DVisu().m_Beginy) / size.y, - ( 2.0 * event.GetX() - size.x) / size.x, - ( size.y - 2.0 * event.GetY() ) / size.y ); - - add_quats( spin_quat, GetPrm3DVisu().m_Quat, GetPrm3DVisu().m_Quat ); - } - else if( event.MiddleIsDown() ) - { - /* middle button drag -> pan */ - - /* Current zoom and an additional factor are taken into account - * for the amount of panning. */ - const double PAN_FACTOR = 8.0 * GetPrm3DVisu().m_Zoom; - m_draw3dOffset.x -= PAN_FACTOR * - ( GetPrm3DVisu().m_Beginx - event.GetX() ) / size.x; - m_draw3dOffset.y -= PAN_FACTOR * - (event.GetY() - GetPrm3DVisu().m_Beginy) / size.y; - } - - /* orientation has changed, redraw mesh */ - DisplayStatus(); - Refresh( false ); - } - - GetPrm3DVisu().m_Beginx = event.GetX(); - GetPrm3DVisu().m_Beginy = event.GetY(); -} - - -void EDA_3D_CANVAS::OnRightClick( wxMouseEvent& event ) -{ - wxPoint pos; - wxMenu PopUpMenu; - - pos.x = event.GetX(); - pos.y = event.GetY(); - - wxMenuItem* item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMIN, _( "Zoom +" ) ); - item->SetBitmap( KiBitmap( zoom_in_xpm )); - PopUpMenu.Append( item ); - - item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMOUT, _( "Zoom -" ) ); - item->SetBitmap( KiBitmap( zoom_out_xpm )); - PopUpMenu.Append( item ); - - PopUpMenu.AppendSeparator(); - item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZPOS, _( "Top View" ) ); - item->SetBitmap( KiBitmap( axis3d_top_xpm )); - PopUpMenu.Append( item ); - - item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZNEG, _( "Bottom View" ) ); - item->SetBitmap( KiBitmap( axis3d_bottom_xpm )); - PopUpMenu.Append( item ); - - PopUpMenu.AppendSeparator(); - item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XPOS, _( "Right View" ) ); - item->SetBitmap( KiBitmap( axis3d_right_xpm )); - PopUpMenu.Append( item ); - - item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XNEG, _( "Left View" ) ); - item->SetBitmap( KiBitmap( axis3d_left_xpm )); - PopUpMenu.Append( item ); - - PopUpMenu.AppendSeparator(); - item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YPOS, _( "Front View" ) ); - item->SetBitmap( KiBitmap( axis3d_front_xpm )); - PopUpMenu.Append( item ); - - item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YNEG, _( "Back View" ) ); - item->SetBitmap( KiBitmap( axis3d_back_xpm )); - PopUpMenu.Append( item ); - - PopUpMenu.AppendSeparator(); - item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_LEFT, _( "Move left <-" ) ); - item->SetBitmap( KiBitmap( left_xpm )); - PopUpMenu.Append( item ); - - item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_RIGHT, _( "Move right ->" ) ); - item->SetBitmap( KiBitmap( right_xpm )); - PopUpMenu.Append( item ); - - item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_UP, _( "Move Up ^" ) ); - item->SetBitmap( KiBitmap( up_xpm )); - PopUpMenu.Append( item ); - - item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_DOWN, _( "Move Down" ) ); - item->SetBitmap( KiBitmap( down_xpm )); - PopUpMenu.Append( item ); - - PopupMenu( &PopUpMenu, pos ); -} - - -void EDA_3D_CANVAS::OnPopUpMenu( wxCommandEvent& event ) -{ - int key = 0; - - switch( event.GetId() ) - { - case ID_POPUP_ZOOMIN: - key = WXK_F1; - break; - - case ID_POPUP_ZOOMOUT: - key = WXK_F2; - break; - - case ID_POPUP_VIEW_XPOS: - key = 'x'; - break; - - case ID_POPUP_VIEW_XNEG: - key = 'X'; - break; - - case ID_POPUP_VIEW_YPOS: - key = 'y'; - break; - - case ID_POPUP_VIEW_YNEG: - key = 'Y'; - break; - - case ID_POPUP_VIEW_ZPOS: - key = 'z'; - break; - - case ID_POPUP_VIEW_ZNEG: - key = 'Z'; - break; - - case ID_POPUP_MOVE3D_LEFT: - key = WXK_LEFT; - break; - - case ID_POPUP_MOVE3D_RIGHT: - key = WXK_RIGHT; - break; - - case ID_POPUP_MOVE3D_UP: - key = WXK_UP; - break; - - case ID_POPUP_MOVE3D_DOWN: - key = WXK_DOWN; - break; - - default: - return; - } - - SetView3D( key ); -} - - -void EDA_3D_CANVAS::DisplayStatus() -{ - wxString msg; - - msg.Printf( wxT( "dx %3.2f" ), m_draw3dOffset.x ); - Parent()->SetStatusText( msg, 1 ); - - msg.Printf( wxT( "dy %3.2f" ), m_draw3dOffset.y ); - Parent()->SetStatusText( msg, 2 ); - - msg.Printf( _( "Zoom: %3.1f" ), 45 * GetPrm3DVisu().m_Zoom ); - Parent()->SetStatusText( msg, 3 ); -} - - -void EDA_3D_CANVAS::OnPaint( wxPaintEvent& event ) -{ - wxPaintDC dc( this ); - - Redraw(); - event.Skip(); -} - - -void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event ) -{ - // Do nothing, to avoid flashing. -} - -typedef struct s_sImage -{ - unsigned int width; - unsigned int height; - unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ - unsigned char pixel_data[64 * 64 * 4 + 1]; -}tsImage; - - -GLuint load_and_generate_texture( tsImage *image ) -{ - - GLuint texture; - glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - glPixelStorei (GL_PACK_ALIGNMENT, 1); - - glGenTextures( 1, &texture ); - glBindTexture( GL_TEXTURE_2D, texture ); - gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGBA, image->width, image->height, - GL_RGBA, GL_UNSIGNED_BYTE, image->pixel_data ); - - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - return texture; -} - - -void EDA_3D_CANVAS::InitGL() -{ - if( !m_init ) - { - m_init = true; - - - m_text_pcb = load_and_generate_texture( (tsImage *)&text_pcb ); - m_text_silk = load_and_generate_texture( (tsImage *)&text_silk ); - - GetPrm3DVisu().m_Zoom = 1.0; - m_ZBottom = 1.0; - m_ZTop = 10.0; - - glDisable( GL_CULL_FACE ); // show back faces - glEnable( GL_DEPTH_TEST ); // Enable z-buffering - glEnable( GL_ALPHA_TEST ); - glEnable( GL_LINE_SMOOTH ); -// glEnable(GL_POLYGON_SMOOTH); // creates issues with some graphic cards - glEnable( GL_NORMALIZE ); - glEnable( GL_COLOR_MATERIAL ); - glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); - - // speedups - //glEnable( GL_DITHER ); - glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE ); - glHint( GL_LINE_SMOOTH_HINT, GL_NICEST ); - glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST ); - - // Initialize alpha blending function. - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - } -} - - -void EDA_3D_CANVAS::SetLights() -{ - // activate light. the source is above the xy plane, at source_pos - GLfloat source_pos[4] = { m_lightPos.x, m_lightPos.y, m_lightPos.z, 0.0f }; - GLfloat light_color[4]; // color of lights (RGBA values) - light_color[3] = 1.0; - - // Light above the xy plane - light_color[0] = light_color[1] = light_color[2] = 0.0; - glLightfv( GL_LIGHT0, GL_AMBIENT, light_color ); - - light_color[0] = light_color[1] = light_color[2] = 1.0; - glLightfv( GL_LIGHT0, GL_DIFFUSE, light_color ); - - light_color[0] = light_color[1] = light_color[2] = 1.0; - glLightfv( GL_LIGHT0, GL_SPECULAR, light_color ); - - glLightfv( GL_LIGHT0, GL_POSITION, source_pos ); - - light_color[0] = light_color[1] = light_color[2] = 0.2; - glLightModelfv( GL_LIGHT_MODEL_AMBIENT, light_color ); - - glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE ); - - glEnable( GL_LIGHT0 ); // White spot on Z axis ( top ) - glEnable( GL_LIGHTING ); -} - - -void EDA_3D_CANVAS::TakeScreenshot( wxCommandEvent& event ) -{ - static wxFileName fn; // Remember path between saves during this session only. - wxString FullFileName; - wxString file_ext, mask; - bool fmt_is_jpeg = false; - - // First time path is set to the project path. - if( !fn.IsOk() ) - fn = Parent()->Prj().GetProjectFullName(); - - if( event.GetId() == ID_MENU_SCREENCOPY_JPEG ) - fmt_is_jpeg = true; - - if( event.GetId() != ID_TOOL_SCREENCOPY_TOCLIBBOARD ) - { - file_ext = fmt_is_jpeg ? wxT( "jpg" ) : wxT( "png" ); - mask = wxT( "*." ) + file_ext; - fn.SetExt( file_ext ); - - FullFileName = EDA_FILE_SELECTOR( _( "3D Image File Name:" ), fn.GetPath(), - fn.GetFullName(), file_ext, mask, this, - wxFD_SAVE | wxFD_OVERWRITE_PROMPT, true ); - - if( FullFileName.IsEmpty() ) - return; - - fn = FullFileName; - - // Be sure the screen area destroyed by the file dialog is redrawn before making - // a screen copy. - // Without this call, under Linux the screen refresh is made to late. - wxYield(); - } - - struct viewport_params - { - GLint originx; - GLint originy; - GLint x; - GLint y; - } viewport; - - // Be sure we have the latest 3D view (remember 3D view is buffered) - Refresh(); - wxYield(); - - // Build image from the 3D buffer - wxWindowUpdateLocker noUpdates( this ); - glGetIntegerv( GL_VIEWPORT, (GLint*) &viewport ); - - unsigned char* pixelbuffer = (unsigned char*) malloc( viewport.x * viewport.y * 3 ); - unsigned char* alphabuffer = (unsigned char*) malloc( viewport.x * viewport.y ); - wxImage image_3d( viewport.x, viewport.y ); - - glPixelStorei( GL_PACK_ALIGNMENT, 1 ); - glReadBuffer( GL_BACK_LEFT ); - glReadPixels( viewport.originx, viewport.originy, - viewport.x, viewport.y, - GL_RGB, GL_UNSIGNED_BYTE, pixelbuffer ); - glReadPixels( viewport.originx, viewport.originy, - viewport.x, viewport.y, - GL_ALPHA, GL_UNSIGNED_BYTE, alphabuffer ); - - image_3d.SetData( pixelbuffer ); - image_3d.SetAlpha( alphabuffer ); - image_3d = image_3d.Mirror( false ); - wxBitmap bitmap( image_3d ); - - if( event.GetId() == ID_TOOL_SCREENCOPY_TOCLIBBOARD ) - { - if( wxTheClipboard->Open() ) - { - wxBitmapDataObject* dobjBmp = new wxBitmapDataObject( bitmap ); - - if( !wxTheClipboard->SetData( dobjBmp ) ) - wxMessageBox( _( "Failed to copy image to clipboard" ) ); - - wxTheClipboard->Flush(); /* the data in clipboard will stay - * available after the application exits */ - wxTheClipboard->Close(); - } - } - else - { - wxImage image = bitmap.ConvertToImage(); - - if( !image.SaveFile( FullFileName, - fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) ) - wxMessageBox( _( "Can't save file" ) ); - - image.Destroy(); - } -} diff --git a/3d-viewer/3d_canvas.h b/3d-viewer/3d_canvas.h deleted file mode 100644 index cd41efe99e..0000000000 --- a/3d-viewer/3d_canvas.h +++ /dev/null @@ -1,375 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2012 Wayne Stambaugh - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_viewer.h - */ - -#ifndef _3D_CANVAS_H_ -#define _3D_CANVAS_H_ - -#include - -#ifdef __WXMAC__ -# ifdef __DARWIN__ -# include -# else -# include -# endif -#else -# include -#endif - -#include <3d_struct.h> -#include -#include -#include "3d_rendering/3d_render_raytracing/shapes3D/cbbox.h" - -class BOARD_DESIGN_SETTINGS; -class EDA_3D_FRAME; -class SHAPE_POLY_SET; -class REPORTER; - -class VIA; -class D_PAD; - -// We are using GL lists to store layers and other items -// to draw or not -// GL_LIST_ID are the GL lists indexes in m_glLists -enum GL_LIST_ID -{ - GL_ID_BEGIN = 0, - GL_ID_AXIS = GL_ID_BEGIN, // list id for 3D axis - GL_ID_GRID, // list id for 3D grid - GL_ID_BOARD, // List id for copper layers - GL_ID_TECH_LAYERS, // List id for non copper layers (masks...) - GL_ID_AUX_LAYERS, // List id for user layers (draw, eco, comment) - GL_ID_3DSHAPES_SOLID_FRONT, // List id for 3D shapes, non transparent entities - GL_ID_3DSHAPES_TRANSP_FRONT,// List id for 3D shapes, transparent entities - GL_ID_3DSHAPES_SOLID_BACK, // List id for 3D shapes, non transparent entities - GL_ID_3DSHAPES_TRANSP_BACK, // List id for 3D shapes, transparent entities - GL_ID_SHADOW_FRONT, - GL_ID_SHADOW_BACK, - GL_ID_SHADOW_BOARD, - GL_ID_BODY, // Body only list - GL_ID_END -}; - -class EDA_3D_CANVAS : public wxGLCanvas -{ -private: - bool m_init; - bool m_reportWarnings; ///< true to report all warnings when building the 3D scene - ///< false to report errors only - GLuint m_glLists[GL_ID_END]; ///< GL lists - wxGLContext* m_glRC; - wxRealPoint m_draw3dOffset; ///< offset to draw the 3D mesh. - double m_ZBottom; ///< position of the back layer - double m_ZTop; ///< position of the front layer - - GLuint m_text_pcb; ///< an index to the texture generated for pcb texts - GLuint m_text_silk; ///< an index to the texture generated for silk layers - - // Index to the textures generated for shadows - bool m_shadow_init; - GLuint m_text_fake_shadow_front; - GLuint m_text_fake_shadow_back; - GLuint m_text_fake_shadow_board; - - CBBOX m_boardAABBox; ///< Axis Align Bounding Box of the board - CBBOX m_fastAABBox; ///< Axis Align Bounding Box that contain the other bounding boxes - CBBOX m_fastAABBox_Shadow; ///< A bit scalled version of the m_fastAABBox - - S3D_VERTEX m_lightPos; - - /// Stores the list of parsers for each new file name (dont repeat files already loaded) - std::vector m_model_parsers_list; - std::vector m_model_filename_list; - - void create_and_render_shadow_buffer( GLuint *aDst_gl_texture, - GLuint aTexture_size, bool aDraw_body, int aBlurPasses ); - - void calcBBox(); - -public: - EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList = 0 ); - ~EDA_3D_CANVAS(); - - EDA_3D_FRAME* Parent() const { return static_cast( GetParent() ); } - - BOARD* GetBoard() { return Parent()->GetBoard(); } - - /** - * Function ClearLists - * Clear the display list. - * @param aGlList = the list to clear. - * if 0 (default) all lists are cleared - */ - void ClearLists( int aGlList = 0 ); - - // Event functions: - void OnPaint( wxPaintEvent& event ); - void OnEraseBackground( wxEraseEvent& event ); - void OnChar( wxKeyEvent& event ); - void OnMouseWheel( wxMouseEvent& event ); -#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT ) - void OnMagnify( wxMouseEvent& event ); -#endif - void OnMouseMove( wxMouseEvent& event ); - void OnRightClick( wxMouseEvent& event ); - void OnPopUpMenu( wxCommandEvent& event ); - - /** - * Function TakeScreenshot - * - * creates a screenshot of the current 3D view and save to file as png or jpeg or image - * is copied to the clipboard - */ - void TakeScreenshot( wxCommandEvent& event ); - void OnEnterWindow( wxMouseEvent& event ); - - // Display functions - void SetView3D( int keycode ); - void DisplayStatus(); - void Redraw(); - void Render(); - - /** - * Function CreateDrawGL_List - * Prepares the parameters of the OpenGL draw list - * creates the OpenGL draw list items (board, grid ...) - * @param aErrorMessages = a REPORTER which will filled with error messages, - * if any - * @param aActivity = a REPORTER to display activity state - */ - void CreateDrawGL_List( REPORTER* aErrorMessages, REPORTER* aActivity ); - void InitGL(); - - void ReportWarnings( bool aReport ) { m_reportWarnings = aReport; } - - void SetLights(); - - void SetOffset(double aPosX, double aPosY) - { - m_draw3dOffset.x = aPosX; - m_draw3dOffset.y = aPosY; - } - - /** @return the INFO3D_VISU which contains the current parameters - * to draw the 3D view og the board - */ - INFO3D_VISU& GetPrm3DVisu() const; - - -private: - - /** - * return true if we are in realistic mode render - */ - bool isRealisticMode() const; - - /** - * @return true if aItem should be displayed - * @param aItem = an item of DISPLAY3D_FLG enum - */ - bool isEnabled( DISPLAY3D_FLG aItem ) const; - - /** Helper function - * @return true if aLayer should be displayed, false otherwise - */ - bool is3DLayerEnabled( LAYER_ID aLayer ) const; - - /** - * @return the size of the board in pcb units - */ - wxSize getBoardSize() const; - - /** - * @return the position of the board center in pcb units - */ - wxPoint getBoardCenter() const; - - /** - * Helper function setGLTechLayersColor - * Initialize the color to draw the non copper layers - * in realistic mode and normal mode. - */ - void setGLTechLayersColor( LAYER_NUM aLayer ); - - /** - * Helper function setGLCopperColor - * Initialize the copper color to draw the board - * in realistic mode (a golden yellow color ) - */ - void setGLCopperColor(); - - /** - * Helper function setGLEpoxyColor - * Initialize the color to draw the epoxy body board in realistic mode. - */ - void setGLEpoxyColor( float aTransparency = 1.0 ); - - /** - * Helper function setGLSolderMaskColor - * Initialize the color to draw the solder mask layers in realistic mode. - */ - void setGLSolderMaskColor( float aTransparency = 1.0 ); - - /** - * Function buildBoard3DView - * Called by CreateDrawGL_List() - * Populates the OpenGL GL_ID_BOARD draw list with board items only on copper layers. - * 3D footprint shapes, tech layers and aux layers are not on this list - * Fills aErrorMessages with error messages created by some calculation function - * display activity state - * @param aBoardList = - * @param aBodyOnlyList = - * @param aErrorMessages = a REPORTER to add error and warning messages - * created by the build process (can be NULL) - * @param aActivity = a REPORTER to display activity state - */ - void buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, - REPORTER* aErrorMessages, REPORTER* aActivity ); - - /** - * Function buildTechLayers3DView - * Called by CreateDrawGL_List() - * Populates the OpenGL GL_ID_TECH_LAYERS draw list with items on tech layers - * @param aErrorMessages = a REPORTER to add error and warning messages - * created by the build process (can be NULL) - * @param aActivity = a REPORTER to display activity state - */ - void buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* aActivity ); - - /** - * Function buildBoardThroughHolesPolygonList - * Helper funtion to build the list of the board through holes polygons - * @param allBoardHoles = the SHAPE_POLY_SET to populate - * @param aSegCountPerCircle = the number of segments to approximate a circle - * @param aOptimizeLargeCircles = true to use more than aSegCountPerCircle - * for large circles (a large circle dimatere is > 1mm ) - */ - void buildBoardThroughHolesPolygonList( SHAPE_POLY_SET& allBoardHoles, - int aSegCountPerCircle, bool aOptimizeLargeCircles ); - - /** - * Function buildShadowList - * Called by CreateDrawGL_List() - */ - void buildShadowList( GLuint aFrontList, GLuint aBacklist, GLuint aBoardList ); - - /** - * Function buildFootprintShape3DList - * Called by CreateDrawGL_List() - * Fills the OpenGL GL_ID_3DSHAPES_SOLID and GL_ID_3DSHAPES_TRANSP - * draw lists with 3D footprint shapes - * @param aOpaqueList is the gl list for non transparent items - * @param aTransparentList is the gl list for non transparent items, - * @param aErrorMessages = a REPORTER to add error and warning messages - * created by the build process (can be NULL) - * @param aActivity = a REPORTER to display activity state - * which need to be drawn after all other items - */ - void buildFootprintShape3DList( GLuint aOpaqueList, - GLuint aTransparentList, - REPORTER* aErrorMessages, REPORTER* aActivity ); - /** - * Function buildBoard3DAuxLayers - * Called by CreateDrawGL_List() - * Fills the OpenGL GL_ID_AUX_LAYERS draw list - * with items on aux layers only - * @param aErrorMessages = a REPORTER to add error and warning messages - * created by the build process (can be NULL) - * @param aActivity = a REPORTER to display activity state - * which need to be drawn after all other items - */ - void buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* aActivity ); - - void draw3DGrid( double aGriSizeMM ); - void draw3DAxis(); - - /** - * Helper function BuildPadShapeThickOutlineAsPolygon: - * Build a pad outline as non filled polygon, to draw pads on silkscreen layer - * with a line thickness = aWidth - * Used only to draw pads outlines on silkscreen layers. - */ - void buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad, - SHAPE_POLY_SET& aCornerBuffer, - int aWidth, - int aCircleToSegmentsCount, - double aCorrectionFactor ); - - - /** - * Helper function draw3DViaHole: - * Draw the via hole: - * Build a vertical hole (a cylinder) between the first and the last via layers - */ - void draw3DViaHole( const VIA * aVia ); - - /** - * Helper function draw3DPadHole: - * Draw the pad hole: - * Build a vertical hole (round or oblong) between the front and back layers - */ - void draw3DPadHole( const D_PAD * aPad ); - - /** - * function render3DComponentShape - * insert mesh in gl list - * @param module - * @param aIsRenderingJustNonTransparentObjects = true to load non transparent objects - * @param aIsRenderingJustTransparentObjects = true to load non transparent objects - * in openGL, transparent objects should be drawn *after* non transparent objects - */ - void render3DComponentShape( MODULE* module, - bool aIsRenderingJustNonTransparentObjects, - bool aIsRenderingJustTransparentObjects ); - - /** - * function read3DComponentShape - * read the 3D component shape(s) of the footprint (physical shape). - * @param module - * @return true if load was succeeded, false otherwise - */ - bool read3DComponentShape( MODULE* module ); - - /** - * function generateFakeShadowsTextures - * creates shadows of the board an footprints - * for aesthetical purpose - * @param aErrorMessages = a REPORTER to add error and warning messages - * created by the build process (can be NULL) - * @param aActivity = a REPORTER to display activity state - */ - void generateFakeShadowsTextures( REPORTER* aErrorMessages, REPORTER* aActivity ); - - DECLARE_EVENT_TABLE() -}; - -void CheckGLError(const char *aFileName, int aLineNumber); - -#endif /* _3D_CANVAS_H_ */ diff --git a/3d-viewer/3d_canvas/cinfo3d_visu.cpp b/3d-viewer/3d_canvas/cinfo3d_visu.cpp new file mode 100644 index 0000000000..f663617ddb --- /dev/null +++ b/3d-viewer/3d_canvas/cinfo3d_visu.cpp @@ -0,0 +1,567 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cinfo3d_visu.cpp + * @brief Handles data related with the board to be visualized + */ + +#include "../3d_rendering/ccamera.h" +#include "cinfo3d_visu.h" +#include <3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h> +#include +#include <3d_math.h> +#include "3d_fastmath.h" +#include + +/** + * Trace mask used to enable or disable the trace output of this class. + * The debug output can be turned on by setting the WXTRACE environment variable to + * "KI_TRACE_EDA_CINFO3D_VISU". See the wxWidgets documentation on wxLogTrace for + * more information. + */ +const wxChar *CINFO3D_VISU::m_logTrace = wxT( "KI_TRACE_EDA_CINFO3D_VISU" ); + + +CINFO3D_VISU G_null_CINFO3D_VISU; + + +CINFO3D_VISU::CINFO3D_VISU() : + m_currentCamera( m_trackBallCamera ), + m_trackBallCamera( RANGE_SCALE_3D ) +{ + wxLogTrace( m_logTrace, wxT( "CINFO3D_VISU::CINFO3D_VISU" ) ); + + m_board = NULL; + m_3d_model_manager = NULL; + m_3D_grid_type = GRID3D_NONE; + m_drawFlags.resize( FL_LAST, false ); + + m_render_engine = RENDER_ENGINE_OPENGL_LEGACY; + m_material_mode = MATERIAL_MODE_NORMAL; + + m_boardPos = wxPoint(); + m_boardSize = wxSize(); + m_boardCenter = SFVEC3F(); + + m_boardBoudingBox.Reset(); + m_board2dBBox3DU.Reset(); + + m_layers_container2D.clear(); + m_layers_holes2D.clear(); + m_through_holes_inner.Clear(); + m_through_holes_outer.Clear(); + + m_copperLayersCount = -1; + m_epoxyThickness3DU = 0.0f; + m_copperThickness3DU = 0.0f; + m_nonCopperLayerThickness3DU = 0.0f; + m_biuTo3Dunits = 1.0; + + m_stats_nr_tracks = 0; + m_stats_nr_vias = 0; + m_stats_via_med_hole_diameter = 0.0f; + m_stats_nr_holes = 0; + m_stats_hole_med_diameter = 0.0f; + m_stats_track_med_width = 0.0f; + + m_calc_seg_min_factor3DU = 0.0f; + m_calc_seg_max_factor3DU = 0.0f; + + + memset( m_layerZcoordTop, 0, sizeof( m_layerZcoordTop ) ); + memset( m_layerZcoordBottom, 0, sizeof( m_layerZcoordBottom ) ); + + SetFlag( FL_USE_REALISTIC_MODE, true ); + SetFlag( FL_MODULE_ATTRIBUTES_NORMAL, true ); + SetFlag( FL_SHOW_BOARD_BODY, true ); + SetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS, true ); + SetFlag( FL_MODULE_ATTRIBUTES_NORMAL, true ); + SetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT, true ); + SetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL, true ); + SetFlag( FL_ZONE, true ); + SetFlag( FL_SILKSCREEN, true ); + SetFlag( FL_SOLDERMASK, true ); + + m_BgColorBot = SFVEC3D( 0.4, 0.4, 0.5 ); + m_BgColorTop = SFVEC3D( 0.8, 0.8, 0.9 ); + m_BoardBodyColor = SFVEC3D( 0.4, 0.4, 0.5 ); + m_SolderMaskColor = SFVEC3D( 0.1, 0.2, 0.1 ); + m_SolderPasteColor = SFVEC3D( 0.4, 0.4, 0.4 ); + m_SilkScreenColor = SFVEC3D( 0.9, 0.9, 0.9 ); + m_CopperColor = SFVEC3D( 0.75, 0.61, 0.23 ); +} + + +CINFO3D_VISU::~CINFO3D_VISU() +{ + destroyLayers(); +} + + +bool CINFO3D_VISU::Is3DLayerEnabled( LAYER_ID aLayer ) const +{ + wxASSERT( aLayer < LAYER_ID_COUNT ); + + DISPLAY3D_FLG flg; + + // see if layer needs to be shown + // check the flags + switch( aLayer ) + { + case B_Adhes: + case F_Adhes: + flg = FL_ADHESIVE; + break; + + case B_Paste: + case F_Paste: + flg = FL_SOLDERPASTE; + break; + + case B_SilkS: + case F_SilkS: + flg = FL_SILKSCREEN; + break; + + case B_Mask: + case F_Mask: + flg = FL_SOLDERMASK; + break; + + case Dwgs_User: + case Cmts_User: + if( GetFlag( FL_USE_REALISTIC_MODE ) ) + return false; + + flg = FL_COMMENTS; + break; + + case Eco1_User: + case Eco2_User: + if( GetFlag( FL_USE_REALISTIC_MODE ) ) + return false; + + flg = FL_ECO; + break; + + case Edge_Cuts: + if( GetFlag( FL_SHOW_BOARD_BODY ) || GetFlag( FL_USE_REALISTIC_MODE ) ) + return false; + + return true; + break; + + case Margin: + if( GetFlag( FL_USE_REALISTIC_MODE ) ) + return false; + + return true; + break; + + case B_Cu: + case F_Cu: + return m_board->GetDesignSettings().IsLayerVisible( aLayer ) || + GetFlag( FL_USE_REALISTIC_MODE ); + break; + + default: + // the layer is an internal copper layer, used the visibility + if( GetFlag( FL_SHOW_BOARD_BODY ) && + (m_render_engine == RENDER_ENGINE_OPENGL_LEGACY) ) + { + // Do not render internal layers if it is overlap with the board + // (on OpenGL render) + return false; + } + + return m_board->GetDesignSettings().IsLayerVisible( aLayer ); + } + + // The layer has a flag, return the flag + return GetFlag( flg ); +} + + +bool CINFO3D_VISU::GetFlag( DISPLAY3D_FLG aFlag ) const +{ + wxASSERT( aFlag < FL_LAST ); + + return m_drawFlags[aFlag]; +} + + +void CINFO3D_VISU::SetFlag( DISPLAY3D_FLG aFlag, bool aState ) +{ + wxASSERT( aFlag < FL_LAST ); + + m_drawFlags[aFlag] = aState; +} + +bool CINFO3D_VISU::ShouldModuleBeDisplayed( MODULE_ATTR_T aModuleAttributs ) const +{ + if( ( ( aModuleAttributs == MOD_DEFAULT ) && + GetFlag( FL_MODULE_ATTRIBUTES_NORMAL ) ) || + ( ( ( aModuleAttributs & MOD_CMS) == MOD_CMS ) && + GetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT ) ) || + ( ( ( aModuleAttributs & MOD_VIRTUAL) == MOD_VIRTUAL ) && + GetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL ) ) ) + { + return true; + } + + return false; +} + + +// !TODO: define the actual copper thickness by user +#define COPPER_THICKNESS KiROUND( 0.035 * IU_PER_MM ) // for 35 um +#define TECH_LAYER_THICKNESS KiROUND( 0.04 * IU_PER_MM ) + +int CINFO3D_VISU::GetCopperThicknessBIU() const +{ + return COPPER_THICKNESS; +} + +// Constant factors used for number of segments approximation calcs +#define MIN_SEG_PER_CIRCLE 12 +#define MAX_SEG_PER_CIRCLE 48 + +#define SEG_MIN_FACTOR_BIU ( 0.10f * IU_PER_MM ) +#define SEG_MAX_FACTOR_BIU ( 6.00f * IU_PER_MM ) + + +unsigned int CINFO3D_VISU::GetNrSegmentsCircle( float aDiameter3DU ) const +{ + wxASSERT( aDiameter3DU > 0.0f ); + + unsigned int result = mapf( aDiameter3DU, + m_calc_seg_min_factor3DU, m_calc_seg_max_factor3DU, + (float)MIN_SEG_PER_CIRCLE, (float)MAX_SEG_PER_CIRCLE ); + wxASSERT( result > 1 ); + + return result; +} + + +unsigned int CINFO3D_VISU::GetNrSegmentsCircle( int aDiameterBUI ) const +{ + wxASSERT( aDiameterBUI > 0 ); + + unsigned int result = mapf( (float)aDiameterBUI, + (float)SEG_MIN_FACTOR_BIU, (float)SEG_MAX_FACTOR_BIU, + (float)MIN_SEG_PER_CIRCLE, (float)MAX_SEG_PER_CIRCLE ); + wxASSERT( result > 1 ); + + return result; +} + + +double CINFO3D_VISU::GetCircleCorrectionFactor( int aNrSides ) const +{ + wxASSERT( aNrSides >= 3 ); + + return 1.0 / cos( M_PI / ( (double)aNrSides * 2.0 ) ); +} + + +void CINFO3D_VISU::InitSettings( REPORTER *aStatusTextReporter ) +{ + wxLogTrace( m_logTrace, wxT( "CINFO3D_VISU::InitSettings" ) ); + + // Calculates the board bounding box + // First, use only the board outlines + EDA_RECT bbbox = m_board->ComputeBoundingBox( true ); + + // If no outlines, use the board with items + if( ( bbbox.GetWidth() == 0 ) && ( bbbox.GetHeight() == 0 ) ) + bbbox = m_board->ComputeBoundingBox( false ); + + // Gives a non null size to avoid issues in zoom / scale calculations + if( ( bbbox.GetWidth() == 0 ) && ( bbbox.GetHeight() == 0 ) ) + bbbox.Inflate( Millimeter2iu( 10 ) ); + + m_boardSize = bbbox.GetSize(); + m_boardPos = bbbox.Centre(); + + wxASSERT( (m_boardSize.x > 0) && (m_boardSize.y > 0) ); + + m_boardPos.y = -m_boardPos.y; // The y coord is inverted in 3D viewer + + m_copperLayersCount = m_board->GetCopperLayerCount(); + + // Ensure the board has 2 sides for 3D views, because it is hard to find + // a *really* single side board in the true life... + if( m_copperLayersCount < 2 ) + m_copperLayersCount = 2; + + // Calculate the convertion to apply to all positions. + m_biuTo3Dunits = RANGE_SCALE_3D / std::max( m_boardSize.x, m_boardSize.y ); + + // Calculate factors for cicle segment approximation + m_calc_seg_min_factor3DU = (float)( SEG_MIN_FACTOR_BIU * m_biuTo3Dunits ); + m_calc_seg_max_factor3DU = (float)( SEG_MAX_FACTOR_BIU * m_biuTo3Dunits ); + + m_epoxyThickness3DU = m_board->GetDesignSettings().GetBoardThickness() * + m_biuTo3Dunits; + + // !TODO: use value defined by user (currently use default values by ctor + m_copperThickness3DU = COPPER_THICKNESS * m_biuTo3Dunits; + m_nonCopperLayerThickness3DU = TECH_LAYER_THICKNESS * m_biuTo3Dunits; + + // Init Z position of each layer + // calculate z position for each copper layer + // Zstart = -m_epoxyThickness / 2.0 is the z position of the back (bottom layer) (layer id = 31) + // Zstart = +m_epoxyThickness / 2.0 is the z position of the front (top layer) (layer id = 0) + // all unused copper layer z position are set to 0 + + // ____==__________==________==______ <- Bottom = +m_epoxyThickness / 2.0, + // | | Top = Bottom + m_copperThickness + // |__________________________________| + // == == == == <- Bottom = -m_epoxyThickness / 2.0, + // Top = Bottom - m_copperThickness + + unsigned int layer; + + for( layer = 0; layer < m_copperLayersCount; ++layer ) + { + m_layerZcoordBottom[layer] = m_epoxyThickness3DU / 2.0f - + (m_epoxyThickness3DU * layer / (m_copperLayersCount - 1) ); + + if( layer < (m_copperLayersCount / 2) ) + m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] + m_copperThickness3DU; + else + m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] - m_copperThickness3DU; + } + + #define layerThicknessMargin 1.1 + const float zpos_offset = m_nonCopperLayerThickness3DU * layerThicknessMargin; + + // Fill remaining unused copper layers and back layer zpos + // with -m_epoxyThickness / 2.0 + for( ; layer < MAX_CU_LAYERS; layer++ ) + { + m_layerZcoordBottom[layer] = -(m_epoxyThickness3DU / 2.0f); + m_layerZcoordTop[layer] = -(m_epoxyThickness3DU / 2.0f) - m_copperThickness3DU; + } + + // This is the top of the copper layer thickness. + const float zpos_copperTop_back = m_layerZcoordTop[B_Cu]; + const float zpos_copperTop_front = m_layerZcoordTop[F_Cu]; + + // calculate z position for each non copper layer + // Solder mask and Solder paste have the same Z position + for( int layer_id = MAX_CU_LAYERS; layer_id < LAYER_ID_COUNT; ++layer_id ) + { + float zposTop; + float zposBottom; + + switch( layer_id ) + { + case B_Adhes: + zposBottom = zpos_copperTop_back - 2.0f * zpos_offset; + zposTop = zposBottom - m_nonCopperLayerThickness3DU; + break; + + case F_Adhes: + zposBottom = zpos_copperTop_front + 2.0f * zpos_offset; + zposTop = zposBottom + m_nonCopperLayerThickness3DU; + break; + + case B_Mask: + case B_Paste: + zposBottom = zpos_copperTop_back; + zposTop = zpos_copperTop_back - m_nonCopperLayerThickness3DU; + break; + + case F_Mask: + case F_Paste: + zposTop = zpos_copperTop_front + m_nonCopperLayerThickness3DU; + zposBottom = zpos_copperTop_front; + break; + + case B_SilkS: + zposBottom = zpos_copperTop_back - 1.0f * zpos_offset; + zposTop = zposBottom - m_nonCopperLayerThickness3DU; + break; + + case F_SilkS: + zposBottom = zpos_copperTop_front + 1.0f * zpos_offset; + zposTop = zposBottom + m_nonCopperLayerThickness3DU; + break; + + // !TODO: review + default: + zposTop = zpos_copperTop_front + (layer_id - MAX_CU_LAYERS + 3.0f) * zpos_offset; + zposBottom = zposTop - m_nonCopperLayerThickness3DU; + break; + } + + m_layerZcoordTop[layer_id] = zposTop; + m_layerZcoordBottom[layer_id] = zposBottom; + } + + m_boardCenter = SFVEC3F( m_boardPos.x * m_biuTo3Dunits, + m_boardPos.y * m_biuTo3Dunits, + 0.0f ); + + SFVEC3F boardSize = SFVEC3F( m_boardSize.x * m_biuTo3Dunits, + m_boardSize.y * m_biuTo3Dunits, + 0.0f ); + boardSize /= 2.0f; + + SFVEC3F boardMin = (m_boardCenter - boardSize); + SFVEC3F boardMax = (m_boardCenter + boardSize); + + boardMin.z = m_layerZcoordTop[B_Adhes]; + boardMax.z = m_layerZcoordTop[F_Adhes]; + + m_boardBoudingBox = CBBOX( boardMin, boardMax ); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_startCreateBoardPolyTime = GetRunningMicroSecs(); +#endif + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Build board body" ) ); + + createBoardPolygon(); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_stopCreateBoardPolyTime = GetRunningMicroSecs(); + unsigned stats_startCreateLayersTime = stats_stopCreateBoardPolyTime; +#endif + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Create layers" ) ); + + createLayers( aStatusTextReporter ); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_stopCreateLayersTime = GetRunningMicroSecs(); + + printf( "CINFO3D_VISU::InitSettings times\n" ); + printf( " CreateBoardPoly: %.3f ms\n", + (float)( stats_stopCreateBoardPolyTime - stats_startCreateBoardPolyTime ) / 1e3 ); + printf( " CreateLayers and holes: %.3f ms\n", + (float)( stats_stopCreateLayersTime - stats_startCreateLayersTime ) / 1e3 ); + printf( "\n" ); +#endif +} + + +void CINFO3D_VISU::createBoardPolygon() +{ + m_board_poly.RemoveAllContours(); + + // Create board outlines and board holes + SHAPE_POLY_SET allLayerHoles; + + wxString errmsg; + + if( !m_board->GetBoardPolygonOutlines( m_board_poly, allLayerHoles, &errmsg ) ) + { + errmsg.append( wxT( "\n\n" ) ); + errmsg.append( _( "Unable to calculate the board outlines." ) ); + errmsg.append( wxT( "\n" ) ); + errmsg.append( _( "Therefore use the board boundary box." ) ); + wxLogMessage( "%s", errmsg.ToUTF8() ); + } + + m_board_poly.BooleanSubtract( allLayerHoles, SHAPE_POLY_SET::PM_FAST ); + + Polygon_Calc_BBox_3DU( m_board_poly, m_board2dBBox3DU, m_biuTo3Dunits ); +} + + +float CINFO3D_VISU::GetModulesZcoord3DIU( bool aIsFlipped ) const +{ + if( aIsFlipped ) + { + if( GetFlag( FL_SOLDERPASTE ) ) + return m_layerZcoordBottom[B_SilkS]; + else + return m_layerZcoordBottom[B_Paste]; + } + else + { + if( GetFlag( FL_SOLDERPASTE ) ) + return m_layerZcoordTop[F_SilkS]; + else + return m_layerZcoordTop[F_Paste]; + } +} + + +void CINFO3D_VISU::CameraSetType( CAMERA_TYPE aCameraType ) +{ + switch( aCameraType ) + { + case CAMERA_TRACKBALL: + m_currentCamera = m_trackBallCamera; + break; + + default: + wxLogMessage( wxT( "CINFO3D_VISU::CameraSetType() error: unknown camera type %d" ), + (int)aCameraType ); + break; + } +} + + +SFVEC3F CINFO3D_VISU::GetLayerColor( LAYER_ID aLayerId ) const +{ + wxASSERT( aLayerId < LAYER_ID_COUNT ); + + const EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( aLayerId ); + const StructColors &colordata = g_ColorRefs[ColorGetBase( color )]; + + static const float inv_255 = 1.0f / 255.0f; + + const float red = colordata.m_Red * inv_255; + const float green = colordata.m_Green * inv_255; + const float blue = colordata.m_Blue * inv_255; + + return SFVEC3F( red, green, blue ); +} + + +SFVEC3F CINFO3D_VISU::GetItemColor( int aItemId ) const +{ + return GetColor( g_ColorsSettings.GetItemColor( aItemId ) ); +} + + +SFVEC3F CINFO3D_VISU::GetColor( EDA_COLOR_T aColor ) const +{ + const StructColors &colordata = g_ColorRefs[ColorGetBase( aColor )]; + + static const float inv_255 = 1.0f / 255.0f; + + const float red = colordata.m_Red * inv_255; + const float green = colordata.m_Green * inv_255; + const float blue = colordata.m_Blue * inv_255; + + return SFVEC3F( red, green, blue ); +} diff --git a/3d-viewer/3d_canvas/cinfo3d_visu.h b/3d-viewer/3d_canvas/cinfo3d_visu.h index aff2491070..cfb1dfedd1 100644 --- a/3d-viewer/3d_canvas/cinfo3d_visu.h +++ b/3d-viewer/3d_canvas/cinfo3d_visu.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,63 +36,25 @@ #include "../3d_rendering/3d_render_raytracing/shapes3D/cbbox.h" #include "../3d_rendering/ccamera.h" #include "../3d_rendering/ctrack_ball.h" +#include "../3d_enums.h" +#include "../3d_cache/3d_cache.h" + #include #include #include #include #include -#include #include #include #include - - -/** - * Flags used in rendering options - */ -enum DISPLAY3D_FLG { - FL_AXIS=0, FL_MODULE, FL_ZONE, - FL_ADHESIVE, FL_SILKSCREEN, FL_SOLDERMASK, FL_SOLDERPASTE, - FL_COMMENTS, FL_ECO, - FL_USE_COPPER_THICKNESS, - FL_SHOW_BOARD_BODY, - FL_USE_REALISTIC_MODE, - FL_RENDER_SHADOWS, - FL_RENDER_SHOW_HOLES_IN_ZONES, - FL_RENDER_TEXTURES, - FL_RENDER_SMOOTH_NORMALS, - FL_RENDER_USE_MODEL_NORMALS, - FL_RENDER_MATERIAL, - FL_RENDER_SHOW_MODEL_BBOX, - FL_LAST -}; - - -/** - * Camera types - */ -enum CAMERA_TYPE -{ - CAMERA_TRACKBALL -}; - - -/** - * Grid types - */ -enum GRID3D_TYPE -{ - GRID3D_NONE, - GRID3D_1MM, - GRID3D_2P5MM, - GRID3D_5MM, - GRID3D_10MM -}; - +#include +#include /// A type that stores a container of 2d objects for each layer id typedef std::map< LAYER_ID, CBVHCONTAINER2D *> MAP_CONTAINER_2D; +/// A type that stores polysets for each layer id +typedef std::map< LAYER_ID, SHAPE_POLY_SET *> MAP_POLY; /// This defines the range that all coord will have to be rendered. /// It will use this value to convert to a normalized value between @@ -112,6 +74,18 @@ class CINFO3D_VISU ~CINFO3D_VISU(); + /** + * @brief Set3DCacheManager - Update the Cache manager pointer + * @param aCachePointer: the pointer to the 3d cache manager + */ + void Set3DCacheManager( S3D_CACHE *aCachePointer ) { m_3d_model_manager = aCachePointer; } + + /** + * @brief Get3DCacheManager - Return the 3d cache manager pointer + * @return + */ + S3D_CACHE *Get3DCacheManager( ) const { return m_3d_model_manager; } + /** * @brief GetFlag - get a configuration status of a flag * @param aFlag: the flag to get the status @@ -133,6 +107,13 @@ class CINFO3D_VISU */ bool Is3DLayerEnabled( LAYER_ID aLayer ) const; + /** + * @brief ShouldModuleBeDisplayed - Test if module should be displayed in + * relation to attributs and the flags + * @return true if module should be displayed, false if not + */ + bool ShouldModuleBeDisplayed( MODULE_ATTR_T aModuleAttributs ) const; + /** * @brief SetBoard - Set current board to be rendered * @param aBoard: board to process @@ -148,18 +129,19 @@ class CINFO3D_VISU /** * @brief InitSettings - Function to be called by the render when it need to * reload the settings for the board. + * @param aStatusTextReporter: the pointer for the status reporter */ - void InitSettings(); + void InitSettings( REPORTER *aStatusTextReporter ); /** - * Function BiuTo3Dunits + * @brief BiuTo3Dunits - Board integer units To 3D units * @return the conversion factor to transform a position from the board to 3d units */ double BiuTo3Dunits() const { return m_biuTo3Dunits; } /** * @brief GetBBox3DU - Get the bbox of the pcb board - * @return + * @return the board bbox in 3d units */ const CBBOX &GetBBox3DU() const { return m_boardBoudingBox; } @@ -167,19 +149,19 @@ class CINFO3D_VISU * @brief GetEpoxyThickness3DU - Get the current epoxy thickness * @return thickness in 3d unities */ - float GetEpoxyThickness3DU() const { return m_epoxyThickness; } + float GetEpoxyThickness3DU() const { return m_epoxyThickness3DU; } /** * @brief GetNonCopperLayerThickness3DU - Get the current non copper layers thickness * @return thickness in 3d unities of non copperlayers */ - float GetNonCopperLayerThickness3DU() const { return m_nonCopperLayerThickness; } + float GetNonCopperLayerThickness3DU() const { return m_nonCopperLayerThickness3DU; } /** * @brief GetCopperThickness3DU - Get the current copper layer thickness * @return thickness in 3d unities of copperlayers */ - float GetCopperThickness3DU() const { return m_copperThickness; } + float GetCopperThickness3DU() const { return m_copperThickness3DU; } /** * @brief GetCopperThicknessBIU - Get the current copper layer thickness @@ -188,26 +170,35 @@ class CINFO3D_VISU int GetCopperThicknessBIU() const; /** - * @brief GetBoardSize3DU - Get the board size - * @return size in 3D unities + * @brief GetBoardSizeBIU - Get the board size + * @return size in BIU unities */ - wxSize GetBoardSize3DU() const { return m_boardSize; } + wxSize GetBoardSizeBIU() const { return m_boardSize; } /** - * Function GetBoardCenter - * @return board center in 3d units + * @brief GetBoardPosBIU - Get the board size + * @return size in BIU unities */ - SFVEC3F &GetBoardCenter3DU() { return m_boardCenter; } + wxPoint GetBoardPosBIU() const { return m_boardPos; } /** - * @param aIsFlipped: true for use in modules on Front (top) layer, false - * if module is on back (bottom) layer - * @return the Z position of 3D shapes, in 3D Units + * @brief GetBoardCenter - the board center position in 3d units + * @return board center vector position in 3d units + */ + const SFVEC3F &GetBoardCenter3DU() const { return m_boardCenter; } + + /** + * @brief GetModulesZcoord3DIU - Get the position of the module in 3d integer units + * considering if it is flipped or not. + * @param aIsFlipped: true for use in modules on Front (top) layer, false + * if module is on back (bottom) layer + * @return the Z position of 3D shapes, in 3D integer units */ float GetModulesZcoord3DIU( bool aIsFlipped ) const ; /** - * @param aCameraType: camera type to use in this canvas + * @brief CameraSetType - Set the camera type to use + * @param aCameraType: camera type to use in this canvas */ void CameraSetType( CAMERA_TYPE aCameraType ); @@ -218,22 +209,46 @@ class CINFO3D_VISU CCAMERA &CameraGet() const { return m_currentCamera; } /** - * Function GridGet - * @return space type of the grid + * @brief GridGet - get the current grid + * @return space type of the grid */ - GRID3D_TYPE GridGet() const { return m_3D_Grid_type; } + GRID3D_TYPE GridGet() const { return m_3D_grid_type; } /** - * Function GridSet - * @param aGridType = the type space of the grid + * @brief GridSet - set the current grid + * @param aGridType = the type space of the grid */ - void GridSet( GRID3D_TYPE aGridType ) { m_3D_Grid_type = aGridType; } + void GridSet( GRID3D_TYPE aGridType ) { m_3D_grid_type = aGridType; } + + /** + * @brief RenderEngineSet + * @param aRenderEngine = the render engine mode selected + */ + void RenderEngineSet( RENDER_ENGINE aRenderEngine ) { m_render_engine = aRenderEngine; } + + /** + * @brief RenderEngineGet + * @return render engine on use + */ + RENDER_ENGINE RenderEngineGet() const { return m_render_engine; } + + /** + * @brief MaterialModeSet + * @param aMaterialMode = the render material mode + */ + void MaterialModeSet( MATERIAL_MODE aMaterialMode ) { m_material_mode = aMaterialMode; } + + /** + * @brief MaterialModeGet + * @return material rendering mode + */ + MATERIAL_MODE MaterialModeGet() const { return m_material_mode; } /** * @brief GetBoardPoly - Get the current polygon of the epoxy board * @return the shape polygon */ - const SHAPE_POLY_SET &GetBoardPoly() const { return m_boardPoly; } + const SHAPE_POLY_SET &GetBoardPoly() const { return m_board_poly; } /** * @brief GetLayerColor - get the technical color of a layer @@ -249,6 +264,13 @@ class CINFO3D_VISU */ SFVEC3F GetItemColor( int aItemId ) const; + /** + * @brief GetColor + * @param aColor: the color mapped + * @return the color in SFVEC3F format + */ + SFVEC3F GetColor( EDA_COLOR_T aColor ) const; + /** * @brief GetLayerTopZpos3DU - Get the top z position * @param aLayerId: layer id @@ -276,90 +298,365 @@ class CINFO3D_VISU const MAP_CONTAINER_2D &GetMapLayersHoles() const { return m_layers_holes2D; } /** - * @brief GetThroughHole_Inflated - Get the inflated ThroughHole container + * @brief GetThroughHole_Outer - Get the inflated ThroughHole container * @return a container with holes */ - const CBVHCONTAINER2D &GetThroughHole_Inflated() const { return m_throughHoles_inflated; } + const CBVHCONTAINER2D &GetThroughHole_Outer() const { return m_through_holes_outer; } /** - * @brief GetThroughHole - Get the ThroughHole container + * @brief GetThroughHole_Outer_poly - + * @return + */ + const SHAPE_POLY_SET &GetThroughHole_Outer_poly() const { return m_through_outer_holes_poly; } + + /** + * @brief GetThroughHole_Outer_poly_NPTH - + * @return + */ + const SHAPE_POLY_SET &GetThroughHole_Outer_poly_NPTH() const { + return m_through_outer_holes_poly_NPTH; } + + /** + * @brief GetThroughHole_Vias_Outer - + * @return a container with via THT holes only + */ + const CBVHCONTAINER2D &GetThroughHole_Vias_Outer() const { return m_through_holes_vias_outer; } + + /** + * @brief GetThroughHole_Vias_Inner - + * @return a container with via THT holes only + */ + const CBVHCONTAINER2D &GetThroughHole_Vias_Inner() const { return m_through_holes_vias_inner; } + + /** + * @brief GetThroughHole_Vias_Outer_poly - + * @return + */ + const SHAPE_POLY_SET &GetThroughHole_Vias_Outer_poly() const { + return m_through_outer_holes_vias_poly; } + + /** + * @brief GetThroughHole_Vias_Inner_poly - + * @return + */ + const SHAPE_POLY_SET &GetThroughHole_Vias_Inner_poly() const { + return m_through_inner_holes_vias_poly; } + + /** + * @brief GetThroughHole_Inner - Get the ThroughHole container * @return a container with holes */ - const CBVHCONTAINER2D &GetThroughHole() const { return m_throughHoles; } + const CBVHCONTAINER2D &GetThroughHole_Inner() const { return m_through_holes_inner; } + /** + * @brief GetThroughHole_Inner_poly - + * @return + */ + const SHAPE_POLY_SET &GetThroughHole_Inner_poly() const { return m_through_inner_holes_poly; } + + /** + * @brief GetStats_Nr_Vias - Get statistics of the nr of vias + * @return number of vias + */ unsigned int GetStats_Nr_Vias() const { return m_stats_nr_vias; } + + /** + * @brief GetStats_Nr_Holes - Get statistics of the nr of holes + * @return number of holes + */ unsigned int GetStats_Nr_Holes() const { return m_stats_nr_holes; } + /** + * @brief GetStats_Med_Via_Hole_Diameter3DU - Average diameter of the via holes + * @return dimension in 3D units + */ float GetStats_Med_Via_Hole_Diameter3DU() const { return m_stats_via_med_hole_diameter; } + + /** + * @brief GetStats_Med_Hole_Diameter3DU - Average diameter of holes + * @return dimension in 3D units + */ float GetStats_Med_Hole_Diameter3DU() const { return m_stats_hole_med_diameter; } + + /** + * @brief GetStats_Med_Track_Width - Average width of the tracks + * @return dimensions in 3D units + */ float GetStats_Med_Track_Width() const { return m_stats_track_med_width; } + /** + * @brief GetNrSegmentsCircle + * @param aDiameter: diameter in 3DU + * @return number of sides that should be used in that circle + */ + unsigned int GetNrSegmentsCircle( float aDiameter3DU ) const; + + /** + * @brief GetNrSegmentsCircle + * @param aDiameterBUI: diameter in board unities + * @return number of sides that should be used in that circle + */ + unsigned int GetNrSegmentsCircle( int aDiameterBUI ) const; + + /** + * @brief GetCircleCorrectionFactor - computes a angle correction + * factor used when creating circles + * @param aNrSides: the number of segments sides of the circle + * @return a factor to apply to contour creation + */ + double GetCircleCorrectionFactor( int aNrSides ) const; + + /** + * @brief GetPolyMap - Get maps of polygons's layers + * @return the map with polygons's layers + */ + const MAP_POLY &GetPolyMap() const { return m_layers_poly; } + + const MAP_POLY &GetPolyMapHoles_Inner() const { return m_layers_inner_holes_poly; } + + const MAP_POLY &GetPolyMapHoles_Outer() const { return m_layers_outer_holes_poly; } + private: void createBoardPolygon(); - void createLayers(); + void createLayers( REPORTER *aStatusTextReporter ); void destroyLayers(); // Helper functions to create the board COBJECT2D *createNewTrack( const TRACK* aTrack , int aClearanceValue ) const; - void createNewPad(const D_PAD* aPad, CGENERICCONTAINER2D *aDstContainer, const wxSize &aInflateValue ) const; - void createNewPadWithClearance( const D_PAD* aPad, CGENERICCONTAINER2D *aDstContainer, int aClearanceValue ) const; - COBJECT2D *createNewPadDrill(const D_PAD* aPad, int aInflateValue); - void AddPadsShapesWithClearanceToContainer( const MODULE* aModule, CGENERICCONTAINER2D *aDstContainer, LAYER_ID aLayerId, int aInflateValue, bool aSkipNPTHPadsWihNoCopper ); - void AddGraphicsShapesWithClearanceToContainer( const MODULE* aModule, CGENERICCONTAINER2D *aDstContainer, LAYER_ID aLayerId, int aInflateValue ); - void AddShapeWithClearanceToContainer( const TEXTE_PCB* aTextPCB, CGENERICCONTAINER2D *aDstContainer, LAYER_ID aLayerId, int aClearanceValue ); - void AddShapeWithClearanceToContainer(const DRAWSEGMENT* aDrawSegment, CGENERICCONTAINER2D *aDstContainer, LAYER_ID aLayerId, int aClearanceValue ); - void AddSolidAreasShapesToContainer( const ZONE_CONTAINER* aZoneContainer, CGENERICCONTAINER2D *aDstContainer, LAYER_ID aLayerId ); - void TransformArcToSegments(const wxPoint &aCentre, const wxPoint &aStart, double aArcAngle, int aCircleToSegmentsCount, int aWidth, CGENERICCONTAINER2D *aDstContainer , const BOARD_ITEM &aBoardItem); - void buildPadShapeThickOutlineAsSegments( const D_PAD* aPad, CGENERICCONTAINER2D *aDstContainer, int aWidth); + + void createNewPad( const D_PAD* aPad, + CGENERICCONTAINER2D *aDstContainer, + const wxSize &aInflateValue ) const; + + void createNewPadWithClearance( const D_PAD *aPad, + CGENERICCONTAINER2D *aDstContainer, + int aClearanceValue ) const; + + COBJECT2D *createNewPadDrill( const D_PAD* aPad, int aInflateValue ); + + void AddPadsShapesWithClearanceToContainer( const MODULE *aModule, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId, + int aInflateValue, + bool aSkipNPTHPadsWihNoCopper ); + + void AddGraphicsShapesWithClearanceToContainer( const MODULE *aModule, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId, + int aInflateValue ); + + void AddShapeWithClearanceToContainer( const TEXTE_PCB *aTextPCB, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId, + int aClearanceValue ); + + void AddShapeWithClearanceToContainer( const DRAWSEGMENT *aDrawSegment, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId, + int aClearanceValue ); + + void AddSolidAreasShapesToContainer( const ZONE_CONTAINER *aZoneContainer, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId ); + + void TransformArcToSegments( const wxPoint &aCentre, + const wxPoint &aStart, + double aArcAngle, + int aCircleToSegmentsCount, + int aWidth, + CGENERICCONTAINER2D *aDstContainer, + const BOARD_ITEM &aBoardItem ); + + void buildPadShapeThickOutlineAsSegments( const D_PAD *aPad, + CGENERICCONTAINER2D *aDstContainer, + int aWidth ); + + // Helper functions to create poly contours + void buildPadShapeThickOutlineAsPolygon( const D_PAD *aPad, + SHAPE_POLY_SET &aCornerBuffer, + int aWidth) const; + + void transformPadsShapesWithClearanceToPolygon( const DLIST &aPads, + LAYER_ID aLayer, + SHAPE_POLY_SET &aCornerBuffer, + int aInflateValue, + bool aSkipNPTHPadsWihNoCopper) const; + + void transformGraphicModuleEdgeToPolygonSet( const MODULE *aModule, + LAYER_ID aLayer, + SHAPE_POLY_SET& aCornerBuffer ) const; + + void buildPadShapePolygon( const D_PAD *aPad, + SHAPE_POLY_SET &aCornerBuffer, + wxSize aInflateValue, + int aSegmentsPerCircle, + double aCorrectionFactor ) const; + public: - wxColour m_BgColor; - wxColour m_BgColor_Top; + SFVEC3D m_BgColorBot; ///< background bottom color + SFVEC3D m_BgColorTop; ///< background top color + SFVEC3D m_BoardBodyColor; ///< in realistic mode: FR4 board color + SFVEC3D m_SolderMaskColor; ///< in realistic mode: solder mask color + SFVEC3D m_SolderPasteColor; ///< in realistic mode: solder paste color + SFVEC3D m_SilkScreenColor; ///< in realistic mode: SilkScreen color + SFVEC3D m_CopperColor; ///< in realistic mode: copper color + private: + + /// Current board BOARD *m_board; + /// pointer to the 3d model manager + S3D_CACHE *m_3d_model_manager; + + // Render options - std::vector< bool > m_drawFlags; ///< options flags to render the board - GRID3D_TYPE m_3D_Grid_type; ///< Stores the current grid type + + /// options flags to render the board + std::vector< bool > m_drawFlags; + + /// Stores the current grid type + GRID3D_TYPE m_3D_grid_type; + + /// render engine currently on use + RENDER_ENGINE m_render_engine; + + /// mode to render the 3d shape models material + MATERIAL_MODE m_material_mode; + // Pcb board position - wxPoint m_boardPos; ///< center board actual position in board units - wxSize m_boardSize; ///< board actual size in board units - SFVEC3F m_boardCenter; ///< 3d center position of the pcb board in 3d units + + /// center board actual position in board units + wxPoint m_boardPos; + + /// board actual size in board units + wxSize m_boardSize; + + /// 3d center position of the pcb board in 3d units + SFVEC3F m_boardCenter; + // Pcb board bounding boxes - CBBOX m_boardBoudingBox; ///< 3d bouding box of the pcb board in 3d units - CBBOX2D m_board2dBBox3DU; ///< 2d bouding box of the pcb board in 3d units - SHAPE_POLY_SET m_boardPoly; ///< PCB board outline polygon + /// 3d bouding box of the pcb board in 3d units + CBBOX m_boardBoudingBox; + + /// 2d bouding box of the pcb board in 3d units + CBBOX2D m_board2dBBox3DU; + + /// It contains polygon contours for each layer + MAP_POLY m_layers_poly; + + /// It contains polygon contours for holes of each layer (outer holes) + MAP_POLY m_layers_outer_holes_poly; + + /// It contains polygon contours for holes of each layer (inner holes) + MAP_POLY m_layers_inner_holes_poly; + + /// It contains polygon contours for (just) non plated through holes (outer cylinder) + SHAPE_POLY_SET m_through_outer_holes_poly_NPTH; + + /// It contains polygon contours for through holes (outer cylinder) + SHAPE_POLY_SET m_through_outer_holes_poly; + + /// It contains polygon contours for through holes (inner cylinder) + SHAPE_POLY_SET m_through_inner_holes_poly; + + /// It contains polygon contours for through holes vias (outer cylinder) + SHAPE_POLY_SET m_through_outer_holes_vias_poly; + + /// It contains polygon contours for through holes vias (inner cylinder) + SHAPE_POLY_SET m_through_inner_holes_vias_poly; + + /// PCB board outline polygon + SHAPE_POLY_SET m_board_poly; + // 2D element containers - MAP_CONTAINER_2D m_layers_container2D; ///< It contains the 2d elements of each layer - MAP_CONTAINER_2D m_layers_holes2D; ///< It contains the holes per each layer - CBVHCONTAINER2D m_throughHoles_inflated; ///< It contains the list of throughHoles of the board, the radius of the hole is inflated with the copper tickness - CBVHCONTAINER2D m_throughHoles; ///< It contains the list of throughHoles of the board, the radius of the hole is inflated with the copper tickness + + /// It contains the 2d elements of each layer + MAP_CONTAINER_2D m_layers_container2D; + + /// It contains the holes per each layer + MAP_CONTAINER_2D m_layers_holes2D; + + /// It contains the list of throughHoles of the board, + /// the radius of the hole is inflated with the copper tickness + CBVHCONTAINER2D m_through_holes_outer; + + /// It contains the list of throughHoles of the board, + /// the radius is the inner hole + CBVHCONTAINER2D m_through_holes_inner; + + /// It contains the list of throughHoles vias of the board, + /// the radius of the hole is inflated with the copper tickness + CBVHCONTAINER2D m_through_holes_vias_outer; + + /// It contains the list of throughHoles vias of the board, + /// the radius of the hole + CBVHCONTAINER2D m_through_holes_vias_inner; + // Layers information - unsigned int m_copperLayersCount; ///< Number of copper layers actually used by the board - double m_biuTo3Dunits; ///< Normalization scale to convert board internal units to 3D units to normalize 3D units between -1.0 and +1.0 - float m_layerZcoordTop[LAYER_ID_COUNT]; ///< Top (End) Z position of each layer (normalized) - float m_layerZcoordBottom[LAYER_ID_COUNT]; ///< Bottom (Start) Z position of each layer (normalized) - float m_copperThickness; ///< Copper thickness (normalized) - float m_epoxyThickness; ///< Epoxy thickness (normalized) - float m_nonCopperLayerThickness; ///< Non copper layers thickness + + /// Number of copper layers actually used by the board + unsigned int m_copperLayersCount; + + /// Normalization scale to convert board internal units to 3D units to + /// normalize 3D units between -1.0 and +1.0 + double m_biuTo3Dunits; + + /// Top (End) Z position of each layer (normalized) + float m_layerZcoordTop[LAYER_ID_COUNT]; + + /// Bottom (Start) Z position of each layer (normalized) + float m_layerZcoordBottom[LAYER_ID_COUNT]; + + /// Copper thickness (normalized) + float m_copperThickness3DU; + + /// Epoxy thickness (normalized) + float m_epoxyThickness3DU; + + /// Non copper layers thickness + float m_nonCopperLayerThickness3DU; + // Cameras - CCAMERA &m_currentCamera; ///< Holds a pointer to current camera in use. + + /// Holds a pointer to current camera in use. + CCAMERA &m_currentCamera; CTRACK_BALL m_trackBallCamera; + /// min factor used for cicle segment approximation calculation + float m_calc_seg_min_factor3DU; + + /// max factor used for cicle segment approximation calculation + float m_calc_seg_max_factor3DU; + + // Statistics + + /// Number of tracks in the board unsigned int m_stats_nr_tracks; + + /// Track average width float m_stats_track_med_width; - unsigned int m_stats_nr_vias; ///< Nr of vias - float m_stats_via_med_hole_diameter; ///< Computed medium diameter of the via holes in 3dunits + + /// Nr of vias + unsigned int m_stats_nr_vias; + + /// Computed medium diameter of the via holes in 3D units + float m_stats_via_med_hole_diameter; + + /// number of holes in the board unsigned int m_stats_nr_holes; - float m_stats_hole_med_diameter; ///< Computed medium diameter of the holes in 3dunits + + /// Computed medium diameter of the holes in 3D units + float m_stats_hole_med_diameter; /** * Trace mask used to enable or disable the trace output of this class. @@ -371,6 +668,7 @@ class CINFO3D_VISU }; +/// This is a dummy visualization configuration extern CINFO3D_VISU G_null_CINFO3D_VISU; #endif // CINFO3D_VISU_H diff --git a/3d-viewer/3d_canvas/create_layer_items.cpp b/3d-viewer/3d_canvas/create_layer_items.cpp new file mode 100644 index 0000000000..4c10d5e2da --- /dev/null +++ b/3d-viewer/3d_canvas/create_layer_items.cpp @@ -0,0 +1,2171 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file create_layer_items.cpp + * @brief This file implements the creation of the pcb board. + * It is based on the function found in the files: + * board_items_to_polygon_shape_transform.cpp + * board_items_to_polygon_shape_transform.cpp + */ + +#include "cinfo3d_visu.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/cring2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h" +#include "../3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes3D/ccylinder.h" +#include "../3d_rendering/3d_render_raytracing/shapes3D/clayeritem.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +// These variables are parameters used in addTextSegmToContainer. +// But addTextSegmToContainer is a call-back function, +// so we cannot send them as arguments. +static int s_textWidth; +static CGENERICCONTAINER2D *s_dstcontainer = NULL; +static float s_biuTo3Dunits; +static const CBBOX2D *s_boardBBox3DU = NULL; +static const BOARD_ITEM *s_boardItem = NULL; + +// This is a call back function, used by DrawGraphicText to draw the 3D text shape: +void addTextSegmToContainer( int x0, int y0, int xf, int yf ) +{ + wxASSERT( s_boardBBox3DU != NULL ); + wxASSERT( s_dstcontainer != NULL ); + + const SFVEC2F start3DU( x0 * s_biuTo3Dunits, -y0 * s_biuTo3Dunits ); + const SFVEC2F end3DU ( xf * s_biuTo3Dunits, -yf * s_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + s_dstcontainer->Add( new CFILLEDCIRCLE2D( start3DU, + s_textWidth * s_biuTo3Dunits, + *s_boardItem) ); + else + s_dstcontainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + s_textWidth * s_biuTo3Dunits, + *s_boardItem ) ); +} + + +// Based on +// void TEXTE_PCB::TransformShapeWithClearanceToPolygonSet +// board_items_to_polygon_shape_transform.cpp +void CINFO3D_VISU::AddShapeWithClearanceToContainer( const TEXTE_PCB* aTextPCB, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId, + int aClearanceValue ) +{ + wxSize size = aTextPCB->GetSize(); + + if( aTextPCB->IsMirrored() ) + size.x = -size.x; + + s_boardItem = (const BOARD_ITEM *)&aTextPCB; + s_dstcontainer = aDstContainer; + s_textWidth = aTextPCB->GetThickness() + ( 2 * aClearanceValue ); + s_biuTo3Dunits = m_biuTo3Dunits; + s_boardBBox3DU = &m_board2dBBox3DU; + + // not actually used, but needed by DrawGraphicText + const EDA_COLOR_T dummy_color = BLACK; + + if( aTextPCB->IsMultilineAllowed() ) + { + wxArrayString strings_list; + wxStringSplit( aTextPCB->GetShownText(), strings_list, '\n' ); + std::vector positions; + positions.reserve( strings_list.Count() ); + aTextPCB->GetPositionsOfLinesOfMultilineText( positions, + strings_list.Count() ); + + for( unsigned ii = 0; ii < strings_list.Count(); ++ii ) + { + wxString txt = strings_list.Item( ii ); + + DrawGraphicText( NULL, NULL, positions[ii], dummy_color, + txt, aTextPCB->GetOrientation(), size, + aTextPCB->GetHorizJustify(), aTextPCB->GetVertJustify(), + aTextPCB->GetThickness(), aTextPCB->IsItalic(), + true, addTextSegmToContainer ); + } + } + else + { + DrawGraphicText( NULL, NULL, aTextPCB->GetTextPosition(), dummy_color, + aTextPCB->GetShownText(), aTextPCB->GetOrientation(), size, + aTextPCB->GetHorizJustify(), aTextPCB->GetVertJustify(), + aTextPCB->GetThickness(), aTextPCB->IsItalic(), + true, addTextSegmToContainer ); + } +} + + +// Based on +// void MODULE::TransformGraphicShapesWithClearanceToPolygonSet +// board_items_to_polygon_shape_transform.cpp#L204 +void CINFO3D_VISU::AddGraphicsShapesWithClearanceToContainer( const MODULE* aModule, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId, + int aInflateValue ) +{ + std::vector texts; // List of TEXTE_MODULE to convert + EDGE_MODULE* outline; + + for( EDA_ITEM* item = aModule->GraphicalItems(); + item != NULL; + item = item->Next() ) + { + switch( item->Type() ) + { + case PCB_MODULE_TEXT_T: + { + TEXTE_MODULE* text = static_cast( item ); + + if( text->GetLayer() == aLayerId && text->IsVisible() ) + texts.push_back( text ); + } + break; + + + case PCB_MODULE_EDGE_T: + { + outline = (EDGE_MODULE*) item; + + if( outline->GetLayer() != aLayerId ) + break; + + AddShapeWithClearanceToContainer( (const DRAWSEGMENT *)outline, + aDstContainer, + aLayerId, 0 ); + } + break; + + default: + break; + } + } + + // Convert texts sur modules + if( aModule->Reference().GetLayer() == aLayerId && aModule->Reference().IsVisible() ) + texts.push_back( &aModule->Reference() ); + + if( aModule->Value().GetLayer() == aLayerId && aModule->Value().IsVisible() ) + texts.push_back( &aModule->Value() ); + + s_boardItem = (const BOARD_ITEM *)&aModule->Value(); + s_dstcontainer = aDstContainer; + s_biuTo3Dunits = m_biuTo3Dunits; + s_boardBBox3DU = &m_board2dBBox3DU; + + for( unsigned ii = 0; ii < texts.size(); ++ii ) + { + TEXTE_MODULE *textmod = texts[ii]; + s_textWidth = textmod->GetThickness() + ( 2 * aInflateValue ); + wxSize size = textmod->GetSize(); + + if( textmod->IsMirrored() ) + size.x = -size.x; + + DrawGraphicText( NULL, NULL, textmod->GetTextPosition(), BLACK, + textmod->GetShownText(), textmod->GetDrawRotation(), size, + textmod->GetHorizJustify(), textmod->GetVertJustify(), + textmod->GetThickness(), textmod->IsItalic(), + true, addTextSegmToContainer ); + } +} + + +COBJECT2D *CINFO3D_VISU::createNewTrack( const TRACK* aTrack, + int aClearanceValue ) const +{ + SFVEC2F start3DU( aTrack->GetStart().x * m_biuTo3Dunits, + -aTrack->GetStart().y * m_biuTo3Dunits ); // y coord is inverted + + switch( aTrack->Type() ) + { + case PCB_VIA_T: + { + const float radius = ( ( aTrack->GetWidth() / 2 ) + aClearanceValue ) * m_biuTo3Dunits; + + return new CFILLEDCIRCLE2D( start3DU, radius, *aTrack ); + } + break; + + default: + { + wxASSERT( aTrack->Type() == PCB_TRACE_T ); + + SFVEC2F end3DU ( aTrack->GetEnd().x * m_biuTo3Dunits, + -aTrack->GetEnd().y * m_biuTo3Dunits ); + + // Cannot add segments that have the same start and end point + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + const float radius = ((aTrack->GetWidth() / 2) + aClearanceValue) * m_biuTo3Dunits; + + return new CFILLEDCIRCLE2D( start3DU, radius, *aTrack ); + } + else + { + const float width = (aTrack->GetWidth() + 2 * aClearanceValue ) * m_biuTo3Dunits; + + return new CROUNDSEGMENT2D( start3DU, end3DU, width, *aTrack ); + } + } + break; + } + + return NULL; +} + + +// Based on: +// void D_PAD:: TransformShapeWithClearanceToPolygon( +// board_items_to_polygon_shape_transform.cpp +void CINFO3D_VISU::createNewPadWithClearance( const D_PAD* aPad, + CGENERICCONTAINER2D *aDstContainer, + int aClearanceValue ) const +{ + const int dx = (aPad->GetSize().x / 2) + aClearanceValue; + const int dy = (aPad->GetSize().y / 2) + aClearanceValue; + + if( !dx || !dy ) + { + wxLogTrace( m_logTrace, + wxT( "CINFO3D_VISU::createNewPadWithClearance - found an invalid pad" ) ); + + return; + } + + wxPoint PadShapePos = aPad->ShapePos(); // Note: for pad having a shape offset, + // the pad position is NOT the shape position + + switch( aPad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + { + const float radius = dx * m_biuTo3Dunits; + + const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits, + -PadShapePos.y * m_biuTo3Dunits ); + + aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) ); + } + break; + + case PAD_SHAPE_OVAL: + { + if( abs( dx - dy ) == 0 ) + { + // The segment object cannot store start and end the same position, + // so add a circle instead + const float radius = dx * m_biuTo3Dunits; + + const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits, + -PadShapePos.y * m_biuTo3Dunits ); + + aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) ); + } + else + { + // An oval pad has the same shape as a segment with rounded ends + + int iwidth; + wxPoint shape_offset = wxPoint( 0, 0 ); + + if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis + { + shape_offset.y = dy - dx; + iwidth = dx * 2; + } + else //if( dy <= dx ) + { + shape_offset.x = dy - dx; + iwidth = dy * 2; + } + + RotatePoint( &shape_offset, aPad->GetOrientation() ); + + const wxPoint start = PadShapePos - shape_offset; + const wxPoint end = PadShapePos + shape_offset; + + const SFVEC2F start3DU( start.x * m_biuTo3Dunits, -start.y * m_biuTo3Dunits ); + const SFVEC2F end3DU ( end.x * m_biuTo3Dunits, -end.y * m_biuTo3Dunits ); + + // Cannot add segments that have the same start and end point + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + (iwidth / 2) * m_biuTo3Dunits, + *aPad ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + iwidth * m_biuTo3Dunits, + *aPad ) ); + } + } + } + break; + + case PAD_SHAPE_TRAPEZOID: + case PAD_SHAPE_RECT: + { + // https://github.com/KiCad/kicad-source-mirror/blob/0cab3e47ad8097db7b898b3cef2cf9b235318ca3/pcbnew/board_items_to_polygon_shape_transform.cpp#L613 + + wxPoint corners[4]; + aPad->BuildPadPolygon( corners, wxSize( 0, 0), aPad->GetOrientation() ); + + SFVEC2F corners3DU[4]; + + // Note: for pad having a shape offset, + // the pad position is NOT the shape position + for( unsigned int ii = 0; ii < 4; ++ii ) + { + corners[ii] += aPad->ShapePos(); // Shift origin to position + + corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits, + -corners[ii].y * m_biuTo3Dunits ); + } + + + // Learn more at: + // https://lists.launchpad.net/kicad-developers/msg18729.html + + // Add the PAD polygon + aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0], + corners3DU[1], + corners3DU[2], + corners3DU[3], + *aPad ) ); + + // Add the PAD contours + // !TODO: check the corners because it cannot add + // roundsegments that are in the same start and end position + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[0], + corners3DU[1], + aClearanceValue * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[1], + corners3DU[2], + aClearanceValue * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[2], + corners3DU[3], + aClearanceValue * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[3], + corners3DU[0], + aClearanceValue * 2.0f * m_biuTo3Dunits, + *aPad ) ); + } + break; + + case PAD_SHAPE_ROUNDRECT: + { + const int pad_radius = aPad->GetRoundRectCornerRadius(); + const int rounding_radius = pad_radius + aClearanceValue; + + wxSize shapesize( aPad->GetSize() ); + shapesize.x += aClearanceValue * 2; + shapesize.y += aClearanceValue * 2; + + wxPoint corners[4]; + + GetRoundRectCornerCenters( corners, + rounding_radius, + PadShapePos, + shapesize, + aPad->GetOrientation() ); + + SFVEC2F corners3DU[4]; + + for( unsigned int ii = 0; ii < 4; ++ii ) + corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits, + -corners[ii].y * m_biuTo3Dunits ); + + // Add the PAD polygon (For some reason the corners need + // to be inverted to display with the correctly orientation) + aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0], + corners3DU[3], + corners3DU[2], + corners3DU[1], + *aPad ) ); + + // Add the PAD contours + // !TODO: check the corners because it cannot add + // roundsegments that are in the same start and end position + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[0], + corners3DU[1], + rounding_radius * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[1], + corners3DU[2], + rounding_radius * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[2], + corners3DU[3], + rounding_radius * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[3], + corners3DU[0], + rounding_radius * 2.0f * m_biuTo3Dunits, + *aPad ) ); + } + break; + + default: + wxFAIL_MSG( "CINFO3D_VISU::createNewPadWithClearance - a pad shape type is not implemented" ); + break; + } +} + + +// Based on: +// BuildPadDrillShapePolygon +// board_items_to_polygon_shape_transform.cpp +COBJECT2D *CINFO3D_VISU::createNewPadDrill( const D_PAD* aPad, int aInflateValue ) +{ + wxSize drillSize = aPad->GetDrillSize(); + + if( !drillSize.x || !drillSize.y ) + { + wxLogTrace( m_logTrace, wxT( "CINFO3D_VISU::createNewPadDrill - found an invalid pad" ) ); + return NULL; + } + + if( drillSize.x == drillSize.y ) // usual round hole + { + const int radius = (drillSize.x / 2) + aInflateValue; + + const SFVEC2F center( aPad->GetPosition().x * m_biuTo3Dunits, + -aPad->GetPosition().y * m_biuTo3Dunits ); + + return new CFILLEDCIRCLE2D( center, radius * m_biuTo3Dunits, *aPad ); + + } + else // Oblong hole + { + wxPoint start, end; + int width; + + aPad->GetOblongDrillGeometry( start, end, width ); + + width += aInflateValue * 2; + start += aPad->GetPosition(); + end += aPad->GetPosition(); + + SFVEC2F start3DU( start.x * m_biuTo3Dunits, + -start.y * m_biuTo3Dunits ); + + SFVEC2F end3DU ( end.x * m_biuTo3Dunits, + -end.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + return new CFILLEDCIRCLE2D( start3DU, (width / 2) * m_biuTo3Dunits, *aPad ); + } + else + { + return new CROUNDSEGMENT2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad ); + } + } + + return NULL; +} + + +// This function pretends to be like the +// void D_PAD::BuildPadShapePolygon( +// board_items_to_polygon_shape_transform.cpp +void CINFO3D_VISU::createNewPad( const D_PAD* aPad, + CGENERICCONTAINER2D *aDstContainer, + const wxSize &aInflateValue ) const +{ + switch( aPad->GetShape() ) + { + default: + wxFAIL_MSG( wxT( "CINFO3D_VISU::createNewPad: found a not implemented pad shape (new shape?)" ) ); + break; + + case PAD_SHAPE_CIRCLE: + case PAD_SHAPE_OVAL: + case PAD_SHAPE_ROUNDRECT: + createNewPadWithClearance( aPad, aDstContainer, aInflateValue.x ); + break; + + case PAD_SHAPE_TRAPEZOID: + case PAD_SHAPE_RECT: + wxPoint corners[4]; + aPad->BuildPadPolygon( corners, aInflateValue, aPad->GetOrientation() ); + + // Note: for pad having a shape offset, + // the pad position is NOT the shape position + for( unsigned int ii = 0; ii < 4; ++ii ) + corners[ii] += aPad->ShapePos(); // Shift origin to position + + aDstContainer->Add( new CPOLYGON4PTS2D( + SFVEC2F( corners[0].x * m_biuTo3Dunits, + -corners[0].y * m_biuTo3Dunits ), + SFVEC2F( corners[1].x * m_biuTo3Dunits, + -corners[1].y * m_biuTo3Dunits ), + SFVEC2F( corners[2].x * m_biuTo3Dunits, + -corners[2].y * m_biuTo3Dunits ), + SFVEC2F( corners[3].x * m_biuTo3Dunits, + -corners[3].y * m_biuTo3Dunits ), + *aPad ) ); + + break; + } +} + + +void CINFO3D_VISU::AddPadsShapesWithClearanceToContainer( const MODULE* aModule, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId, + int aInflateValue, + bool aSkipNPTHPadsWihNoCopper ) +{ + const D_PAD* pad = aModule->Pads(); + + wxSize margin; + + for( ; pad != NULL; pad = pad->Next() ) + { + if( !pad->IsOnLayer( aLayerId ) ) + continue; + + // NPTH pads are not drawn on layers if the + // shape size and pos is the same as their hole: + if( aSkipNPTHPadsWihNoCopper && (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED) ) + { + if( (pad->GetDrillSize() == pad->GetSize()) && + (pad->GetOffset() == wxPoint( 0, 0 )) ) + { + switch( pad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) + continue; + break; + + case PAD_SHAPE_OVAL: + if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE ) + continue; + break; + + default: + break; + } + } + } + + switch( aLayerId ) + { + case F_Mask: + case B_Mask: + margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue; + break; + + case F_Paste: + case B_Paste: + margin = pad->GetSolderPasteMargin(); + margin.x += aInflateValue; + margin.y += aInflateValue; + break; + + default: + margin.x = margin.y = aInflateValue; + break; + } + + createNewPad( pad, aDstContainer, margin ); + } +} + +// based on TransformArcToPolygon function from +// common/convert_basic_shapes_to_polygon.cpp +void CINFO3D_VISU::TransformArcToSegments( const wxPoint &aCentre, + const wxPoint &aStart, + double aArcAngle, + int aCircleToSegmentsCount, + int aWidth, + CGENERICCONTAINER2D *aDstContainer, + const BOARD_ITEM &aBoardItem ) +{ + wxPoint arc_start, arc_end; + int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree + + arc_end = arc_start = aStart; + + if( aArcAngle != 3600 ) + { + RotatePoint( &arc_end, aCentre, -aArcAngle ); + } + + if( aArcAngle < 0 ) + { + std::swap( arc_start, arc_end ); + aArcAngle = -aArcAngle; + } + + // Compute the ends of segments and creates poly + wxPoint curr_end = arc_start; + wxPoint curr_start = arc_start; + + for( int ii = delta; ii < aArcAngle; ii += delta ) + { + curr_end = arc_start; + RotatePoint( &curr_end, aCentre, -ii ); + + const SFVEC2F start3DU( curr_start.x * m_biuTo3Dunits, -curr_start.y * m_biuTo3Dunits ); + const SFVEC2F end3DU ( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + ( aWidth / 2 ) * m_biuTo3Dunits, + aBoardItem ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + aWidth * m_biuTo3Dunits, + aBoardItem ) ); + } + + curr_start = curr_end; + } + + if( curr_end != arc_end ) + { + const SFVEC2F start3DU( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits ); + const SFVEC2F end3DU ( arc_end.x * m_biuTo3Dunits, -arc_end.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + ( aWidth / 2 ) * m_biuTo3Dunits, + aBoardItem ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + aWidth * m_biuTo3Dunits, + aBoardItem ) ); + } + } +} + +// Based on +// TransformShapeWithClearanceToPolygon +// board_items_to_polygon_shape_transform.cpp#L431 +void CINFO3D_VISU::AddShapeWithClearanceToContainer( const DRAWSEGMENT* aDrawSegment, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId, + int aClearanceValue ) +{ + // The full width of the lines to create: + const int linewidth = aDrawSegment->GetWidth() + (2 * aClearanceValue); + + switch( aDrawSegment->GetShape() ) + { + case S_CIRCLE: + { + const SFVEC2F center3DU( aDrawSegment->GetCenter().x * m_biuTo3Dunits, + -aDrawSegment->GetCenter().y * m_biuTo3Dunits ); + + const float inner_radius = (aDrawSegment->GetRadius() - linewidth / 2) * m_biuTo3Dunits; + const float outter_radius = (aDrawSegment->GetRadius() + linewidth / 2) * m_biuTo3Dunits; + + aDstContainer->Add( new CRING2D( center3DU, + inner_radius, + outter_radius, + *aDrawSegment ) ); + } + break; + + case S_ARC: + { + const unsigned int nr_segments = + GetNrSegmentsCircle( aDrawSegment->GetBoundingBox().GetSizeMax() ); + + TransformArcToSegments( aDrawSegment->GetCenter(), + aDrawSegment->GetArcStart(), + aDrawSegment->GetAngle(), + nr_segments, + aDrawSegment->GetWidth(), + aDstContainer, + *aDrawSegment ); + } + break; + + case S_SEGMENT: + { + const SFVEC2F start3DU( aDrawSegment->GetStart().x * m_biuTo3Dunits, + -aDrawSegment->GetStart().y * m_biuTo3Dunits ); + + const SFVEC2F end3DU ( aDrawSegment->GetEnd().x * m_biuTo3Dunits, + -aDrawSegment->GetEnd().y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + ( linewidth / 2 ) * m_biuTo3Dunits, + *aDrawSegment ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + linewidth * m_biuTo3Dunits, + *aDrawSegment ) ); + } + } + break; + + case S_POLYGON: + { + // Check for malformed polygon. + if( aDrawSegment->GetPolyPoints().size() > 2 ) + { + // The polygon is expected to be a simple polygon + // not self intersecting, no hole. + MODULE* module = aDrawSegment->GetParentModule(); // NULL for items not in footprints + const double orientation = module ? module->GetOrientation() : 0.0; + + // Build the polygon with the actual position and orientation: + std::vector< wxPoint> poly; + poly = aDrawSegment->GetPolyPoints(); + + for( unsigned ii = 0; ii < poly.size(); ++ii ) + { + RotatePoint( &poly[ii], orientation ); + poly[ii] += aDrawSegment->GetPosition(); + } + + // Generate polygons for the outline + clearance + + if( linewidth ) // Add thick outlines + { + CPolyPt corner1( poly[poly.size()-1] ); + + for( unsigned ii = 0; ii < poly.size(); ++ii ) + { + CPolyPt corner2( poly[ii] ); + + if( corner2 != corner1 ) + { + const SFVEC2F start3DU( corner1.x * m_biuTo3Dunits, + -corner1.y * m_biuTo3Dunits ); + + const SFVEC2F end3DU( corner2.x * m_biuTo3Dunits, + -corner2.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( + new CFILLEDCIRCLE2D( start3DU, + (linewidth / 2) * m_biuTo3Dunits, + *aDrawSegment ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + linewidth * m_biuTo3Dunits, + *aDrawSegment ) ); + } + } + + corner1 = corner2; + } + } + + // Polygon for the inside + SHAPE_LINE_CHAIN path; + + for( unsigned ii = 0; ii < poly.size(); ++ii ) + { + wxPoint corner = poly[ii]; + path.Append( corner.x, corner.y ); + } + + path.SetClosed( true ); + + SHAPE_POLY_SET polyList; + + polyList.AddOutline( path ); + + // This convert the poly in outline and holes + polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + if( polyList.IsEmpty() ) // Just for caution + break; + + Convert_shape_line_polygon_to_triangles( polyList, + *aDstContainer, + m_biuTo3Dunits, + *aDrawSegment ); + } + } + break; + + case S_CURVE: // Bezier curve (not yet in use in KiCad) + break; + + default: + break; + } +} + + +// Based on +// TransformSolidAreasShapesToPolygonSet +// board_items_to_polygon_shape_transform.cpp +void CINFO3D_VISU::AddSolidAreasShapesToContainer( const ZONE_CONTAINER* aZoneContainer, + CGENERICCONTAINER2D *aDstContainer, + LAYER_ID aLayerId ) +{ + // Copy the polys list because we have to simplify it + SHAPE_POLY_SET polyList = SHAPE_POLY_SET(aZoneContainer->GetFilledPolysList()); + + // This convert the poly in outline and holes + + // Note: This two sequencial calls are need in order to get + // the triangulation function to work properly. + polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + if( polyList.IsEmpty() ) + return; + + Convert_shape_line_polygon_to_triangles( polyList, + *aDstContainer, + m_biuTo3Dunits, + *aZoneContainer ); + + + // add filled areas outlines, which are drawn with thick lines segments + // ///////////////////////////////////////////////////////////////////////// + for( int i = 0; i < polyList.OutlineCount(); ++i ) + { + // Add outline + const SHAPE_LINE_CHAIN& pathOutline = polyList.COutline( i ); + + for( int j = 0; j < pathOutline.PointCount(); ++j ) + { + const VECTOR2I& a = pathOutline.CPoint( j ); + const VECTOR2I& b = pathOutline.CPoint( j + 1 ); + + SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); + SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + (aZoneContainer->GetMinThickness() / 2) * + m_biuTo3Dunits, + *aZoneContainer ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, + aZoneContainer->GetMinThickness() * + m_biuTo3Dunits, + *aZoneContainer ) ); + } + } + + // Add holes (of the poly, ie: the open parts) for this outline + for( int h = 0; h < polyList.HoleCount( i ); ++h ) + { + const SHAPE_LINE_CHAIN& pathHole = polyList.CHole( i, h ); + + for( int j = 0; j < pathHole.PointCount(); j++ ) + { + const VECTOR2I& a = pathHole.CPoint( j ); + const VECTOR2I& b = pathHole.CPoint( j + 1 ); + + SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); + SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( + new CFILLEDCIRCLE2D( start3DU, + (aZoneContainer->GetMinThickness() / 2) * + m_biuTo3Dunits, + *aZoneContainer ) ); + } + else + { + aDstContainer->Add( + new CROUNDSEGMENT2D( start3DU, end3DU, + aZoneContainer->GetMinThickness() * + m_biuTo3Dunits, + *aZoneContainer ) ); + } + } + } + } +} + + + +void CINFO3D_VISU::buildPadShapeThickOutlineAsSegments( const D_PAD* aPad, + CGENERICCONTAINER2D *aDstContainer, + int aWidth ) +{ + if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring + { + const SFVEC2F center3DU( aPad->ShapePos().x * m_biuTo3Dunits, + -aPad->ShapePos().y * m_biuTo3Dunits ); + + const int radius = aPad->GetSize().x / 2; + const float inner_radius = (radius - aWidth / 2) * m_biuTo3Dunits; + const float outter_radius = (radius + aWidth / 2) * m_biuTo3Dunits; + + aDstContainer->Add( new CRING2D( center3DU, + inner_radius, + outter_radius, + *aPad ) ); + + return; + } + + // For other shapes, draw polygon outlines + SHAPE_POLY_SET corners; + + const int segcountforcircle = GetNrSegmentsCircle( glm::min( aPad->GetSize().x, + aPad->GetSize().y) ); + + const double correctionFactor = GetCircleCorrectionFactor( segcountforcircle ); + + aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ), + // This two factors are only expected to be used if render an oval + segcountforcircle, correctionFactor ); + + + // Add outlines as thick segments in polygon buffer + + const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); + + for( int j = 0; j < path.PointCount(); j++ ) + { + const VECTOR2I& a = path.CPoint( j ); + const VECTOR2I& b = path.CPoint( j + 1 ); + + SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); + SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + (aWidth / 2) * m_biuTo3Dunits, + *aPad ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, + aWidth * m_biuTo3Dunits, + *aPad ) ); + } + } +} + + +void CINFO3D_VISU::destroyLayers() +{ + if( !m_layers_poly.empty() ) + { + for( MAP_POLY::iterator ii = m_layers_poly.begin(); + ii != m_layers_poly.end(); + ++ii ) + { + delete ii->second; + ii->second = NULL; + } + + m_layers_poly.clear(); + } + + if( !m_layers_inner_holes_poly.empty() ) + { + for( MAP_POLY::iterator ii = m_layers_inner_holes_poly.begin(); + ii != m_layers_inner_holes_poly.end(); + ++ii ) + { + delete ii->second; + ii->second = NULL; + } + + m_layers_inner_holes_poly.clear(); + } + + if( !m_layers_outer_holes_poly.empty() ) + { + for( MAP_POLY::iterator ii = m_layers_outer_holes_poly.begin(); + ii != m_layers_outer_holes_poly.end(); + ++ii ) + { + delete ii->second; + ii->second = NULL; + } + + m_layers_outer_holes_poly.clear(); + } + + if( !m_layers_container2D.empty() ) + { + for( MAP_CONTAINER_2D::iterator ii = m_layers_container2D.begin(); + ii != m_layers_container2D.end(); + ++ii ) + { + delete ii->second; + ii->second = NULL; + } + + m_layers_container2D.clear(); + } + + if( !m_layers_holes2D.empty() ) + { + for( MAP_CONTAINER_2D::iterator ii = m_layers_holes2D.begin(); + ii != m_layers_holes2D.end(); + ++ii ) + { + delete ii->second; + ii->second = NULL; + } + + m_layers_holes2D.clear(); + } + + m_through_holes_inner.Clear(); + m_through_holes_outer.Clear(); + m_through_holes_vias_outer.Clear(); + m_through_holes_vias_inner.Clear(); + m_through_outer_holes_poly_NPTH.RemoveAllContours(); + m_through_outer_holes_poly.RemoveAllContours(); + //m_through_inner_holes_poly.RemoveAllContours(); + + m_through_outer_holes_vias_poly.RemoveAllContours(); + m_through_inner_holes_vias_poly.RemoveAllContours(); +} + + +void CINFO3D_VISU::createLayers( REPORTER *aStatusTextReporter ) +{ + // Number of segments to draw a circle using segments (used on countour zones + // and text copper elements ) + const int segcountforcircle = 12; + const double correctionFactor = GetCircleCorrectionFactor( segcountforcircle ); + + // segments to draw a circle to build texts. Is is used only to build + // the shape of each segment of the stroke font, therefore no need to have + // many segments per circle. + const int segcountInStrokeFont = 12; + const double correctionFactorStroke = GetCircleCorrectionFactor( segcountInStrokeFont ); + + destroyLayers(); + + // Build Copper layers + // Based on: https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L692 + // ///////////////////////////////////////////////////////////////////////// + + #ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_startCopperLayersTime = GetRunningMicroSecs(); + + unsigned start_Time = stats_startCopperLayersTime; +#endif + + LAYER_ID cu_seq[MAX_CU_LAYERS]; + LSET cu_set = LSET::AllCuMask( m_copperLayersCount ); + + m_stats_nr_tracks = 0; + m_stats_track_med_width = 0; + m_stats_nr_vias = 0; + m_stats_via_med_hole_diameter = 0; + m_stats_nr_holes = 0; + m_stats_hole_med_diameter = 0; + + // Prepare track list, convert in a vector. Calc statistic for the holes + // ///////////////////////////////////////////////////////////////////////// + std::vector< const TRACK *> trackList; + trackList.clear(); + trackList.reserve( m_board->m_Track.GetCount() ); + + for( const TRACK* track = m_board->m_Track; track; track = track->Next() ) + { + if( !Is3DLayerEnabled( track->GetLayer() ) ) // Skip non enabled layers + continue; + + // Note: a TRACK holds normal segment tracks and + // also vias circles (that have also drill values) + trackList.push_back( track ); + + if( track->Type() == PCB_VIA_T ) + { + const VIA *via = static_cast< const VIA*>( track ); + m_stats_nr_vias++; + m_stats_via_med_hole_diameter += via->GetDrillValue() * m_biuTo3Dunits; + } + else + { + m_stats_nr_tracks++; + } + + m_stats_track_med_width += track->GetWidth() * m_biuTo3Dunits; + } + + if( m_stats_nr_tracks ) + m_stats_track_med_width /= (float)m_stats_nr_tracks; + + if( m_stats_nr_vias ) + m_stats_via_med_hole_diameter /= (float)m_stats_nr_vias; + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T01: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Prepare copper layers index and containers + // ///////////////////////////////////////////////////////////////////////// + std::vector< LAYER_ID > layer_id; + layer_id.clear(); + layer_id.reserve( m_copperLayersCount ); + + for( unsigned i = 0; i < DIM( cu_seq ); ++i ) + cu_seq[i] = ToLAYER_ID( B_Cu - i ); + + for( LSEQ cu = cu_set.Seq( cu_seq, DIM( cu_seq ) ); cu; ++cu ) + { + const LAYER_ID curr_layer_id = *cu; + + if( !Is3DLayerEnabled( curr_layer_id ) ) // Skip non enabled layers + continue; + + layer_id.push_back( curr_layer_id ); + + CBVHCONTAINER2D *layerContainer = new CBVHCONTAINER2D; + m_layers_container2D[curr_layer_id] = layerContainer; + + if( GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) && + (m_render_engine == RENDER_ENGINE_OPENGL_LEGACY) ) + { + SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET; + m_layers_poly[curr_layer_id] = layerPoly; + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T02: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Create tracks and vias" ) ); + + // Create tracks as objects and add it to container + // ///////////////////////////////////////////////////////////////////////// + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + wxASSERT( m_layers_container2D.find( curr_layer_id ) != m_layers_container2D.end() ); + + CBVHCONTAINER2D *layerContainer = m_layers_container2D[curr_layer_id]; + + // ADD TRACKS + unsigned int nTracks = trackList.size(); + + for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx ) + { + const TRACK *track = trackList[trackIdx]; + + // NOTE: Vias can be on multiple layers + if( !track->IsOnLayer( curr_layer_id ) ) + continue; + + // Add object item to layer container + layerContainer->Add( createNewTrack( track, 0.0f ) ); + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T03: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Create VIAS and THTs objects and add it to holes containers + // ///////////////////////////////////////////////////////////////////////// + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + // ADD TRACKS + unsigned int nTracks = trackList.size(); + + for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx ) + { + const TRACK *track = trackList[trackIdx]; + + if( !track->IsOnLayer( curr_layer_id ) ) + continue; + + // ADD VIAS and THT + if( track->Type() == PCB_VIA_T ) + { + const VIA *via = static_cast< const VIA*>( track ); + const VIATYPE_T viatype = via->GetViaType(); + const float holediameter = via->GetDrillValue() * BiuTo3Dunits(); + const float thickness = GetCopperThickness3DU(); + const float hole_inner_radius = ( holediameter / 2.0f ); + + const SFVEC2F via_center( via->GetStart().x * m_biuTo3Dunits, + -via->GetStart().y * m_biuTo3Dunits ); + + if( viatype != VIA_THROUGH ) + { + + // Add hole objects + // ///////////////////////////////////////////////////////// + + CBVHCONTAINER2D *layerHoleContainer = NULL; + + // Check if the layer is already created + if( m_layers_holes2D.find( curr_layer_id ) == m_layers_holes2D.end() ) + { + // not found, create a new container + layerHoleContainer = new CBVHCONTAINER2D; + m_layers_holes2D[curr_layer_id] = layerHoleContainer; + } + else + { + // found + layerHoleContainer = m_layers_holes2D[curr_layer_id]; + } + + // Add a hole for this layer + layerHoleContainer->Add( new CFILLEDCIRCLE2D( via_center, + hole_inner_radius + thickness, + *track ) ); + } + else if( lIdx == 0 ) // it only adds once the THT holes + { + // Add through hole object + // ///////////////////////////////////////////////////////// + m_through_holes_outer.Add( new CFILLEDCIRCLE2D( via_center, + hole_inner_radius + thickness, + *track ) ); + + m_through_holes_vias_outer.Add( + new CFILLEDCIRCLE2D( via_center, + hole_inner_radius + thickness, + *track ) ); + + m_through_holes_inner.Add( new CFILLEDCIRCLE2D( via_center, + hole_inner_radius, + *track ) ); + + //m_through_holes_vias_inner.Add( new CFILLEDCIRCLE2D( via_center, + // hole_inner_radius, + // *track ) ); + } + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T04: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Create VIAS and THTs objects and add it to holes containers + // ///////////////////////////////////////////////////////////////////////// + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + // ADD TRACKS + const unsigned int nTracks = trackList.size(); + + for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx ) + { + const TRACK *track = trackList[trackIdx]; + + if( !track->IsOnLayer( curr_layer_id ) ) + continue; + + // ADD VIAS and THT + if( track->Type() == PCB_VIA_T ) + { + const VIA *via = static_cast< const VIA*>( track ); + const VIATYPE_T viatype = via->GetViaType(); + + if( viatype != VIA_THROUGH ) + { + + // Add VIA hole contourns + // ///////////////////////////////////////////////////////// + + // Add outter holes of VIAs + SHAPE_POLY_SET *layerOuterHolesPoly = NULL; + SHAPE_POLY_SET *layerInnerHolesPoly = NULL; + + // Check if the layer is already created + if( m_layers_outer_holes_poly.find( curr_layer_id ) == + m_layers_outer_holes_poly.end() ) + { + // not found, create a new container + layerOuterHolesPoly = new SHAPE_POLY_SET; + m_layers_outer_holes_poly[curr_layer_id] = layerOuterHolesPoly; + + wxASSERT( m_layers_inner_holes_poly.find( curr_layer_id ) == + m_layers_inner_holes_poly.end() ); + + layerInnerHolesPoly = new SHAPE_POLY_SET; + m_layers_inner_holes_poly[curr_layer_id] = layerInnerHolesPoly; + } + else + { + // found + layerOuterHolesPoly = m_layers_outer_holes_poly[curr_layer_id]; + + wxASSERT( m_layers_inner_holes_poly.find( curr_layer_id ) != + m_layers_inner_holes_poly.end() ); + + layerInnerHolesPoly = m_layers_inner_holes_poly[curr_layer_id]; + } + + const int holediameter = via->GetDrillValue(); + const int hole_outer_radius = (holediameter / 2) + GetCopperThicknessBIU(); + + TransformCircleToPolygon( *layerOuterHolesPoly, + via->GetStart(), + hole_outer_radius, + GetNrSegmentsCircle( hole_outer_radius * 2 ) ); + + TransformCircleToPolygon( *layerInnerHolesPoly, + via->GetStart(), + holediameter / 2, + GetNrSegmentsCircle( holediameter ) ); + } + else if( lIdx == 0 ) // it only adds once the THT holes + { + const int holediameter = via->GetDrillValue(); + const int hole_outer_radius = (holediameter / 2)+ GetCopperThicknessBIU(); + + // Add through hole contourns + // ///////////////////////////////////////////////////////// + TransformCircleToPolygon( m_through_outer_holes_poly, + via->GetStart(), + hole_outer_radius, + GetNrSegmentsCircle( hole_outer_radius * 2 ) ); + + TransformCircleToPolygon( m_through_inner_holes_poly, + via->GetStart(), + holediameter / 2, + GetNrSegmentsCircle( holediameter ) ); + + // Add samething for vias only + + TransformCircleToPolygon( m_through_outer_holes_vias_poly, + via->GetStart(), + hole_outer_radius, + GetNrSegmentsCircle( hole_outer_radius * 2 ) ); + + //TransformCircleToPolygon( m_through_inner_holes_vias_poly, + // via->GetStart(), + // holediameter / 2, + // GetNrSegmentsCircle( holediameter ) ); + } + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T05: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Creates outline contours of the tracks and add it to the poly of the layer + // ///////////////////////////////////////////////////////////////////////// + if( GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) && + (m_render_engine == RENDER_ENGINE_OPENGL_LEGACY) ) + { + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() ); + + SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id]; + + // ADD TRACKS + unsigned int nTracks = trackList.size(); + + for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx ) + { + const TRACK *track = trackList[trackIdx]; + + if( !track->IsOnLayer( curr_layer_id ) ) + continue; + + // Add the track contour + int nrSegments = GetNrSegmentsCircle( track->GetWidth() ); + + track->TransformShapeWithClearanceToPolygon( + *layerPoly, + 0, + nrSegments, + GetCircleCorrectionFactor( nrSegments ) ); + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T06: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Add holes of modules + // ///////////////////////////////////////////////////////////////////////// + for( const MODULE* module = m_board->m_Modules; module; module = module->Next() ) + { + const D_PAD* pad = module->Pads(); + + for( ; pad; pad = pad->Next() ) + { + const wxSize padHole = pad->GetDrillSize(); + + if( !padHole.x ) // Not drilled pad like SMD pad + continue; + + // The hole in the body is inflated by copper thickness, + // if not plated, no copper + const int inflate = (pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED) ? + GetCopperThicknessBIU() : 0; + + m_stats_nr_holes++; + m_stats_hole_med_diameter += ( ( pad->GetDrillSize().x + + pad->GetDrillSize().y ) / 2.0f ) * m_biuTo3Dunits; + + m_through_holes_outer.Add( createNewPadDrill( pad, inflate ) ); + m_through_holes_inner.Add( createNewPadDrill( pad, 0 ) ); + } + } + if( m_stats_nr_holes ) + m_stats_hole_med_diameter /= (float)m_stats_nr_holes; + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T07: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Add contours of the pad holes (pads can be Circle or Segment holes) + // ///////////////////////////////////////////////////////////////////////// + for( const MODULE* module = m_board->m_Modules; module; module = module->Next() ) + { + const D_PAD* pad = module->Pads(); + + for( ; pad; pad = pad->Next() ) + { + const wxSize padHole = pad->GetDrillSize(); + + if( !padHole.x ) // Not drilled pad like SMD pad + continue; + + // The hole in the body is inflated by copper thickness. + const int inflate = GetCopperThicknessBIU(); + + // we use the hole diameter to calculate the seg count. + // for round holes, padHole.x == padHole.y + // for oblong holes, the diameter is the smaller of (padHole.x, padHole.y) + const int diam = std::min( padHole.x, padHole.y ); + + + if( pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED ) + { + pad->BuildPadDrillShapePolygon( m_through_outer_holes_poly, + inflate, + GetNrSegmentsCircle( diam ) ); + + pad->BuildPadDrillShapePolygon( m_through_inner_holes_poly, + 0, + GetNrSegmentsCircle( diam ) ); + } + else + { + // If not plated, no copper. + pad->BuildPadDrillShapePolygon( m_through_outer_holes_poly_NPTH, + inflate, + GetNrSegmentsCircle( diam ) ); + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T08: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Add modules PADs objects to containers + // ///////////////////////////////////////////////////////////////////////// + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + wxASSERT( m_layers_container2D.find( curr_layer_id ) != m_layers_container2D.end() ); + + CBVHCONTAINER2D *layerContainer = m_layers_container2D[curr_layer_id]; + + // ADD PADS + for( const MODULE* module = m_board->m_Modules; module; module = module->Next() ) + { + // Note: NPTH pads are not drawn on copper layers when the pad + // has same shape as its hole + AddPadsShapesWithClearanceToContainer( module, + layerContainer, + curr_layer_id, + 0, + true ); + + // Micro-wave modules may have items on copper layers + AddGraphicsShapesWithClearanceToContainer( module, + layerContainer, + curr_layer_id, + 0 ); + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T09: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Add modules PADs poly contourns + // ///////////////////////////////////////////////////////////////////////// + if( GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) && + (m_render_engine == RENDER_ENGINE_OPENGL_LEGACY) ) + { + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() ); + + SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id]; + + // ADD PADS + for( const MODULE* module = m_board->m_Modules; + module; + module = module->Next() ) + { + // Construct polys + // ///////////////////////////////////////////////////////////// + + // Note: NPTH pads are not drawn on copper layers when the pad + // has same shape as its hole + transformPadsShapesWithClearanceToPolygon( module->Pads(), + curr_layer_id, + *layerPoly, + 0, + true ); + + // Micro-wave modules may have items on copper layers + module->TransformGraphicTextWithClearanceToPolygonSet( curr_layer_id, + *layerPoly, + 0, + segcountforcircle, + correctionFactor ); + + transformGraphicModuleEdgeToPolygonSet( module, curr_layer_id, *layerPoly ); + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T10: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Add graphic item on copper layers to object containers + // ///////////////////////////////////////////////////////////////////////// + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + wxASSERT( m_layers_container2D.find( curr_layer_id ) != m_layers_container2D.end() ); + + CBVHCONTAINER2D *layerContainer = m_layers_container2D[curr_layer_id]; + + // ADD GRAPHIC ITEMS ON COPPER LAYERS (texts) + for( const BOARD_ITEM* item = m_board->m_Drawings; + item; + item = item->Next() ) + { + if( !item->IsOnLayer( curr_layer_id ) ) + continue; + + switch( item->Type() ) + { + case PCB_LINE_T: // should not exist on copper layers + { + AddShapeWithClearanceToContainer( (DRAWSEGMENT*)item, + layerContainer, + curr_layer_id, + 0 ); + } + break; + + case PCB_TEXT_T: + AddShapeWithClearanceToContainer( (TEXTE_PCB*) item, + layerContainer, + curr_layer_id, + 0 ); + break; + + default: + wxLogTrace( m_logTrace, + wxT( "createLayers: item type: %d not implemented" ), + item->Type() ); + break; + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T11: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Add graphic item on copper layers to poly contourns + // ///////////////////////////////////////////////////////////////////////// + if( GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) && + (m_render_engine == RENDER_ENGINE_OPENGL_LEGACY) ) + { + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() ); + + SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id]; + + // ADD GRAPHIC ITEMS ON COPPER LAYERS (texts) + for( const BOARD_ITEM* item = m_board->m_Drawings; + item; + item = item->Next() ) + { + if( !item->IsOnLayer( curr_layer_id ) ) + continue; + + switch( item->Type() ) + { + case PCB_LINE_T: // should not exist on copper layers + { + const int nrSegments = + GetNrSegmentsCircle( item->GetBoundingBox().GetSizeMax() ); + + ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( + *layerPoly, + 0, + nrSegments, + GetCircleCorrectionFactor( nrSegments ) ); + } + break; + + case PCB_TEXT_T: + ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( + *layerPoly, + 0, + segcountforcircle, + correctionFactor ); + break; + + default: + wxLogTrace( m_logTrace, + wxT( "createLayers: item type: %d not implemented" ), + item->Type() ); + break; + } + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T12: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + if( GetFlag( FL_ZONE ) ) + { + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Create zones" ) ); + + // Add zones objects + // ///////////////////////////////////////////////////////////////////// + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + if( aStatusTextReporter ) + aStatusTextReporter->Report( wxString::Format( _( "Create zones of layer %s" ), + LSET::Name( curr_layer_id ) ) ); + + wxASSERT( m_layers_container2D.find( curr_layer_id ) != m_layers_container2D.end() ); + + CBVHCONTAINER2D *layerContainer = m_layers_container2D[curr_layer_id]; + + // ADD COPPER ZONES + for( int ii = 0; ii < m_board->GetAreaCount(); ++ii ) + { + const ZONE_CONTAINER* zone = m_board->GetArea( ii ); + const LAYER_ID zonelayer = zone->GetLayer(); + + if( zonelayer == curr_layer_id ) + { + AddSolidAreasShapesToContainer( zone, + layerContainer, + curr_layer_id ); + } + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T13: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + if( GetFlag( FL_ZONE ) && + GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) && + (m_render_engine == RENDER_ENGINE_OPENGL_LEGACY) ) + { + // Add zones poly contourns + // ///////////////////////////////////////////////////////////////////// + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() ); + + SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id]; + + // ADD COPPER ZONES + for( int ii = 0; ii < m_board->GetAreaCount(); ++ii ) + { + const ZONE_CONTAINER* zone = m_board->GetArea( ii ); + const LAYER_NUM zonelayer = zone->GetLayer(); + + if( zonelayer == curr_layer_id ) + { + zone->TransformSolidAreasShapesToPolygonSet( *layerPoly, + segcountforcircle, + correctionFactor ); + } + } + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T14: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Simplify layer polygons + // ///////////////////////////////////////////////////////////////////////// + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Simplifying polygons" ) ); + + if( GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) && + (m_render_engine == RENDER_ENGINE_OPENGL_LEGACY) ) + { + const int nLayers = layer_id.size(); + + #pragma omp parallel for + for( signed int lIdx = 0; lIdx < nLayers; ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() ); + + SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id]; + + wxASSERT( layerPoly != NULL ); + + // This will make a union of all added contourns + layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST ); + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T15: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); + start_Time = GetRunningMicroSecs(); +#endif + + // Simplify holes polygon contours + // ///////////////////////////////////////////////////////////////////////// + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Simplify holes contours" ) ); + + for( unsigned int lIdx = 0; lIdx < layer_id.size(); ++lIdx ) + { + const LAYER_ID curr_layer_id = layer_id[lIdx]; + + if( m_layers_outer_holes_poly.find( curr_layer_id ) != + m_layers_outer_holes_poly.end() ) + { + // found + SHAPE_POLY_SET *polyLayer = m_layers_outer_holes_poly[curr_layer_id]; + polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST ); + + wxASSERT( m_layers_inner_holes_poly.find( curr_layer_id ) != + m_layers_inner_holes_poly.end() ); + + polyLayer = m_layers_inner_holes_poly[curr_layer_id]; + polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST ); + } + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "T16: %.3f ms\n", (float)( GetRunningMicroSecs() - start_Time ) / 1e3 ); +#endif + // End Build Copper layers + + + // This will make a union of all added contourns + m_through_inner_holes_poly.Simplify( SHAPE_POLY_SET::PM_FAST ); + m_through_outer_holes_poly.Simplify( SHAPE_POLY_SET::PM_FAST ); + m_through_outer_holes_poly_NPTH.Simplify( SHAPE_POLY_SET::PM_FAST ); + m_through_outer_holes_vias_poly.Simplify( SHAPE_POLY_SET::PM_FAST ); + //m_through_inner_holes_vias_poly.Simplify( SHAPE_POLY_SET::PM_FAST ); // Not in use + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_endCopperLayersTime = GetRunningMicroSecs(); +#endif + + + // Build Tech layers + // Based on: https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059 + // ///////////////////////////////////////////////////////////////////////// +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_startTechLayersTime = GetRunningMicroSecs(); +#endif + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Build Tech layers" ) ); + + // draw graphic items, on technical layers + static const LAYER_ID teckLayerList[] = { + B_Adhes, + F_Adhes, + B_Paste, + F_Paste, + B_SilkS, + F_SilkS, + B_Mask, + F_Mask, + + // Aux Layers + Dwgs_User, + Cmts_User, + Eco1_User, + Eco2_User, + Edge_Cuts, + Margin + }; + + // User layers are not drawn here, only technical layers + for( LSEQ seq = LSET::AllNonCuMask().Seq( teckLayerList, DIM( teckLayerList ) ); + seq; + ++seq ) + { + const LAYER_ID curr_layer_id = *seq; + + if( !Is3DLayerEnabled( curr_layer_id ) ) + continue; + + CBVHCONTAINER2D *layerContainer = new CBVHCONTAINER2D; + m_layers_container2D[curr_layer_id] = layerContainer; + + SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET; + m_layers_poly[curr_layer_id] = layerPoly; + + // Add drawing objects + // ///////////////////////////////////////////////////////////////////// + for( BOARD_ITEM* item = m_board->m_Drawings; item; item = item->Next() ) + { + if( !item->IsOnLayer( curr_layer_id ) ) + continue; + + switch( item->Type() ) + { + case PCB_LINE_T: + AddShapeWithClearanceToContainer( (DRAWSEGMENT*)item, + layerContainer, + curr_layer_id, + 0 ); + break; + + case PCB_TEXT_T: + AddShapeWithClearanceToContainer( (TEXTE_PCB*) item, + layerContainer, + curr_layer_id, + 0 ); + break; + + default: + break; + } + } + + + // Add drawing contours + // ///////////////////////////////////////////////////////////////////// + for( BOARD_ITEM* item = m_board->m_Drawings; item; item = item->Next() ) + { + if( !item->IsOnLayer( curr_layer_id ) ) + continue; + + switch( item->Type() ) + { + case PCB_LINE_T: + { + const unsigned int nr_segments = + GetNrSegmentsCircle( item->GetBoundingBox().GetSizeMax() ); + + ((DRAWSEGMENT*) item)->TransformShapeWithClearanceToPolygon( *layerPoly, + 0, + nr_segments, + 0.0 ); + } + break; + + case PCB_TEXT_T: + ((TEXTE_PCB*) item)->TransformShapeWithClearanceToPolygonSet( *layerPoly, + 0, + segcountInStrokeFont, + 1.0 ); + break; + + default: + break; + } + } + + + // Add modules tech layers - objects + // ///////////////////////////////////////////////////////////////////// + for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) + { + if( (curr_layer_id == F_SilkS) || (curr_layer_id == B_SilkS) ) + { + D_PAD* pad = module->Pads(); + int linewidth = g_DrawDefaultLineThickness; + + for( ; pad; pad = pad->Next() ) + { + if( !pad->IsOnLayer( curr_layer_id ) ) + continue; + + buildPadShapeThickOutlineAsSegments( pad, + layerContainer, + linewidth ); + } + } + else + { + AddPadsShapesWithClearanceToContainer( module, + layerContainer, + curr_layer_id, + 0, + false ); + } + + AddGraphicsShapesWithClearanceToContainer( module, + layerContainer, + curr_layer_id, + 0 ); + } + + + // Add modules tech layers - contours + // ///////////////////////////////////////////////////////////////////// + for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) + { + if( (curr_layer_id == F_SilkS) || (curr_layer_id == B_SilkS) ) + { + D_PAD* pad = module->Pads(); + const int linewidth = g_DrawDefaultLineThickness; + + for( ; pad; pad = pad->Next() ) + { + if( !pad->IsOnLayer( curr_layer_id ) ) + continue; + + buildPadShapeThickOutlineAsPolygon( pad, *layerPoly, linewidth ); + } + } + else + { + transformPadsShapesWithClearanceToPolygon( module->Pads(), + curr_layer_id, + *layerPoly, + 0, + false ); + } + + // On tech layers, use a poor circle approximation, only for texts (stroke font) + module->TransformGraphicTextWithClearanceToPolygonSet( curr_layer_id, + *layerPoly, + 0, + segcountInStrokeFont, + correctionFactorStroke, + segcountInStrokeFont ); + + // Add the remaining things with dynamic seg count for circles + transformGraphicModuleEdgeToPolygonSet( module, curr_layer_id, *layerPoly ); + } + + + // Draw non copper zones + // ///////////////////////////////////////////////////////////////////// + if( GetFlag( FL_ZONE ) ) + { + for( int ii = 0; ii < m_board->GetAreaCount(); ++ii ) + { + ZONE_CONTAINER* zone = m_board->GetArea( ii ); + + if( !zone->IsOnLayer( curr_layer_id ) ) + continue; + + AddSolidAreasShapesToContainer( zone, + layerContainer, + curr_layer_id ); + } + + for( int ii = 0; ii < m_board->GetAreaCount(); ++ii ) + { + ZONE_CONTAINER* zone = m_board->GetArea( ii ); + + if( !zone->IsOnLayer( curr_layer_id ) ) + continue; + + zone->TransformSolidAreasShapesToPolygonSet( *layerPoly, + // Use the same segcount as stroke font + segcountInStrokeFont, + correctionFactorStroke ); + } + } + + // This will make a union of all added contourns + layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST ); + } + // End Build Tech layers + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_endTechLayersTime = GetRunningMicroSecs(); +#endif + + + // Build BVH for holes and vias + // ///////////////////////////////////////////////////////////////////////// + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_startHolesBVHTime = GetRunningMicroSecs(); +#endif + + m_through_holes_inner.BuildBVH(); + m_through_holes_outer.BuildBVH(); + + if( !m_layers_holes2D.empty() ) + { + for( MAP_CONTAINER_2D::iterator ii = m_layers_holes2D.begin(); + ii != m_layers_holes2D.end(); + ++ii ) + { + ((CBVHCONTAINER2D *)(ii->second))->BuildBVH(); + } + } + + // We only need the Solder mask to initialize the BVH + // because..? + if( (CBVHCONTAINER2D *)m_layers_container2D[B_Mask] ) + ((CBVHCONTAINER2D *)m_layers_container2D[B_Mask])->BuildBVH(); + + if( (CBVHCONTAINER2D *)m_layers_container2D[F_Mask] ) + ((CBVHCONTAINER2D *)m_layers_container2D[F_Mask])->BuildBVH(); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_endHolesBVHTime = GetRunningMicroSecs(); + + printf( "CINFO3D_VISU::createLayers times\n" ); + printf( " Copper Layers: %.3f ms\n", + (float)( stats_endCopperLayersTime - stats_startCopperLayersTime ) / 1e3 ); + printf( " Holes BVH creation: %.3f ms\n", + (float)( stats_endHolesBVHTime - stats_startHolesBVHTime ) / 1e3 ); + printf( " Tech Layers: %.3f ms\n", + (float)( stats_endTechLayersTime - stats_startTechLayersTime ) / 1e3 ); + printf( "Statistics:\n" ); + printf( " m_stats_nr_tracks %u\n", m_stats_nr_tracks ); + printf( " m_stats_nr_vias %u\n", m_stats_nr_vias ); + printf( " m_stats_nr_holes %u\n", m_stats_nr_holes ); + printf( " m_stats_via_med_hole_diameter (3DU) %f\n", m_stats_via_med_hole_diameter ); + printf( " m_stats_hole_med_diameter (3DU) %f\n", m_stats_hole_med_diameter ); + printf( " m_calc_seg_min_factor3DU (3DU) %f\n", m_calc_seg_min_factor3DU ); + printf( " m_calc_seg_max_factor3DU (3DU) %f\n", m_calc_seg_max_factor3DU ); +#endif +} diff --git a/3d-viewer/3d_canvas/create_layer_poly.cpp b/3d-viewer/3d_canvas/create_layer_poly.cpp new file mode 100644 index 0000000000..b3016afeb2 --- /dev/null +++ b/3d-viewer/3d_canvas/create_layer_poly.cpp @@ -0,0 +1,226 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file create_layer_poly.cpp + * @brief This file implements the creation of the pcb board items in the poly + * contours format. It is based on the function found in the files: + * board_items_to_polygon_shape_transform.cpp + * board_items_to_polygon_shape_transform.cpp + */ + +#include "cinfo3d_visu.h" +#include +#include +#include + + +// This is the same function as in board_items_to_polygon_shape_transform.cpp +// but it adds the rect/trapezoid shapes with a different winding +void CINFO3D_VISU::buildPadShapePolygon( const D_PAD* aPad, + SHAPE_POLY_SET& aCornerBuffer, + wxSize aInflateValue, + int aSegmentsPerCircle, + double aCorrectionFactor ) const +{ + wxPoint corners[4]; + wxPoint PadShapePos = aPad->ShapePos(); /* Note: for pad having a shape offset, + * the pad position is NOT the shape position */ + switch( aPad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + case PAD_SHAPE_OVAL: + case PAD_SHAPE_ROUNDRECT: + aPad->TransformShapeWithClearanceToPolygon( aCornerBuffer, aInflateValue.x, + aSegmentsPerCircle, aCorrectionFactor ); + break; + + case PAD_SHAPE_TRAPEZOID: + case PAD_SHAPE_RECT: + { + SHAPE_LINE_CHAIN aLineChain; + + aPad->BuildPadPolygon( corners, aInflateValue, aPad->GetOrientation() ); + + for( int ii = 0; ii < 4; ++ii ) + { + corners[3-ii] += PadShapePos; // Shift origin to position + aLineChain.Append( corners[3-ii].x, corners[3-ii].y ); + } + + aLineChain.SetClosed( true ); + + aCornerBuffer.AddOutline( aLineChain ); + } + break; + + default: + wxFAIL_MSG( wxT( "CINFO3D_VISU::buildPadShapePolygon: found a not implemented pad shape (new shape?)" ) ); + break; + } +} + + +void CINFO3D_VISU::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad, + SHAPE_POLY_SET& aCornerBuffer, + int aWidth ) const +{ + if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring + { + unsigned int nr_sides_per_circle = GetNrSegmentsCircle( ( aPad->GetSize().x / 2 + + aWidth / 2 ) * 2 ); + + TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), + aPad->GetSize().x / 2, nr_sides_per_circle, aWidth ); + return; + } + + // For other shapes, draw polygon outlines + SHAPE_POLY_SET corners; + + unsigned int nr_sides_per_circle = GetNrSegmentsCircle( glm::min( aPad->GetSize().x, + aPad->GetSize().y) ); + + buildPadShapePolygon( aPad, corners, wxSize( 0, 0 ), + nr_sides_per_circle, GetCircleCorrectionFactor( nr_sides_per_circle ) ); + + // Add outlines as thick segments in polygon buffer + + const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); + + for( int ii = 0; ii < path.PointCount(); ++ii ) + { + const VECTOR2I& a = path.CPoint( ii ); + const VECTOR2I& b = path.CPoint( ii + 1 ); + + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, + wxPoint( a.x, a.y ), + wxPoint( b.x, b.y ), + nr_sides_per_circle, + aWidth ); + } +} + + +// Based on the same function name in board_items_to_polyshape_transform.cpp +// It was implemented here to allow dynamic segments count per pad shape +void CINFO3D_VISU::transformPadsShapesWithClearanceToPolygon( const DLIST& aPads, LAYER_ID aLayer, + SHAPE_POLY_SET& aCornerBuffer, + int aInflateValue, + bool aSkipNPTHPadsWihNoCopper ) const +{ + const D_PAD* pad = aPads; + + wxSize margin; + for( ; pad != NULL; pad = pad->Next() ) + { + if( !pad->IsOnLayer(aLayer) ) + continue; + + // NPTH pads are not drawn on layers if the shape size and pos is the same + // as their hole: + if( aSkipNPTHPadsWihNoCopper && (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED) ) + { + if( (pad->GetDrillSize() == pad->GetSize()) && + (pad->GetOffset() == wxPoint( 0, 0 )) ) + { + switch( pad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) + continue; + break; + + case PAD_SHAPE_OVAL: + if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE ) + continue; + break; + + default: + break; + } + } + } + + switch( aLayer ) + { + case F_Mask: + case B_Mask: + margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue; + break; + + case F_Paste: + case B_Paste: + margin = pad->GetSolderPasteMargin(); + margin.x += aInflateValue; + margin.y += aInflateValue; + break; + + default: + margin.x = margin.y = aInflateValue; + break; + } + + unsigned int aCircleToSegmentsCount = GetNrSegmentsCircle( pad->GetSize().x ); + double aCorrectionFactor = GetCircleCorrectionFactor( aCircleToSegmentsCount ); + + buildPadShapePolygon( pad, aCornerBuffer, margin, + aCircleToSegmentsCount, aCorrectionFactor ); + } +} + +void CINFO3D_VISU::transformGraphicModuleEdgeToPolygonSet( const MODULE *aModule, + LAYER_ID aLayer, + SHAPE_POLY_SET& aCornerBuffer ) const +{ + for( const EDA_ITEM* item = aModule->GraphicalItems(); + item != NULL; + item = item->Next() ) + { + switch( item->Type() ) + { + case PCB_MODULE_EDGE_T: + { + EDGE_MODULE*outline = (EDGE_MODULE*) item; + + if( outline->GetLayer() != aLayer ) + break; + + unsigned int aCircleToSegmentsCount = + GetNrSegmentsCircle( outline->GetBoundingBox().GetSizeMax() ); + + double aCorrectionFactor = GetCircleCorrectionFactor( aCircleToSegmentsCount ); + + outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, + 0, + aCircleToSegmentsCount, + aCorrectionFactor ); + } + break; + + default: + break; + } + } +} diff --git a/3d-viewer/3d_canvas/eda_3d_canvas.cpp b/3d-viewer/3d_canvas/eda_3d_canvas.cpp new file mode 100644 index 0000000000..c88823aaef --- /dev/null +++ b/3d-viewer/3d_canvas/eda_3d_canvas.cpp @@ -0,0 +1,1046 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file eda_3d_canvas.cpp + * @brief Implementation of a 3d canvas + */ + +#include // Must be included first + +#include "../common_ogl/openGL_includes.h" +#include "../common_ogl/ogl_utils.h" +#include "eda_3d_canvas.h" +#include "../3d_viewer_id.h" +#include "../3d_rendering/3d_render_raytracing/c3d_render_raytracing.h" +#include "../3d_viewer/eda_3d_viewer.h" +#include "../3d_rendering/test_cases.h" +#include +#include "status_text_reporter.h" +#include + +/** + * Trace mask used to enable or disable the trace output of this class. + * The debug output can be turned on by setting the WXTRACE environment variable to + * "KI_TRACE_EDA_3D_CANVAS". See the wxWidgets documentation on wxLogTrace for + * more information. + */ +const wxChar * EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" ); + +const float EDA_3D_CANVAS::m_delta_move_step_factor = 0.7f; + + +BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas ) + EVT_PAINT( EDA_3D_CANVAS::OnPaint ) + EVT_CHAR( EDA_3D_CANVAS::OnKeyEvent ) + EVT_CHAR_HOOK( EDA_3D_CANVAS::OnCharHook ) + + // mouse events + EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown ) + EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp ) + EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp ) + EVT_MIDDLE_DOWN( EDA_3D_CANVAS::OnMiddleDown) + EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightClick ) + EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel ) + EVT_MOTION( EDA_3D_CANVAS::OnMouseMove ) + +#ifdef USE_OSX_MAGNIFY_EVENT + EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify ) +#endif + + // other events + EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground ) + EVT_MENU_RANGE( ID_POPUP_3D_VIEW_START, + ID_POPUP_3D_VIEW_END, EDA_3D_CANVAS::OnPopUpMenu ) + + EVT_CLOSE( EDA_3D_CANVAS::OnCloseWindow ) +END_EVENT_TABLE() + + +EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow *aParent, + const int *aAttribList, + BOARD *aBoard, + CINFO3D_VISU &aSettings , S3D_CACHE *a3DCachePointer ) : + + wxGLCanvas( aParent, + wxID_ANY, + aAttribList, + wxDefaultPosition, + wxDefaultSize, + wxFULL_REPAINT_ON_RESIZE + ), + m_settings( aSettings ) +{ + // Run test cases in debug mode, once. + //DBG( Run_3d_viewer_test_cases() ); + + wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) ); + + m_editing_timeout_timer.SetOwner( this ); + Connect( m_editing_timeout_timer.GetId(), + wxEVT_TIMER, + wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ), + NULL, + this ); + + m_redraw_trigger_timer.SetOwner( this ); + Connect( m_redraw_trigger_timer.GetId(), + wxEVT_TIMER, + wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ), + NULL, + this ); + + m_mouse_was_moved = false; + m_mouse_is_moving = false; + m_camera_is_moving = false; + m_render_pivot = false; + m_camera_moving_speed = 1.0f; + + m_strtime_camera_movement = 0; + + m_is_opengl_initialized = false; + + m_render_raytracing_was_requested = false; + + m_parentStatusBar = NULL; + m_glRC = NULL; + + m_3d_render = NULL; + + m_3d_render_raytracing = new C3D_RENDER_RAYTRACING( aSettings ); + m_3d_render_ogl_legacy = new C3D_RENDER_OGL_LEGACY( aSettings ); + + wxASSERT( m_3d_render_raytracing != NULL ); + wxASSERT( m_3d_render_ogl_legacy != NULL ); + + RenderEngineChanged(); + + wxASSERT( aBoard != NULL ); + m_settings.SetBoard( aBoard ); + + wxASSERT( a3DCachePointer != NULL ); + m_settings.Set3DCacheManager( a3DCachePointer ); +} + + +EDA_3D_CANVAS::~EDA_3D_CANVAS() +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::~EDA_3D_CANVAS" ) ); + + releaseOpenGL(); +} + + +void EDA_3D_CANVAS::releaseOpenGL() +{ + if( m_glRC ) + { + GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this ); + + delete m_3d_render_raytracing; + m_3d_render_raytracing = NULL; + + delete m_3d_render_ogl_legacy; + m_3d_render_ogl_legacy = NULL; + + // This is just a copy of a pointer, can safelly be set to NULL + m_3d_render = NULL; + + GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); + GL_CONTEXT_MANAGER::Get().DestroyCtx( m_glRC ); + m_glRC = NULL; + } +} + + +void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent &event ) +{ + releaseOpenGL(); + + event.Skip(); +} + + +bool EDA_3D_CANVAS::initializeOpenGL() +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL" ) ); + + const GLenum err = glewInit(); + + if( GLEW_OK != err ) + { + const wxString msgError = (const char*) glewGetErrorString( err ); + + wxLogMessage( "%s", msgError.ToUTF8() ); + + return false; + } + else + { + wxLogTrace( m_logTrace, + wxString( wxT( "EDA_3D_CANVAS::initializeOpenGL Using GLEW " ) ) + + FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) ); + } + + m_is_opengl_initialized = true; + + return true; +} + + +void EDA_3D_CANVAS::GetScreenshot( wxImage &aDstImage ) +{ + OGL_GetScreenshot( aDstImage ); +} + + +void EDA_3D_CANVAS::ReloadRequest( BOARD *aBoard , S3D_CACHE *aCachePointer ) +{ + if( aCachePointer != NULL ) + m_settings.Set3DCacheManager( aCachePointer ); + + if( aBoard != NULL ) + m_settings.SetBoard( aBoard ); + + if( m_3d_render ) + m_3d_render->ReloadRequest(); +} + + +void EDA_3D_CANVAS::RenderRaytracingRequest() +{ + m_3d_render = m_3d_render_raytracing; + + if( m_3d_render ) + m_3d_render->ReloadRequest(); + + m_render_raytracing_was_requested = true; + //m_mouse_was_moved = true; + + Request_refresh(); +} + + +void EDA_3D_CANVAS::DisplayStatus() +{ + if( m_parentStatusBar ) + { + wxString msg; + + msg.Printf( wxT( "dx %3.2f" ), m_settings.CameraGet().GetCameraPos().x ); + m_parentStatusBar->SetStatusText( msg, 1 ); + + msg.Printf( wxT( "dy %3.2f" ), m_settings.CameraGet().GetCameraPos().y ); + m_parentStatusBar->SetStatusText( msg, 2 ); + + //msg.Printf( _( "Zoom: %3.1f" ), 50 * m_settings.CameraGet().ZoomGet() ); + //m_parentStatusBar->SetStatusText( msg, 3 ); + } +} + + +void EDA_3D_CANVAS::OnPaint( wxPaintEvent &event ) +{ + // Please have a look at: + // https://lists.launchpad.net/kicad-developers/msg25149.html + // wxPaintDC( this ); + // event.Skip( false ); + + // SwapBuffer requires the window to be shown before calling + if( !IsShownOnScreen() ) + { + wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnPaint !IsShown" ) ); + return; + } + + // Because the board to draw is handled by the parent viewer frame, + // ensure this parent is still alive. When it is closed before the viewer + // frame, a paint event can be generated after the parent is closed, + // therefore with invalid board. + // This is dependant of the platform. + // Especially on OSX, but also on Windows, it frequently happens + if( !GetParent()->GetParent()->IsShown() ) + return; // The parent board editor frame is no more alive + + wxString err_messages; + + // !TODO: implement error reporter + //WX_STRING_REPORTER errorReporter( &err_messages ); + STATUS_TEXT_REPORTER activityReporter( m_parentStatusBar, 0 ); + + + unsigned strtime = GetRunningMicroSecs(); + + // "Makes the OpenGL state that is represented by the OpenGL rendering + // context context current, i.e. it will be used by all subsequent OpenGL calls. + // This function may only be called when the window is shown on screen" + + // Explicitly create a new rendering context instance for this canvas. + if( m_glRC == NULL ) + m_glRC = GL_CONTEXT_MANAGER::Get().CreateCtx( this ); + + GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this ); + + // Set the OpenGL viewport according to the client size of this canvas. + // This is done here rather than in a wxSizeEvent handler because our + // OpenGL rendering context (and thus viewport setting) is used with + // multiple canvases: If we updated the viewport in the wxSizeEvent + // handler, changing the size of one canvas causes a viewport setting that + // is wrong when next another canvas is repainted. + wxSize clientSize = GetClientSize(); + + const bool windows_size_changed = m_settings.CameraGet().SetCurWindowSize( clientSize ); + + + // Initialize openGL if need + // ///////////////////////////////////////////////////////////////////////// + if( !m_is_opengl_initialized ) + { + if( !initializeOpenGL() ) + { + GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); + + return; + } + } + + + // Check if a raytacing was requented and need to switch to raytracing mode + if( m_settings.RenderEngineGet() == RENDER_ENGINE_OPENGL_LEGACY ) + { + const bool was_camera_changed = m_settings.CameraGet().ParametersChanged(); + + // It reverts back to OpenGL mode if it was requested a raytracing + // render of the current scene. AND the mouse / camera is moving + if( ( m_mouse_is_moving || + m_camera_is_moving || + was_camera_changed || + windows_size_changed ) && + m_render_raytracing_was_requested ) + { + m_render_raytracing_was_requested = false; + m_3d_render = m_3d_render_ogl_legacy; + } + } + + + float curtime_delta_s = 0.0f; + + if( m_camera_is_moving ) + { + const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement; + curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed; + m_settings.CameraGet().Interpolate( curtime_delta_s ); + + if( curtime_delta_s > 1.0f ) + { + m_render_pivot = false; + m_camera_is_moving = false; + m_mouse_was_moved = true; + + restart_editingTimeOut_Timer(); + DisplayStatus(); + } + else + { + Request_refresh(); + } + } + + + // It will return true if the render request a new redraw + bool requested_redraw = false; + + if( m_3d_render ) + { + m_3d_render->SetCurWindowSize( clientSize ); + + requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving, + &activityReporter ); + } + + if( m_render_pivot ) + { + const float scale = glm::min( m_settings.CameraGet().ZoomGet(), 1.0f ); + render_pivot( curtime_delta_s, scale * scale ); + } + + // "Swaps the double-buffer of this window, making the back-buffer the + // front-buffer and vice versa, so that the output of the previous OpenGL + // commands is displayed on the window." + SwapBuffers(); + + GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); + + if( !activityReporter.HasMessage() ) + { + if( m_mouse_was_moved || m_camera_is_moving ) + { + // Calculation time in miliseconds + const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3; + + activityReporter.Report( wxString::Format( _( "Render time %.0f ms ( %.1f fps)" ), + calculation_time, 1000.0 / calculation_time ) ); + } + } + + // This will reset the flag of camera parameters changed + m_settings.CameraGet().ParametersChanged(); + + if( !err_messages.IsEmpty() ) + wxLogMessage( err_messages ); + + if( (!m_camera_is_moving) && requested_redraw ) + { + m_mouse_was_moved = false; + Request_refresh( false ); + } +} + + +void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent &event ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnEraseBackground" ) ); + // Do nothing, to avoid flashing. +} + + +void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent &event ) +{ + bool mouseActivity = false; + + wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseWheel" ) ); + + if( m_camera_is_moving ) + return; + + float delta_move = m_delta_move_step_factor * m_settings.CameraGet().ZoomGet(); + + if( m_settings.GetFlag( FL_MOUSEWHEEL_PANNING ) ) + delta_move *= (0.05f * event.GetWheelRotation()); + else + if( event.GetWheelRotation() < 0 ) + delta_move = -delta_move; + + if( m_settings.GetFlag( FL_MOUSEWHEEL_PANNING ) ) + { + if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL ) + m_settings.CameraGet().Pan( SFVEC3F( -delta_move, 0.0f, 0.0f ) ); + else + m_settings.CameraGet().Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) ); + + mouseActivity = true; + } + else if( event.ShiftDown() ) + { + m_settings.CameraGet().Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) ); + mouseActivity = true; + } + else if( event.ControlDown() ) + { + m_settings.CameraGet().Pan( SFVEC3F( -delta_move, 0.0f, 0.0f ) ); + mouseActivity = true; + } + else + { + if( event.GetWheelRotation() > 0 ) + { + mouseActivity = m_settings.CameraGet().ZoomIn( 1.1f ); + } + else + { + mouseActivity = m_settings.CameraGet().ZoomOut( 1.1f ); + } + } + + // If it results on a camera movement + if( mouseActivity ) + { + DisplayStatus(); + Request_refresh(); + + m_mouse_is_moving = true; + m_mouse_was_moved = true; + + restart_editingTimeOut_Timer(); + } + + // Update the cursor current mouse position on the camera + m_settings.CameraGet().SetCurMousePosition( event.GetPosition() ); +} + + +#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT ) +void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event ) +{ + if( m_camera_is_moving ) + return; + + //m_is_moving_mouse = true; + restart_editingTimeOut_Timer(); + + float magnification = ( event.GetMagnification() + 1.0f ); + + m_settings.CameraGet().ZoomIn( magnification ); + + DisplayStatus(); + Request_refresh(); + + // Please someone test if this is need + //m_settings.CameraGet().SetCurMousePosition( event.GetPosition() ); +} +#endif + + +void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent &event ) +{ + //wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseMove" ) ); + + if( m_camera_is_moving ) + return; + + m_settings.CameraGet().SetCurWindowSize( GetClientSize() ); + + if( event.Dragging() ) + { + if( event.LeftIsDown() ) // Drag + m_settings.CameraGet().Drag( event.GetPosition() ); + else if( event.MiddleIsDown() ) // Pan + m_settings.CameraGet().Pan( event.GetPosition() ); + + m_mouse_is_moving = true; + m_mouse_was_moved = true; + + // orientation has changed, redraw mesh + DisplayStatus(); + Request_refresh(); + } + + const wxPoint eventPosition = event.GetPosition(); + m_settings.CameraGet().SetCurMousePosition( eventPosition ); +} + + +void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent &event ) +{ + stop_editingTimeOut_Timer(); +} + + +void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent &event ) +{ + if( m_camera_is_moving ) + return; + + if( m_mouse_is_moving ) + { + m_mouse_is_moving = false; + restart_editingTimeOut_Timer(); + } +} + + +void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent &event ) +{ + stop_editingTimeOut_Timer(); +} + + +void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent &event ) +{ + if( m_camera_is_moving ) + return; + + if( m_mouse_is_moving ) + { + m_mouse_is_moving = false; + restart_editingTimeOut_Timer(); + } + else + { + move_pivot_based_on_cur_mouse_position(); + } +} + + +void EDA_3D_CANVAS::OnRightClick( wxMouseEvent &event ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnRightClick" ) ); + + if( m_camera_is_moving ) + return; + + wxPoint pos; + wxMenu PopUpMenu; + + pos.x = event.GetX(); + pos.y = event.GetY(); + + wxMenuItem* item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMIN, _( "Zoom +" ) ); + item->SetBitmap( KiBitmap( zoom_in_xpm )); + PopUpMenu.Append( item ); + + item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMOUT, _( "Zoom -" ) ); + item->SetBitmap( KiBitmap( zoom_out_xpm )); + PopUpMenu.Append( item ); + + PopUpMenu.AppendSeparator(); + item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZPOS, _( "Top View" ) ); + item->SetBitmap( KiBitmap( axis3d_top_xpm )); + PopUpMenu.Append( item ); + + item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZNEG, _( "Bottom View" ) ); + item->SetBitmap( KiBitmap( axis3d_bottom_xpm )); + PopUpMenu.Append( item ); + + PopUpMenu.AppendSeparator(); + item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XPOS, _( "Right View" ) ); + item->SetBitmap( KiBitmap( axis3d_right_xpm )); + PopUpMenu.Append( item ); + + item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XNEG, _( "Left View" ) ); + item->SetBitmap( KiBitmap( axis3d_left_xpm )); + PopUpMenu.Append( item ); + + PopUpMenu.AppendSeparator(); + item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YPOS, _( "Front View" ) ); + item->SetBitmap( KiBitmap( axis3d_front_xpm )); + PopUpMenu.Append( item ); + + item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YNEG, _( "Back View" ) ); + item->SetBitmap( KiBitmap( axis3d_back_xpm )); + PopUpMenu.Append( item ); + + PopUpMenu.AppendSeparator(); + item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_LEFT, _( "Move left <-" ) ); + item->SetBitmap( KiBitmap( left_xpm )); + PopUpMenu.Append( item ); + + item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_RIGHT, _( "Move right ->" ) ); + item->SetBitmap( KiBitmap( right_xpm )); + PopUpMenu.Append( item ); + + item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_UP, _( "Move Up ^" ) ); + item->SetBitmap( KiBitmap( up_xpm )); + PopUpMenu.Append( item ); + + item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_DOWN, _( "Move Down" ) ); + item->SetBitmap( KiBitmap( down_xpm )); + PopUpMenu.Append( item ); + + PopupMenu( &PopUpMenu, pos ); +} + + +void EDA_3D_CANVAS::OnPopUpMenu( wxCommandEvent &event ) +{ + int id = event.GetId(); + + wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnPopUpMenu id:%d" ), id ); + + int key = 0; + + switch( id ) + { + case ID_POPUP_ZOOMIN: + key = WXK_F1; + break; + + case ID_POPUP_ZOOMOUT: + key = WXK_F2; + break; + + case ID_POPUP_VIEW_XPOS: + key = 'x'; + break; + + case ID_POPUP_VIEW_XNEG: + key = 'X'; + break; + + case ID_POPUP_VIEW_YPOS: + key = 'y'; + break; + + case ID_POPUP_VIEW_YNEG: + key = 'Y'; + break; + + case ID_POPUP_VIEW_ZPOS: + key = 'z'; + break; + + case ID_POPUP_VIEW_ZNEG: + key = 'Z'; + break; + + case ID_POPUP_MOVE3D_LEFT: + key = WXK_LEFT; + break; + + case ID_POPUP_MOVE3D_RIGHT: + key = WXK_RIGHT; + break; + + case ID_POPUP_MOVE3D_UP: + key = WXK_UP; + break; + + case ID_POPUP_MOVE3D_DOWN: + key = WXK_DOWN; + break; + + default: + return; + } + + SetView3D( key ); +} + + +void EDA_3D_CANVAS::OnCharHook( wxKeyEvent &event ) +{ + //wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnCharHook" ) ); + event.Skip(); +} + + +void EDA_3D_CANVAS::OnKeyEvent( wxKeyEvent& event ) +{ + //wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnKeyEvent" ) ); + + if( m_camera_is_moving ) + return; + + SetView3D( event.GetKeyCode() ); + event.Skip(); +} + + +void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent &event ) +{ + (void)event; + + m_mouse_is_moving = false; + m_mouse_was_moved = false; + + Request_refresh(); +} + + +void EDA_3D_CANVAS::stop_editingTimeOut_Timer() +{ + m_editing_timeout_timer.Stop(); +} + + +void EDA_3D_CANVAS::restart_editingTimeOut_Timer() +{ + m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut() , wxTIMER_ONE_SHOT ); +} + + +void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent &event ) +{ + (void)event; + + //Refresh(); + //Update(); + + wxPaintEvent redrawEvent; + wxPostEvent( this, redrawEvent ); +} + + +void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately ) +{ + if( aRedrawImmediately ) + { + // On some systems, just calling Refresh does not work always + // (Issue experienced on Win7 MSYS2) + //Refresh(); + //Update(); + + // Using PostEvent will take priority to other events, like + // mouse movements, keys, etc. + wxPaintEvent redrawEvent; + wxPostEvent( this, redrawEvent ); + + // This behaves the same + // wxQueueEvent( this, + // From wxWidget documentation: "The heap-allocated and + // non-NULL event to queue, the function takes ownership of it." + // new wxPaintEvent() + // ); + } + else + { + // Schedule a timed redraw + m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT ); + } +} + + +void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot ) +{ + wxASSERT( aMovingSpeed > FLT_EPSILON ); + + m_render_pivot = aRenderPivot; + m_camera_moving_speed = aMovingSpeed; + + stop_editingTimeOut_Timer(); + + DisplayStatus(); + Request_refresh(); + + m_camera_is_moving = true; + + m_strtime_camera_movement = GetRunningMicroSecs(); +} + + +void EDA_3D_CANVAS::move_pivot_based_on_cur_mouse_position() +{ + SFVEC3F rayOrigin; + SFVEC3F rayDir; + + // Generate a ray origin and direction based on current mouser position and camera + m_settings.CameraGet().MakeRayAtCurrrentMousePosition( rayOrigin, rayDir ); + + RAY mouseRay; + mouseRay.Init( rayOrigin, rayDir ); + + float hit_t; + + // Test it with the board bounding box + if( m_settings.GetBBox3DU().Intersect( mouseRay, &hit_t ) ) + { + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().SetLookAtPos_T1( mouseRay.at( hit_t ) ); + m_settings.CameraGet().ResetXYpos_T1(); + + request_start_moving_camera(); + } +} + + +void EDA_3D_CANVAS::SetView3D( int keycode ) +{ + if( m_camera_is_moving ) + return; + + const float delta_move = m_delta_move_step_factor * m_settings.CameraGet().ZoomGet(); + const float arrow_moving_time_speed = 8.0f; + + switch( keycode ) + { + case WXK_SPACE: + move_pivot_based_on_cur_mouse_position(); + return; + + case WXK_LEFT: + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_LINEAR ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) ); + request_start_moving_camera( arrow_moving_time_speed, false ); + return; + + case WXK_RIGHT: + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_LINEAR ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) ); + request_start_moving_camera( arrow_moving_time_speed, false ); + return; + + case WXK_UP: + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_LINEAR ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) ); + request_start_moving_camera( arrow_moving_time_speed, false ); + return; + + case WXK_DOWN: + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_LINEAR ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) ); + request_start_moving_camera( arrow_moving_time_speed, false ); + return; + + case WXK_HOME: + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Reset_T1(); + request_start_moving_camera( glm::min( glm::max( m_settings.CameraGet().ZoomGet(), 0.5f ), 1.125f ) ); + return; + + case WXK_END: + break; + + case WXK_TAB: + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_EASING_IN_OUT ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().RotateZ_T1( glm::radians( 45.0f ) ); + request_start_moving_camera(); + break; + + case WXK_F1: + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + if( m_settings.CameraGet().ZoomIn_T1( 1.4f ) ) + request_start_moving_camera( 3.0f ); + return; + + case WXK_F2: + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + if( m_settings.CameraGet().ZoomOut_T1( 1.4f ) ) + request_start_moving_camera( 3.0f ); + return; + + case '+': + break; + + case '-': + break; + + case 't': + case 'T': + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_NORMAL, + !m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL ) ); + ReloadRequest(); + break; + + case 's': + case 'S': + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT, + !m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT ) ); + ReloadRequest(); + break; + + case 'v': + case 'V': + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL, + !m_settings.GetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL ) ); + ReloadRequest(); + break; + + case 'r': + case 'R': + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Reset_T1(); + request_start_moving_camera( glm::min( glm::max( m_settings.CameraGet().ZoomGet(), 0.5f ), 1.125f ) ); + return; + + case 'x': + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Reset_T1(); + m_settings.CameraGet().RotateZ_T1( glm::radians( -90.0f ) ); + m_settings.CameraGet().RotateX_T1( glm::radians( -90.0f ) ); + request_start_moving_camera(); + return; + + case 'X': + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Reset_T1(); + m_settings.CameraGet().RotateZ_T1( glm::radians( 90.0f ) ); + m_settings.CameraGet().RotateX_T1( glm::radians( -90.0f ) ); + request_start_moving_camera(); + return; + + case 'y': + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Reset_T1(); + m_settings.CameraGet().RotateX_T1( glm::radians( -90.0f ) ); + request_start_moving_camera(); + return; + + case 'Y': + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Reset_T1(); + m_settings.CameraGet().RotateX_T1( glm::radians( -90.0f ) ); + m_settings.CameraGet().RotateZ_T1( glm::radians( -180.0f ) ); + request_start_moving_camera(); + return; + + case 'z': + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Reset_T1(); + request_start_moving_camera( + glm::min( glm::max( m_settings.CameraGet().ZoomGet(), 0.5f ), 1.125f ) ); + return; + + case 'Z': + m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER ); + m_settings.CameraGet().SetT0_and_T1_current_T(); + m_settings.CameraGet().Reset_T1(); + m_settings.CameraGet().RotateX_T1( glm::radians( -180.0f ) ); + request_start_moving_camera( + glm::min( glm::max( m_settings.CameraGet().ZoomGet(), 0.5f ), 1.125f ) ); + return; + + default: + return; + } + + m_mouse_was_moved = true; + + restart_editingTimeOut_Timer(); + + DisplayStatus(); + Request_refresh(); +} + + +void EDA_3D_CANVAS::RenderEngineChanged() +{ + + switch( m_settings.RenderEngineGet() ) + { + case RENDER_ENGINE_OPENGL_LEGACY: + m_3d_render = m_3d_render_ogl_legacy; + break; + + case RENDER_ENGINE_RAYTRACING: + m_3d_render = m_3d_render_raytracing; + break; + + default: + m_3d_render = NULL; + break; + } + + if( m_3d_render ) + m_3d_render->ReloadRequest(); + + m_mouse_was_moved = false; + + Request_refresh(); +} diff --git a/3d-viewer/3d_canvas/eda_3d_canvas.h b/3d-viewer/3d_canvas/eda_3d_canvas.h new file mode 100644 index 0000000000..6b20a2b292 --- /dev/null +++ b/3d-viewer/3d_canvas/eda_3d_canvas.h @@ -0,0 +1,279 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file eda_3d_canvas.h + * @brief + */ + +#ifndef EDA_3D_CANVAS_H +#define EDA_3D_CANVAS_H + + +#include "cinfo3d_visu.h" +#include "3d_rendering/c3d_render_base.h" +#include "3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.h" +#include "3d_rendering/3d_render_raytracing/c3d_render_raytracing.h" +#include "3d_cache/3d_cache.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Class EDA_3D_CANVAS + * Implement a canvas based on a wxGLCanvas + */ +class EDA_3D_CANVAS : public wxGLCanvas +{ + + public: + + + /** + * @brief EDA_3D_CANVAS - Creates a new 3D Canvas with a attribute list + * @param aParent: the parent creator of this canvas + * @param aAttribList: a list of openGL options created by GetOpenGL_AttributesList + * @param aBoard: The board + * @param aSettings: the settings options to be used by this canvas + */ + EDA_3D_CANVAS( wxWindow *aParent, + const int *aAttribList = 0, + BOARD *aBoard = NULL, + CINFO3D_VISU &aSettings = G_null_CINFO3D_VISU, + S3D_CACHE *a3DCachePointer = NULL ); + + ~EDA_3D_CANVAS(); + + void SetStatusBar( wxStatusBar *aStatusBar ) { m_parentStatusBar = aStatusBar; } + + void ReloadRequest( BOARD *aBoard = NULL, S3D_CACHE *aCachePointer = NULL ); + + /** + * @brief IsReloadRequestPending - Query if there is a pending reload request + * @return true if it wants to reload, false if there is no reload pending + */ + bool IsReloadRequestPending() const + { + if( m_3d_render ) + return m_3d_render->IsReloadRequestPending(); + else + return false; + } + + /** + * @brief RenderRaytracingRequest - Request to render the current view in Raytracing mode + */ + void RenderRaytracingRequest(); + + /** + * Request a screenshot and output it to the aDstImage + * @param aDstImage - Screenshot destination image + */ + void GetScreenshot( wxImage &aDstImage ); + + /** + * @brief SetView3D - Helper function to call view commands + * @param keycode: ascii key commands + */ + void SetView3D( int keycode ); + + /** + * @brief RenderEngineChanged - Notify that the render engine was changed + */ + void RenderEngineChanged(); + + /** + * @brief DisplayStatus - Update the status bar with the position information + */ + void DisplayStatus(); + + /** + * @brief Request_refresh - Schedule a refresh update of the canvas + * @param aRedrawImmediately - true will request a redraw, false will + * schedule a redraw, after a short timeout. + */ + void Request_refresh( bool aRedrawImmediately = true ); + + private: + + void OnPaint( wxPaintEvent &event ); + + void OnEraseBackground( wxEraseEvent &event ); + + void OnMouseWheel( wxMouseEvent &event ); + +#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT ) + void OnMagnify( wxMouseEvent& event ); +#endif + + void OnMouseMove( wxMouseEvent &event ); + + void OnLeftDown( wxMouseEvent &event ); + + void OnLeftUp( wxMouseEvent &event ); + + void OnMiddleUp( wxMouseEvent &event ); + + void OnMiddleDown( wxMouseEvent &event ); + + void OnRightClick( wxMouseEvent &event ); + + void OnPopUpMenu( wxCommandEvent &event ); + + void OnCharHook( wxKeyEvent& event ); + + void OnKeyEvent( wxKeyEvent& event ); + + void OnTimerTimeout_Editing( wxTimerEvent& event ); + + /** + * @brief OnCloseWindow - called when the frame is closed + * @param event + */ + void OnCloseWindow( wxCloseEvent &event ); + + void OnTimerTimeout_Redraw( wxTimerEvent& event ); + + DECLARE_EVENT_TABLE(); + + private: + + /** + * @brief stop_editingTimeOut_Timer - stop the editing time, so it will not timeout + */ + void stop_editingTimeOut_Timer(); + + + /** + * @brief restart_editingTimeOut_Timer - reset the editing timer + */ + void restart_editingTimeOut_Timer(); + + + /** + * @brief request_start_moving_camera - start a camera movement + * @param aMovingSpeed: the time speed + * @param aRenderPivot: if it should display pivot cursor while move + */ + void request_start_moving_camera( float aMovingSpeed = 2.0f, + bool aRenderPivot = true ); + + + /** + * @brief move_pivot_based_on_cur_mouse_position - + * This function hits a ray to the board and start a moviment + */ + void move_pivot_based_on_cur_mouse_position(); + + + /** + * @brief render_pivot - render the pivot cursor + * @param t: time between 0.0 and 1.0 + * @param aScale: scale to apply on the cursor + */ + void render_pivot( float t, float aScale ); + + /** + * @brief initializeOpenGL + * @return if OpenGL initialization succeed + */ + bool initializeOpenGL(); + + /** + * @brief releaseOpenGL - free created targets and openGL context + */ + void releaseOpenGL(); + + private: + + /// current OpenGL context + wxGLContext *m_glRC; + + /// Parent statusbar to report progress + wxStatusBar *m_parentStatusBar; + + /// Time timeout will expires after some time sinalizing that the mouse / + /// keyboard movements are over. + wxTimer m_editing_timeout_timer; + + /// This timer will be used to schedule a redraw event + wxTimer m_redraw_trigger_timer; + + /// true if mouse activity is on progress + bool m_mouse_is_moving; + + /// true if there was some type of activity, it will be used to render in + /// preview mode + bool m_mouse_was_moved; + + /// true if camera animation is ongoing + bool m_camera_is_moving; + + /// activated the render of pivot while camera moving + bool m_render_pivot; + + /// 1.0f will be 1:1 + float m_camera_moving_speed; + + /// Stores the ticktime when the camera star its movement + unsigned m_strtime_camera_movement; + + /// Stores all pre-computed 3D information and visualization settings to render the board + CINFO3D_VISU &m_settings; + + /// The current render in used for this canvas + C3D_RENDER_BASE *m_3d_render; + + /// Raytracing render class + C3D_RENDER_RAYTRACING *m_3d_render_raytracing; + + /// OpenGL legacy render class + C3D_RENDER_OGL_LEGACY *m_3d_render_ogl_legacy; + + /// Flag to store if opengl was initialized already + bool m_is_opengl_initialized; + + /// Step factor to used with cursor on relation to the current zoom + static const float m_delta_move_step_factor; + + /// Flags that the user requested the current view to be render with raytracing + bool m_render_raytracing_was_requested; + + /** + * Trace mask used to enable or disable the trace output of this class. + * The debug output can be turned on by setting the WXTRACE environment variable to + * "KI_TRACE_EDA_3D_CANVAS". See the wxWidgets documentation on wxLogTrace for + * more information. + */ + static const wxChar *m_logTrace; +}; + + +#endif // EDA_3D_CANVAS_H diff --git a/3d-viewer/3d_canvas/eda_3d_canvas_pivot.cpp b/3d-viewer/3d_canvas/eda_3d_canvas_pivot.cpp new file mode 100644 index 0000000000..8b8452c1d2 --- /dev/null +++ b/3d-viewer/3d_canvas/eda_3d_canvas_pivot.cpp @@ -0,0 +1,122 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file eda_3d_canvas_pivot.cpp + * @brief Implementation of a 3d cursor + */ + +#include "../common_ogl/openGL_includes.h" +#include "../common_ogl/ogl_utils.h" +#include "eda_3d_canvas.h" + + +static void pivot_render_triangles( float t ) +{ + wxASSERT( t >= 0.0f ); + + SFVEC3F vertexPointer[12]; + + const float u = 1.0f / 6.0f; + + vertexPointer[0] = SFVEC3F( (-3.0f + t) * u, -2.0f * u, 0.0f ); + vertexPointer[1] = SFVEC3F( (-3.0f + t) * u, 2.0f * u, 0.0f ); + vertexPointer[2] = SFVEC3F( (-1.0f + t) * u, 0.0f * u, 0.0f ); + + vertexPointer[3] = SFVEC3F( -2.0f * u, (-3.0f + t) * u, 0.0f ); + vertexPointer[4] = SFVEC3F( 2.0f * u, (-3.0f + t) * u, 0.0f ); + vertexPointer[5] = SFVEC3F( 0.0f * u, (-1.0f + t) * u, 0.0f ); + + vertexPointer[6] = SFVEC3F( (3.0f - t) * u, -2.0f * u, 0.0f ); + vertexPointer[7] = SFVEC3F( (3.0f - t) * u, 2.0f * u, 0.0f ); + vertexPointer[8] = SFVEC3F( (1.0f - t) * u, 0.0f * u, 0.0f ); + + vertexPointer[9] = SFVEC3F( 2.0f * u, (3.0f - t) * u, 0.0f ); + vertexPointer[10] = SFVEC3F( -2.0f * u, (3.0f - t) * u, 0.0f ); + vertexPointer[11] = SFVEC3F( 0.0f * u, (1.0f - t) * u, 0.0f ); + + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + glDisableClientState( GL_NORMAL_ARRAY ); + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_FLOAT, 0, vertexPointer ); + + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + glDrawArrays( GL_TRIANGLES, 0, 4 * 3 ); + + glDisable( GL_BLEND ); + + glDisableClientState( GL_VERTEX_ARRAY ); +} + + +void EDA_3D_CANVAS::render_pivot( float t , float aScale ) +{ + wxASSERT( aScale >= 0.0f ); + wxASSERT( t >= 0.0f ); + + if( t > 1.0f ) + t = 1.0f; + + const SFVEC3F &lookAtPos = m_settings.CameraGet().GetLookAtPos_T1(); + + glDisable( GL_LIGHTING ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_CULL_FACE ); + + // Set projection and modelview matrixes + // ///////////////////////////////////////////////////////////////////////// + glMatrixMode( GL_PROJECTION ); + glLoadMatrixf( glm::value_ptr( m_settings.CameraGet().GetProjectionMatrix() ) ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glLoadMatrixf( glm::value_ptr( m_settings.CameraGet().GetViewMatrix() ) ); + + glEnable( GL_COLOR_MATERIAL ); + glColor4f( 0.0f, 1.0f, 0.0f, 0.75f - t * 0.75f ); + + // Translate to the look at position + glTranslatef( lookAtPos.x, lookAtPos.y, lookAtPos.z ); + + glScalef( aScale, aScale, aScale ); + + pivot_render_triangles( t * 0.5f ); + + t = t * 0.80f; + glScalef( 1.0f - t, 1.0f - t, 1.0f - t ); + glColor4f( 0.0f, 1.0f, 0.0f, 0.8f - t ); + + glPushMatrix(); + glRotatef( t * 90.0f, 0.0f, 0.0f, 1.0f ); + pivot_render_triangles( t * 0.5f ); + glPopMatrix(); + + glPushMatrix(); + glRotatef( -t * 90.0f, 0.0f, 0.0f, 1.0f ); + pivot_render_triangles( t * 0.5f ); + glPopMatrix(); +} diff --git a/3d-viewer/3d_canvas/status_text_reporter.h b/3d-viewer/3d_canvas/status_text_reporter.h new file mode 100644 index 0000000000..2e12660658 --- /dev/null +++ b/3d-viewer/3d_canvas/status_text_reporter.h @@ -0,0 +1,68 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file status_text_reporter.h + * @brief + */ + +#ifndef STATUS_TEXT_REPORTER_H_ +#define STATUS_TEXT_REPORTER_H_ + +#include + +/** + * Class STATUS_TEXT_REPORTER + * is a wrapper for reporting to a wxString in a wxFrame status text. + */ +class STATUS_TEXT_REPORTER : public REPORTER +{ +private: + wxStatusBar *m_parentStatusBar; + int m_position; + bool m_hasMessage; + +public: + STATUS_TEXT_REPORTER( wxStatusBar* aParentStatusBar, int aPosition = 0 ) : + REPORTER(), + m_parentStatusBar( aParentStatusBar ), m_position( aPosition ) + { + m_hasMessage = false; + } + + REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_UNDEFINED ) + { + if( !aText.IsEmpty() ) + m_hasMessage = true; + + if( m_parentStatusBar ) + m_parentStatusBar->SetStatusText( aText, m_position ); + + return *this; + } + + bool HasMessage() const { return m_hasMessage; } +}; + + +#endif // STATUS_TEXT_REPORTER_H_ diff --git a/3d-viewer/3d_class.cpp b/3d-viewer/3d_class.cpp deleted file mode 100644 index 611070543b..0000000000 --- a/3d-viewer/3d_class.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_class.cpp - */ - -#include - -#include "3d_viewer.h" -#include "3d_struct.h" -#include "common.h" -#include "modelparsers.h" -#include -#include <3d_cache.h> -#include <3d_filename_resolver.h> - -extern KIWAY* TheKiway; - - -void S3D_MASTER::Insert( S3D_MATERIAL* aMaterial ) -{ - aMaterial->SetNext( m_Materials ); - m_Materials = aMaterial; -} - - -void S3D_MASTER::Copy( S3D_MASTER* pattern ) -{ - SetShape3DName( pattern->GetShape3DName() ); - m_MatScale = pattern->m_MatScale; - m_MatRotation = pattern->m_MatRotation; - m_MatPosition = pattern->m_MatPosition; - m_3D_Drawings = NULL; - m_Materials = NULL; -} - - -S3D_MASTER::S3D_MASTER( EDA_ITEM* aParent ) : - EDA_ITEM( aParent, NOT_USED ) -{ - m_MatScale.x = m_MatScale.y = m_MatScale.z = 1.0; - - m_3D_Drawings = NULL; - m_Materials = NULL; - m_parser = NULL; - m_ShapeType = FILE3D_NONE; - - m_use_modelfile_diffuseColor = true; - m_use_modelfile_emissiveColor = true; - m_use_modelfile_specularColor = true; - m_use_modelfile_ambientIntensity = true; - m_use_modelfile_transparency = true; - m_use_modelfile_shininess = true; -} - - -S3D_MASTER:: ~S3D_MASTER() -{ - STRUCT_3D_SHAPE* next; - S3D_MATERIAL* nextmat; - - for( ; m_3D_Drawings != NULL; m_3D_Drawings = next ) - { - next = m_3D_Drawings->Next(); - delete m_3D_Drawings; - m_3D_Drawings = 0; - } - - for( ; m_Materials != NULL; m_Materials = nextmat ) - { - nextmat = m_Materials->Next(); - delete m_Materials; - m_Materials = NULL; - } -} - - -bool S3D_MASTER::Is3DType( enum FILE3D_TYPE aShapeType ) -{ - // type 'none' is not valid and will always return false - if( aShapeType == FILE3D_NONE ) - return false; - - // no one is interested if we have no file - if( m_Shape3DName.empty() ) - return false; - - if( aShapeType == m_ShapeType ) - return true; - - return false; -} - - -void S3D_MASTER::SetShape3DName( const wxString& aShapeName ) -{ - m_ShapeType = FILE3D_NONE; - m_Shape3DName = aShapeName; - - if( m_Shape3DName.empty() ) - return; - - wxFileName fn = m_Shape3DName; - m_Shape3DNameExtension = fn.GetExt(); - - if( m_Shape3DNameExtension == wxT( "wrl" ) || - m_Shape3DNameExtension == wxT( "x3d" ) ) - m_ShapeType = FILE3D_VRML; - else if( m_Shape3DNameExtension == wxT( "idf" ) ) - m_ShapeType = FILE3D_IDF; - else - m_ShapeType = FILE3D_UNKNOWN; - - // Expand any environment variables embedded in footprint's m_Shape3DName field. - // To ensure compatibility with most of footprint's m_Shape3DName field, - // if the m_Shape3DName is not an absolute path the default path - // given by the environment variable KISYS3DMOD will be used - - if( m_Shape3DName.StartsWith( wxT("${") ) || - m_Shape3DName.StartsWith( wxT("$(") ) ) - m_Shape3DFullFilename = ExpandEnvVarSubstitutions( m_Shape3DName ); - else - m_Shape3DFullFilename = m_Shape3DName; - - if( NULL != TheKiway ) - { - m_Shape3DFullFilename = TheKiway->Prj().Get3DCacheManager()->GetResolver() - ->ResolvePath( m_Shape3DFullFilename ); - } - - return; -} - - -const wxString S3D_MASTER::GetShape3DFullFilename() -{ - return m_Shape3DFullFilename; -} - - -const wxString S3D_MASTER::GetShape3DExtension() -{ - return m_Shape3DNameExtension; -} - - -STRUCT_3D_SHAPE::STRUCT_3D_SHAPE( EDA_ITEM* aParent ) : - EDA_ITEM( aParent, NOT_USED ) -{ - m_3D_Coord = NULL; - m_3D_CoordIndex = NULL; - m_3D_Points = 0; -} - - -STRUCT_3D_SHAPE:: ~STRUCT_3D_SHAPE() -{ - delete m_3D_Coord; - delete m_3D_CoordIndex; -} diff --git a/3d-viewer/3d_draw.cpp b/3d-viewer/3d_draw.cpp deleted file mode 100644 index ebd0a4953e..0000000000 --- a/3d-viewer/3d_draw.cpp +++ /dev/null @@ -1,1218 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_draw.cpp - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define GLM_FORCE_RADIANS -#include -#include -#ifdef __WINDOWS__ -#include // must be included before gl.h -#endif - -#include <3d_viewer.h> -#include <3d_canvas.h> -#include -#include -#include <3d_draw_basic_functions.h> -#include "3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h" -#include - -#include -#include - - -extern SHAPE_POLY_SET::POLYGON_MODE polygonsCalcMode; - -/* returns the Z orientation parameter 1.0 or -1.0 for aLayer - * Z orientation is 1.0 for all layers but "back" layers: - * B_Cu , B_Adhes, B_Paste ), B_SilkS - * used to calculate the Z orientation parameter for glNormal3f - */ -GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer ); - - -/** - * Class STATUS_TEXT_REPORTER - * is a wrapper for reporting to a wxString in a wxFrame status text. - */ -class STATUS_TEXT_REPORTER : public REPORTER -{ - wxFrame * m_frame; - int m_position; - bool m_hasMessage; - - -public: - STATUS_TEXT_REPORTER( wxFrame* aFrame, int aPosition = 0 ) : - REPORTER(), - m_frame( aFrame ), m_position( aPosition ) - { - m_hasMessage = false; - } - - REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_UNDEFINED ) - { - if( !aText.IsEmpty() ) - m_hasMessage = true; - - m_frame->SetStatusText( aText, m_position ); - return *this; - } - - bool HasMessage() const { return m_hasMessage; } -}; - - -void EDA_3D_CANVAS::create_and_render_shadow_buffer( GLuint *aDst_gl_texture, - GLuint aTexture_size, bool aDraw_body, int aBlurPasses ) -{ - glDisable( GL_TEXTURE_2D ); - - glViewport( 0, 0, aTexture_size, aTexture_size); - - glClearColor( 1.0f, 1.0f, 1.0f, 1.0f ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - // Render body and shapes - - if( aDraw_body && m_glLists[GL_ID_BODY] ) - glCallList( m_glLists[GL_ID_BODY] ); - - if( m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ) - glCallList( m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ); - - // Create and Initialize the float depth buffer - - float *depthbufferFloat = (float*) malloc( aTexture_size * aTexture_size * sizeof(float) ); - - for( unsigned int i = 0; i < (aTexture_size * aTexture_size); i++ ) - depthbufferFloat[i] = 1.0f; - - glPixelStorei( GL_PACK_ALIGNMENT, 4 ); - glPixelStorei( GL_UNPACK_ALIGNMENT, 4 ); - glReadBuffer( GL_BACK_LEFT ); - glReadPixels( 0, 0, - aTexture_size, aTexture_size, - GL_DEPTH_COMPONENT, GL_FLOAT, depthbufferFloat ); - - CheckGLError( __FILE__, __LINE__ ); - - glEnable( GL_TEXTURE_2D ); - glGenTextures( 1, aDst_gl_texture ); - glBindTexture( GL_TEXTURE_2D, *aDst_gl_texture ); - - CIMAGE imgDepthBuffer( aTexture_size, aTexture_size ); - CIMAGE imgDepthBufferAux( aTexture_size, aTexture_size ); - - imgDepthBuffer.SetPixelsFromNormalizedFloat( depthbufferFloat ); - - free( depthbufferFloat ); - - // Debug texture image - //wxString filename; - //filename.Printf( "imgDepthBuffer_%04d", *aDst_gl_texture ); - //imgDepthBuffer.SaveAsPNG( filename ); - - while( aBlurPasses > 0 ) - { - aBlurPasses--; - imgDepthBufferAux.EfxFilter( &imgDepthBuffer, FILTER_GAUSSIAN_BLUR ); - imgDepthBuffer.EfxFilter( &imgDepthBufferAux, FILTER_GAUSSIAN_BLUR ); - } - - // Debug texture image - //filename.Printf( "imgDepthBuffer_blur%04d", *aDst_gl_texture ); - //imgDepthBuffer.SaveAsPNG( filename ); - - unsigned char *depthbufferRGBA = (unsigned char*) malloc( aTexture_size * aTexture_size * 4 ); - unsigned char *pPixels = imgDepthBuffer.GetBuffer(); - - // Convert it to a RGBA buffer - for( unsigned int i = 0; i < (aTexture_size * aTexture_size); i++ ) - { - depthbufferRGBA[i * 4 + 0] = 0; - depthbufferRGBA[i * 4 + 1] = 0; - depthbufferRGBA[i * 4 + 2] = 0; - depthbufferRGBA[i * 4 + 3] = 255 - pPixels[i]; // Store in alpha channel the inversion of the image - } - - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, aTexture_size, aTexture_size, 0, GL_RGBA, GL_UNSIGNED_BYTE, depthbufferRGBA ); - - free( depthbufferRGBA ); - - CheckGLError( __FILE__, __LINE__ ); -} - -/// Scale factor to make a bigger BBox in order to blur the texture and dont have artifacts in the edges -#define SHADOW_BOUNDING_BOX_SCALE 1.25f - -void EDA_3D_CANVAS::generateFakeShadowsTextures( REPORTER* aErrorMessages, REPORTER* aActivity ) -{ - if( m_shadow_init == true ) - { - return; - } - - // Init info 3d parameters and create gl lists: - CreateDrawGL_List( aErrorMessages, aActivity ); - - DBG( unsigned strtime = GetRunningMicroSecs() ); - - m_shadow_init = true; - - glClearColor( 0, 0, 0, 1 ); - - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - - const float zDistMax = Millimeter2iu( 3.5 ) * GetPrm3DVisu().m_BiuTo3Dunits; - - glOrtho( -GetPrm3DVisu().m_BoardSize.x * GetPrm3DVisu().m_BiuTo3Dunits / 2.0f, - GetPrm3DVisu().m_BoardSize.x * GetPrm3DVisu().m_BiuTo3Dunits / 2.0f, - -GetPrm3DVisu().m_BoardSize.y * GetPrm3DVisu().m_BiuTo3Dunits / 2.0f, - GetPrm3DVisu().m_BoardSize.y * GetPrm3DVisu().m_BiuTo3Dunits / 2.0f, - 0.0, zDistMax ); - - float zpos = GetPrm3DVisu().GetLayerZcoordBIU( F_Paste ) * GetPrm3DVisu().m_BiuTo3Dunits; - - // Render FRONT shadow - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - glTranslatef( 0.0f, 0.0f, zpos ); - glRotatef( 180.0f, 0.0f, 1.0f, 0.0f ); - - // move the board in order to draw it with its center at 0,0 3D coordinates - glTranslatef( -GetPrm3DVisu().m_BoardPos.x * GetPrm3DVisu().m_BiuTo3Dunits, - -GetPrm3DVisu().m_BoardPos.y * GetPrm3DVisu().m_BiuTo3Dunits, - 0.0f ); - - create_and_render_shadow_buffer( &m_text_fake_shadow_front, 512, false, 1 ); - - zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Paste ) * GetPrm3DVisu().m_BiuTo3Dunits; - - // Render BACK shadow - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - glTranslatef( 0.0f, 0.0f, fabs( zpos ) ); - - - // move the board in order to draw it with its center at 0,0 3D coordinates - glTranslatef( -GetPrm3DVisu().m_BoardPos.x * GetPrm3DVisu().m_BiuTo3Dunits, - -GetPrm3DVisu().m_BoardPos.y * GetPrm3DVisu().m_BiuTo3Dunits, - 0.0f ); - - create_and_render_shadow_buffer( &m_text_fake_shadow_back, 512, false, 1 ); - - - // Render ALL BOARD shadow - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - - // Normalization scale to convert bouding box - // to normalize 3D units between -1.0 and +1.0 - - S3D_VERTEX v = m_fastAABBox_Shadow.Max() - m_fastAABBox_Shadow.Min(); - float BoundingBoxBoardiuTo3Dunits = 2.0f / glm::max( v.x, v.y ); - - //float zDistance = (m_lightPos.z * zDistMax) / sqrt( (m_lightPos.z - m_fastAABBox_Shadow.Min().z) ); - float zDistance = (m_lightPos.z - m_fastAABBox_Shadow.Min().z) / 3.0f; - - glOrtho( -v.x * BoundingBoxBoardiuTo3Dunits / 2.0f, - v.x * BoundingBoxBoardiuTo3Dunits / 2.0f, - -v.y * BoundingBoxBoardiuTo3Dunits / 2.0f, - v.y * BoundingBoxBoardiuTo3Dunits / 2.0f, - 0.0f, zDistance ); - - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - - // fits the bouding box to scale this size - glScalef( BoundingBoxBoardiuTo3Dunits, BoundingBoxBoardiuTo3Dunits, 1.0f ); - - // Place the eye in the lowerpoint of boudingbox and turn arround and look up to the model - glTranslatef( 0.0f, 0.0f, m_fastAABBox_Shadow.Min().z ); - glRotatef( 180.0, 0.0f, 1.0f, 0.0f ); - - // move the bouding box in order to draw it with its center at 0,0 3D coordinates - glTranslatef( -(m_fastAABBox_Shadow.Min().x + v.x / 2.0f), -(m_fastAABBox_Shadow.Min().y + v.y / 2.0f), 0.0f ); - - create_and_render_shadow_buffer( &m_text_fake_shadow_board, 512, true, 10 ); - - DBG( printf( " generateFakeShadowsTextures total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); -} - - -void EDA_3D_CANVAS::Redraw() -{ - // SwapBuffer requires the window to be shown before calling - if( !IsShownOnScreen() ) - return; - - wxString err_messages; - WX_STRING_REPORTER errorReporter( &err_messages ); - STATUS_TEXT_REPORTER activityReporter( Parent(), 0 ); - - // Display build time at the end of build - unsigned strtime = GetRunningMicroSecs(); - - GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this ); - - // Set the OpenGL viewport according to the client size of this canvas. - // This is done here rather than in a wxSizeEvent handler because our - // OpenGL rendering context (and thus viewport setting) is used with - // multiple canvases: If we updated the viewport in the wxSizeEvent - // handler, changing the size of one canvas causes a viewport setting that - // is wrong when next another canvas is repainted. - wxSize size = GetClientSize(); - - InitGL(); - - if( isRealisticMode() && isEnabled( FL_RENDER_SHADOWS ) ) - { - generateFakeShadowsTextures( &errorReporter, &activityReporter ); - } - - // *MUST* be called *after* GL_CONTEXT_MANAGER::LockCtx(): - glViewport( 0, 0, size.x, size.y ); - - // clear color and depth buffers - glClearColor( 0.95, 0.95, 1.0, 1.0 ); - glClearStencil( 0 ); - glClearDepth( 1.0 ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); - - glShadeModel( GL_SMOOTH ); - - // Draw background - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - - glDisable( GL_LIGHTING ); - glDisable( GL_COLOR_MATERIAL ); - glDisable( GL_DEPTH_TEST ); - glDisable( GL_TEXTURE_2D ); - - // Draw the background ( rectangle with color gradient) - glBegin( GL_QUADS ); - SetGLColor( GetPrm3DVisu().m_BgColor_Top, 1.0 ); - glVertex2f( -1.0, 1.0 ); // Top left corner - - SetGLColor( GetPrm3DVisu().m_BgColor, 1.0 ); - glVertex2f( -1.0,-1.0 ); // bottom left corner - glVertex2f( 1.0,-1.0 ); // bottom right corner - - SetGLColor( GetPrm3DVisu().m_BgColor_Top, 1.0 ); - glVertex2f( 1.0, 1.0 ); // top right corner - - glEnd(); - glEnable( GL_DEPTH_TEST ); - - - // set viewing projection - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - -#define MAX_VIEW_ANGLE 160.0 / 45.0 - - if( GetPrm3DVisu().m_Zoom > MAX_VIEW_ANGLE ) - GetPrm3DVisu().m_Zoom = MAX_VIEW_ANGLE; - - if( Parent()->ModeIsOrtho() ) - { - // OrthoReductionFactor is chosen to provide roughly the same size as - // Perspective View - const double orthoReductionFactor = 400 / GetPrm3DVisu().m_Zoom; - - // Initialize Projection Matrix for Ortographic View - glOrtho( -size.x / orthoReductionFactor, size.x / orthoReductionFactor, - -size.y / orthoReductionFactor, size.y / orthoReductionFactor, 1, 100 ); - } - else - { - // Ratio width / height of the window display - double ratio_HV = (double) size.x / size.y; - - // Initialize Projection Matrix for Perspective View - gluPerspective( 45.0f * GetPrm3DVisu().m_Zoom, ratio_HV, 1, 100 ); - } - - // position viewer - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - - glTranslatef( 0.0f, 0.0f, -(m_ZBottom + m_ZTop) / 2.0f ); - - // Setup light sources: - SetLights(); - - CheckGLError( __FILE__, __LINE__ ); - - glMatrixMode( GL_MODELVIEW ); // position viewer - - // transformations - GLfloat mat[4][4]; - - // Translate motion first, so rotations don't mess up the orientation... - glTranslatef( m_draw3dOffset.x, m_draw3dOffset.y, 0.0F ); - - build_rotmatrix( mat, GetPrm3DVisu().m_Quat ); - glMultMatrixf( &mat[0][0] ); - - glRotatef( GetPrm3DVisu().m_Rot[0], 1.0, 0.0, 0.0 ); - glRotatef( GetPrm3DVisu().m_Rot[1], 0.0, 1.0, 0.0 ); - glRotatef( GetPrm3DVisu().m_Rot[2], 0.0, 0.0, 1.0 ); - - - if( ! m_glLists[GL_ID_BOARD] || ! m_glLists[GL_ID_TECH_LAYERS] ) - CreateDrawGL_List( &errorReporter, &activityReporter ); - - if( isEnabled( FL_AXIS ) && m_glLists[GL_ID_AXIS] ) - glCallList( m_glLists[GL_ID_AXIS] ); - - // move the board in order to draw it with its center at 0,0 3D coordinates - glTranslatef( -GetPrm3DVisu().m_BoardPos.x * GetPrm3DVisu().m_BiuTo3Dunits, - -GetPrm3DVisu().m_BoardPos.y * GetPrm3DVisu().m_BiuTo3Dunits, - 0.0f ); - - if( isEnabled( FL_MODULE ) ) - { - if( ! m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ) - CreateDrawGL_List( &errorReporter, &activityReporter ); - } - - glEnable( GL_LIGHTING ); - - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - - if( isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ) ) - glEnable( GL_TEXTURE_2D ); - else - glDisable( GL_TEXTURE_2D ); - - // Set material for the board - glEnable( GL_COLOR_MATERIAL ); - SetOpenGlDefaultMaterial(); - - //glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, FALSE ); - - // Board Body - - GLint shininess_value = 32; - glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, shininess_value ); - - if( isEnabled( FL_SHOW_BOARD_BODY ) ) - { - if( m_glLists[GL_ID_BODY] ) - { - glCallList( m_glLists[GL_ID_BODY] ); - } - } - - - // Board - - // specify material parameters for the lighting model. - shininess_value = 52; - glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, shininess_value ); - - glm::vec4 specular( GetPrm3DVisu().m_CopperColor.m_Red * 0.20f, - GetPrm3DVisu().m_CopperColor.m_Green * 0.20f, - GetPrm3DVisu().m_CopperColor.m_Blue * 0.20f, 1.0f ); - glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.x ); - - if( m_glLists[GL_ID_BOARD] ) - { - glCallList( m_glLists[GL_ID_BOARD] ); - } - - - // Tech layers - - shininess_value = 32; - glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, shininess_value ); - - glm::vec4 specularTech( 0.0f, 0.0f, 0.0f, 1.0f ); - glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specularTech.x ); - - if( m_glLists[GL_ID_TECH_LAYERS] ) - { - glCallList( m_glLists[GL_ID_TECH_LAYERS] ); - } - - if( isEnabled( FL_COMMENTS ) || isEnabled( FL_ECO ) ) - { - if( ! m_glLists[GL_ID_AUX_LAYERS] ) - CreateDrawGL_List( &errorReporter, &activityReporter ); - - glCallList( m_glLists[GL_ID_AUX_LAYERS] ); - } - - //glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, TRUE ); - - // Draw Component Shadow - - if( isEnabled( FL_MODULE ) && isRealisticMode() && - isEnabled( FL_RENDER_SHADOWS ) ) - { - glEnable( GL_CULL_FACE ); - glDisable( GL_DEPTH_TEST ); - - glEnable( GL_COLOR_MATERIAL ) ; - SetOpenGlDefaultMaterial(); - glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); - - glEnable( GL_TEXTURE_2D ); - - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - - if( m_glLists[GL_ID_SHADOW_FRONT] ) - { - glBindTexture( GL_TEXTURE_2D, m_text_fake_shadow_front ); - glCallList( m_glLists[GL_ID_SHADOW_FRONT] ); - } - - if( m_glLists[GL_ID_SHADOW_BACK] ) - { - glBindTexture( GL_TEXTURE_2D, m_text_fake_shadow_back ); - glCallList( m_glLists[GL_ID_SHADOW_BACK] ); - } - glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); - - glEnable( GL_DEPTH_TEST ); - glDisable( GL_TEXTURE_2D ); - glDisable( GL_CULL_FACE ); - } - - glEnable( GL_COLOR_MATERIAL ); - SetOpenGlDefaultMaterial(); - - glDisable( GL_BLEND ); - - - // Draw Solid Shapes - - if( isEnabled( FL_MODULE ) ) - { - if( ! m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ) - CreateDrawGL_List( &errorReporter, &activityReporter ); - - glCallList( m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ); - } - - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - - - // Grid uses transparency: draw it after all objects - - if( isEnabled( FL_GRID ) ) - { - if( ! m_glLists[GL_ID_GRID] ) - CreateDrawGL_List( &errorReporter, &activityReporter ); - - glCallList( m_glLists[GL_ID_GRID] ); - } - - - // Draw Board Shadow - - if( isRealisticMode() && isEnabled( FL_RENDER_SHADOWS ) ) - { - if( m_glLists[GL_ID_SHADOW_BOARD] ) - { - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glColor4f( 1.0, 1.0, 1.0, 0.75f ); - glEnable( GL_CULL_FACE ); - glDisable( GL_COLOR_MATERIAL ); - glEnable( GL_TEXTURE_2D ); - glBindTexture( GL_TEXTURE_2D, m_text_fake_shadow_board ); - glCallList( m_glLists[GL_ID_SHADOW_BOARD] ); - glDisable( GL_CULL_FACE ); - glDisable( GL_TEXTURE_2D ); - } - } - - // This list must be drawn last, because it contains the - // transparent gl objects, which should be drawn after all - // non transparent objects - if( isEnabled( FL_MODULE ) && m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] ) - { - glEnable( GL_COLOR_MATERIAL ); - SetOpenGlDefaultMaterial(); - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glCallList( m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] ); - } - - // Debug bounding boxes - /* - glDisable( GL_BLEND ); - glDisable( GL_COLOR_MATERIAL ); - glDisable( GL_LIGHTING ); - glColor4f( 1.0f, 0.0f, 1.0f, 1.0f ); - m_fastAABBox_Shadow.GLdebug(); - - glColor4f( 0.0f, 1.0f, 1.0f, 1.0f ); - m_boardAABBox.GLdebug(); - */ - - SwapBuffers(); - GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); - - // Show calculation time if some activity was reported - if( activityReporter.HasMessage() ) - { - // Calculation time in seconds - double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e6; - - activityReporter.Report( wxString::Format( _( "Build time %.3f s" ), - calculation_time ) ); - } - else - activityReporter.Report( wxEmptyString ); - - if( !err_messages.IsEmpty() ) - wxLogMessage( err_messages ); - -} - - -/** - * Function buildBoard3DAuxLayers - * Called by CreateDrawGL_List() - * Fills the OpenGL GL_ID_BOARD draw list with items - * on aux layers only - */ -void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* aActivity ) -{ - const int segcountforcircle = 18; - double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) ); - BOARD* pcb = GetBoard(); - - SHAPE_POLY_SET bufferPolys; - - static const LAYER_ID sequence[] = { - Dwgs_User, - Cmts_User, - Eco1_User, - Eco2_User, - Edge_Cuts, - Margin - }; - - for( LSEQ aux( sequence, sequence+DIM(sequence) ); aux; ++aux ) - { - LAYER_ID layer = *aux; - - if( !is3DLayerEnabled( layer ) ) - continue; - - if( aActivity ) - aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); - - bufferPolys.RemoveAllContours(); - - for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) - { - if( !item->IsOnLayer( layer ) ) - continue; - - switch( item->Type() ) - { - case PCB_LINE_T: - ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( - bufferPolys, 0, segcountforcircle, correctionFactor ); - break; - - case PCB_TEXT_T: - ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( - bufferPolys, 0, segcountforcircle, correctionFactor ); - break; - - default: - break; - } - } - - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - { - module->TransformPadsShapesWithClearanceToPolygon( layer, - bufferPolys, - 0, - segcountforcircle, - correctionFactor ); - - module->TransformGraphicShapesWithClearanceToPolygonSet( layer, - bufferPolys, - 0, - segcountforcircle, - correctionFactor ); - } - - // bufferPolys contains polygons to merge. Many overlaps . - // Calculate merged polygons and remove pads and vias holes - if( bufferPolys.IsEmpty() ) - continue; - - bufferPolys.Simplify( polygonsCalcMode ); - - int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); - int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); - // for Draw3D_SolidHorizontalPolyPolygons, - // zpos it the middle between bottom and top sides. - // However for top layers, zpos should be the bottom layer pos, - // and for bottom layers, zpos should be the top layer pos. - if( Get3DLayer_Z_Orientation( layer ) > 0 ) - zpos += thickness/2; - else - zpos -= thickness/2 ; - - float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted) - - // If we are not using thickness, then the znormal must face the layer direction - // because it will draw just one plane - if( !thickness ) - zNormal = Get3DLayer_Z_Orientation( layer ); - - setGLTechLayersColor( layer ); - Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, - thickness, GetPrm3DVisu().m_BiuTo3Dunits, false, - zNormal ); - } -} - -void EDA_3D_CANVAS::buildShadowList( GLuint aFrontList, GLuint aBacklist, GLuint aBoardList ) -{ - // Board shadows are based on board dimension. - - float xmin = m_boardAABBox.Min().x; - float xmax = m_boardAABBox.Max().x; - float ymin = m_boardAABBox.Min().y; - float ymax = m_boardAABBox.Max().y; - - float zpos = GetPrm3DVisu().GetLayerZcoordBIU( F_Paste ) * GetPrm3DVisu().m_BiuTo3Dunits; - - // Shadow FRONT - glNewList( aFrontList, GL_COMPILE ); - - glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( F_Paste ) ); - - glBegin (GL_QUADS); - glTexCoord2f( 1.0, 0.0 ); glVertex3f( xmin, ymin, zpos ); - glTexCoord2f( 0.0, 0.0 ); glVertex3f( xmax, ymin, zpos ); - glTexCoord2f( 0.0, 1.0 ); glVertex3f( xmax, ymax, zpos ); - glTexCoord2f( 1.0, 1.0 ); glVertex3f( xmin, ymax, zpos ); - glEnd(); - - glEndList(); - - - // Shadow BACK - zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Paste ) * GetPrm3DVisu().m_BiuTo3Dunits; - - glNewList( aBacklist, GL_COMPILE ); - - glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( B_Paste ) ); - - glBegin (GL_QUADS); - glTexCoord2f( 0.0, 0.0 ); glVertex3f( xmin, ymin, zpos ); - glTexCoord2f( 0.0, 1.0 ); glVertex3f( xmin, ymax, zpos ); - glTexCoord2f( 1.0, 1.0 ); glVertex3f( xmax, ymax, zpos ); - glTexCoord2f( 1.0, 0.0 ); glVertex3f( xmax, ymin, zpos ); - glEnd(); - - glEndList(); - - // Shadow BOARD - - // Floor shadow is based on axis alighned bounding box dimension - xmin = m_fastAABBox_Shadow.Min().x; - xmax = m_fastAABBox_Shadow.Max().x; - ymin = m_fastAABBox_Shadow.Min().y; - ymax = m_fastAABBox_Shadow.Max().y; - - glNewList( aBoardList, GL_COMPILE ); - glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( F_Paste ) ); - - glBegin (GL_QUADS); - glTexCoord2f( 1.0, 0.0 ); glVertex3f( xmin, ymin, m_fastAABBox_Shadow.Min().z ); - glTexCoord2f( 0.0, 0.0 ); glVertex3f( xmax, ymin, m_fastAABBox_Shadow.Min().z ); - glTexCoord2f( 0.0, 1.0 ); glVertex3f( xmax, ymax, m_fastAABBox_Shadow.Min().z ); - glTexCoord2f( 1.0, 1.0 ); glVertex3f( xmin, ymax, m_fastAABBox_Shadow.Min().z ); - glEnd(); - - glEndList(); -} - - -void EDA_3D_CANVAS::CreateDrawGL_List( REPORTER* aErrorMessages, REPORTER* aActivity ) -{ - BOARD* pcb = GetBoard(); - - wxBusyCursor dummy; - - // Build 3D board parameters: - GetPrm3DVisu().InitSettings( pcb ); - - glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); - - // Create axis gl list (if it is not shown, the list will be not called - draw3DAxis(); - - // Create Board full gl lists: - - if( ! m_glLists[GL_ID_BOARD] ) - { - DBG( unsigned strtime = GetRunningMicroSecs() ); - - m_glLists[GL_ID_BOARD] = glGenLists( 1 ); - m_glLists[GL_ID_BODY] = glGenLists( 1 ); - buildBoard3DView(m_glLists[GL_ID_BOARD], m_glLists[GL_ID_BODY], aErrorMessages, aActivity ); - CheckGLError( __FILE__, __LINE__ ); - - DBG( printf( " buildBoard3DView total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); - } - - if( ! m_glLists[GL_ID_TECH_LAYERS] ) - { - DBG( unsigned strtime = GetRunningMicroSecs() ); - - m_glLists[GL_ID_TECH_LAYERS] = glGenLists( 1 ); - glNewList( m_glLists[GL_ID_TECH_LAYERS], GL_COMPILE ); - // when calling BuildTechLayers3DView, - // do not show warnings, which are the same as buildBoard3DView - buildTechLayers3DView( aErrorMessages, aActivity ); - glEndList(); - CheckGLError( __FILE__, __LINE__ ); - - DBG( printf( " buildTechLayers3DView total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); - } - - if( ! m_glLists[GL_ID_AUX_LAYERS] ) - { - DBG( unsigned strtime = GetRunningMicroSecs() ); - - m_glLists[GL_ID_AUX_LAYERS] = glGenLists( 1 ); - glNewList( m_glLists[GL_ID_AUX_LAYERS], GL_COMPILE ); - buildBoard3DAuxLayers( aErrorMessages, aActivity ); - glEndList(); - CheckGLError( __FILE__, __LINE__ ); - - DBG( printf( " buildBoard3DAuxLayers total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); - } - - // draw modules 3D shapes - if( ! m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] && isEnabled( FL_MODULE ) ) - { - m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] = glGenLists( 1 ); - - // GL_ID_3DSHAPES_TRANSP_FRONT is an auxiliary list for 3D shapes; - // Ensure it is cleared before rebuilding it - if( m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] ) - glDeleteLists( m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT], 1 ); - - bool useMaterial = g_Parm_3D_Visu.GetFlag( FL_RENDER_MATERIAL ); - - if( useMaterial ) - m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] = glGenLists( 1 ); - else - m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] = 0; - - buildFootprintShape3DList( m_glLists[GL_ID_3DSHAPES_SOLID_FRONT], - m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT], - aErrorMessages, aActivity ); - - CheckGLError( __FILE__, __LINE__ ); - } - - calcBBox(); - - // Create grid gl list - if( ! m_glLists[GL_ID_GRID] ) - { - m_glLists[GL_ID_GRID] = glGenLists( 1 ); - glNewList( m_glLists[GL_ID_GRID], GL_COMPILE ); - - draw3DGrid( GetPrm3DVisu().m_3D_Grid ); - - glEndList(); - } - - if( !m_glLists[GL_ID_SHADOW_FRONT] ) - m_glLists[GL_ID_SHADOW_FRONT] = glGenLists( 1 ); - - if( !m_glLists[GL_ID_SHADOW_BACK] ) - m_glLists[GL_ID_SHADOW_BACK] = glGenLists( 1 ); - - if( !m_glLists[GL_ID_SHADOW_BOARD] ) - m_glLists[GL_ID_SHADOW_BOARD] = glGenLists( 1 ); - - buildShadowList( m_glLists[GL_ID_SHADOW_FRONT], - m_glLists[GL_ID_SHADOW_BACK], - m_glLists[GL_ID_SHADOW_BOARD] ); - - CheckGLError( __FILE__, __LINE__ ); -} - - -void EDA_3D_CANVAS::calcBBox() -{ - BOARD* pcb = GetBoard(); - - m_fastAABBox.Reset(); - - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - { - CBBOX tmpFastAABBox; - - // Compute the transformation matrix for this module based on translation, rotation and orientation. - float zpos = GetPrm3DVisu().GetModulesZcoord3DIU( module->IsFlipped() ); - wxPoint pos = module->GetPosition(); - - glm::mat4 fullTransformMatrix; - fullTransformMatrix = glm::translate( glm::mat4(), S3D_VERTEX( (float)(pos.x * GetPrm3DVisu().m_BiuTo3Dunits), - (float)(-pos.y * GetPrm3DVisu().m_BiuTo3Dunits), - zpos ) ); - - if( module->GetOrientation() ) - fullTransformMatrix = glm::rotate( fullTransformMatrix, - glm::radians( (float)(module->GetOrientation() / 10.0f) ), - S3D_VERTEX( 0.0f, 0.0f, 1.0f ) ); - - if( module->IsFlipped() ) - { - fullTransformMatrix = glm::rotate( fullTransformMatrix, glm::radians( 180.0f ), S3D_VERTEX( 0.0f, 1.0f, 0.0f ) ); - fullTransformMatrix = glm::rotate( fullTransformMatrix, glm::radians( 180.0f ), S3D_VERTEX( 0.0f, 0.0f, 1.0f ) ); - } - - // Compute a union bounding box for all the shapes of the model - - S3D_MASTER* shape3D = module->Models(); - - for( ; shape3D; shape3D = shape3D->Next() ) - { - if( shape3D->Is3DType( S3D_MASTER::FILE3D_VRML ) ) - tmpFastAABBox.Union( shape3D->getFastAABBox() ); - } - - tmpFastAABBox.ApplyTransformationAA( fullTransformMatrix ); - - m_fastAABBox.Union( tmpFastAABBox ); - } - - // Create a board bounding box based on board size - wxSize brd_size = getBoardSize(); - wxPoint brd_center_pos = getBoardCenter(); - - float xsize = brd_size.x; - float ysize = brd_size.y; - - float scale = GetPrm3DVisu().m_BiuTo3Dunits; - float xmin = (brd_center_pos.x - xsize / 2.0) * scale; - float xmax = (brd_center_pos.x + xsize / 2.0) * scale; - float ymin = (brd_center_pos.y - ysize / 2.0) * scale; - float ymax = (brd_center_pos.y + ysize / 2.0) * scale; - - float zmin = GetPrm3DVisu().GetLayerZcoordBIU( B_Adhes ) * scale; - float zmax = GetPrm3DVisu().GetLayerZcoordBIU( F_Adhes ) * scale; - - m_boardAABBox = CBBOX( S3D_VERTEX(xmin, ymin, zmin), - S3D_VERTEX(xmax, ymax, zmax) ); - - // Add BB board with BB models and scale it a bit - m_fastAABBox.Union( m_boardAABBox ); - m_fastAABBox_Shadow = m_fastAABBox; - m_fastAABBox_Shadow.Scale( SHADOW_BOUNDING_BOX_SCALE ); -} - - -void EDA_3D_CANVAS::buildFootprintShape3DList( GLuint aOpaqueList, - GLuint aTransparentList, - REPORTER* aErrorMessages, - REPORTER* aActivity ) -{ - DBG( unsigned strtime = GetRunningMicroSecs() ); - - if( aActivity ) - aActivity->Report( _( "Load 3D Shapes" ) ); - - // clean the parser list if it have any already loaded files - m_model_parsers_list.clear(); - m_model_filename_list.clear(); - - BOARD* pcb = GetBoard(); - - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - read3DComponentShape( module ); - - DBG( printf( " read3DComponentShape total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); - - DBG( strtime = GetRunningMicroSecs() ); - - bool useMaterial = g_Parm_3D_Visu.GetFlag( FL_RENDER_MATERIAL ); - - if( useMaterial ) - { - // aOpaqueList is the gl list for non transparent items - // aTransparentList is the gl list for non transparent items, - // which need to be drawn after all other items - - glNewList( aOpaqueList, GL_COMPILE ); - bool loadOpaqueObjects = true; - - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - render3DComponentShape( module, loadOpaqueObjects, - !loadOpaqueObjects ); - - glEndList(); - - - glNewList( aTransparentList, GL_COMPILE ); - bool loadTransparentObjects = true; - - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - render3DComponentShape( module, !loadTransparentObjects, - loadTransparentObjects ); - - glEndList(); - } - else - { - // Just create one list - glNewList( aOpaqueList, GL_COMPILE ); - - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - render3DComponentShape( module, false, false ); - glEndList(); - } - - DBG( printf( " render3DComponentShape total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); -} - - -bool EDA_3D_CANVAS::read3DComponentShape( MODULE* module ) -{ - if( module ) - { - S3D_MASTER* shape3D = module->Models(); - - for( ; shape3D; shape3D = shape3D->Next() ) - { - if( shape3D->Is3DType( S3D_MASTER::FILE3D_VRML ) ) - { - bool found = false; - - unsigned int i; - wxString shape_filename = shape3D->GetShape3DFullFilename(); - - // Search for already loaded files - for( i = 0; i < m_model_filename_list.size(); i++ ) - { - if( shape_filename.Cmp(m_model_filename_list[i]) == 0 ) - { - found = true; - break; - } - } - - if( found == false ) - { - // Create a new parser - S3D_MODEL_PARSER *newParser = S3D_MODEL_PARSER::Create( shape3D, shape3D->GetShape3DExtension() ); - - if( newParser ) - { - // Read file - if( shape3D->ReadData( newParser ) == 0 ) - { - // Store this couple filename / parsed file - m_model_filename_list.push_back( shape_filename ); - m_model_parsers_list.push_back( newParser ); - } - } - } - else - { - // Reusing file - shape3D->m_parser = m_model_parsers_list[i]; - } - } - } - } - - return true; -} - - -void EDA_3D_CANVAS::render3DComponentShape( MODULE* module, - bool aIsRenderingJustNonTransparentObjects, - bool aIsRenderingJustTransparentObjects ) -{ - double zpos = GetPrm3DVisu().GetModulesZcoord3DIU( module->IsFlipped() ); - - glPushMatrix(); - - wxPoint pos = module->GetPosition(); - - glTranslatef( pos.x * GetPrm3DVisu().m_BiuTo3Dunits, - -pos.y * GetPrm3DVisu().m_BiuTo3Dunits, - zpos ); - - if( module->GetOrientation() ) - glRotatef( (double) module->GetOrientation() / 10.0, 0.0f, 0.0f, 1.0f ); - - if( module->IsFlipped() ) - { - glRotatef( 180.0f, 0.0f, 1.0f, 0.0f ); - glRotatef( 180.0f, 0.0f, 0.0f, 1.0f ); - } - - S3D_MASTER* shape3D = module->Models(); - - for( ; shape3D; shape3D = shape3D->Next() ) - { - if( shape3D->Is3DType( S3D_MASTER::FILE3D_VRML ) ) - { - glPushMatrix(); - - shape3D->Render( aIsRenderingJustNonTransparentObjects, - aIsRenderingJustTransparentObjects ); - - const CBBOX &shapeBBox = shape3D->getBBox(); - if( isEnabled( FL_RENDER_SHOW_MODEL_BBOX ) && shapeBBox.IsInitialized() ) - { - // Set the alpha current color to opaque - float currentColor[4]; - glGetFloatv( GL_CURRENT_COLOR,currentColor ); - currentColor[3] = 1.0f; - glColor4fv( currentColor ); - OGL_draw_bbox( shapeBBox ); - } - - glPopMatrix(); - - // Debug AABBox - //thisBBox = shape3D->getfastAABBox(); - //thisBBox.GLdebug(); - } - } - - glPopMatrix(); -} - - -bool EDA_3D_CANVAS::is3DLayerEnabled( LAYER_ID aLayer ) const -{ - DISPLAY3D_FLG flg; - - // see if layer needs to be shown - // check the flags - switch( aLayer ) - { - case B_Adhes: - case F_Adhes: - flg = FL_ADHESIVE; - break; - - case B_Paste: - case F_Paste: - flg = FL_SOLDERPASTE; - break; - - case B_SilkS: - case F_SilkS: - flg = FL_SILKSCREEN; - break; - - case B_Mask: - case F_Mask: - flg = FL_SOLDERMASK; - break; - - case Dwgs_User: - case Cmts_User: - if( isRealisticMode() ) - return false; - - flg = FL_COMMENTS; - break; - - case Eco1_User: - case Eco2_User: - if( isRealisticMode() ) - return false; - - flg = FL_ECO; - break; - - case B_Cu: - case F_Cu: - return GetPrm3DVisu().m_BoardSettings->IsLayerVisible( aLayer ) - || isRealisticMode(); - break; - - default: - // the layer is an internal copper layer, used the visibility - // - if( isRealisticMode() ) - return false; - - return GetPrm3DVisu().m_BoardSettings->IsLayerVisible( aLayer ); - } - - // The layer has a flag, return the flag - return isEnabled( flg ); -} diff --git a/3d-viewer/3d_draw_basic_functions.cpp b/3d-viewer/3d_draw_basic_functions.cpp deleted file mode 100644 index d4086902f8..0000000000 --- a/3d-viewer/3d_draw_basic_functions.cpp +++ /dev/null @@ -1,426 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_draw_basic_functions.cpp - */ - -#include -#include -#include - -#include <3d_viewer.h> -#include -#include <3d_draw_basic_functions.h> -#include - -// Number of segments to approximate a circle by segments -#define SEGM_PER_CIRCLE 24 - -#ifndef CALLBACK -#define CALLBACK -#endif - -// Variables used to pass a value to call back openGL functions -static float s_textureScale; -static double s_currentZpos; -static double s_biuTo3Dunits; -bool s_useTextures; - -// CALLBACK functions for GLU_TESS -static void CALLBACK tessBeginCB( GLenum which ); -static void CALLBACK tessEndCB(); -static void CALLBACK tessErrorCB( GLenum errorCode ); -static void CALLBACK tessCPolyPt2Vertex( const GLvoid* data ); - -void TransfertToGLlist( std::vector< S3D_VERTEX >& aVertices, double aBiuTo3DUnits ); - -/* Draw3D_VerticalPolygonalCylinder is a helper function. - * - * draws a "vertical cylinder" having a polygon shape - * from Z position = aZpos to aZpos + aHeight - * Used to create the vertical sides of 3D horizontal shapes with thickness. - */ -static void Draw3D_VerticalPolygonalCylinder( const SHAPE_POLY_SET& aPolysList, - int aHeight, int aZpos, - bool aInside, double aBiuTo3DUnits ) -{ - if( aHeight == 0 ) - return; - - std::vector coords; - coords.resize( 4 ); - - // Init Z position of the 4 points of a GL_QUAD - if( aInside ) - { - coords[0].z = aZpos; - coords[1].z = aZpos + aHeight; - } - else - { - coords[0].z = aZpos + aHeight; - coords[1].z = aZpos; - } - coords[2].z = coords[1].z; - coords[3].z = coords[0].z; - - // Draw the vertical polygonal side - for( int idx = 0; idx < aPolysList.OutlineCount(); idx++ ) - { - // Each polygon in aPolysList is a polygon with holes - const SHAPE_POLY_SET::POLYGON& curr_polywithholes = aPolysList.CPolygon( idx ); - - // Draw the outline of each simple polygon inside the polygon with holes: - for( unsigned ipoly = 0; ipoly < curr_polywithholes.size(); ipoly++ ) - { - const SHAPE_LINE_CHAIN& path = curr_polywithholes[ipoly]; // a simple polygon - - for( int jj = 0; jj < path.PointCount(); jj++ ) - { - const VECTOR2I& a = path.CPoint( jj ); - const VECTOR2I& b = path.CPoint( jj + 1 ); - - // Build the 4 vertices of each GL_QUAD - coords[0].x = a.x; - coords[0].y = -a.y; - coords[1].x = coords[0].x; - coords[1].y = coords[0].y; // only z change - coords[2].x = b.x; - coords[2].y = -b.y; - coords[3].x = coords[2].x; - coords[3].y = coords[2].y; // only z change - - // Creates the GL_QUAD - TransfertToGLlist( coords, aBiuTo3DUnits ); - } - } - } -} - - -void SetGLColor( EDA_COLOR_T color, double alpha ) -{ - const StructColors &colordata = g_ColorRefs[ColorGetBase( color )]; - - float red = colordata.m_Red / 255.0; - float blue = colordata.m_Blue / 255.0; - float green = colordata.m_Green / 255.0; - glColor4f( red, green, blue, (float)alpha ); -} - - -void SetGLColor( S3D_COLOR& aColor, float aTransparency ) -{ - glColor4f( aColor.m_Red, aColor.m_Green, aColor.m_Blue, aTransparency ); -} - - -void SetGLTexture( GLuint text_id, float scale ) -{ - glEnable( GL_TEXTURE_2D ); - glBindTexture( GL_TEXTURE_2D, text_id ); - s_textureScale = scale; // for Tess callback functions -} - - -/* draw all solid polygons found in aPolysList - * aZpos = z position in board internal units - * aThickness = thickness in board internal units - * If aThickness = 0, a polygon area is drawn in a XY plane at Z position = aZpos. - * If aThickness > 0, a solid object is drawn. - * The top side is located at aZpos + aThickness / 2 - * The bottom side is located at aZpos - aThickness / 2 - */ -void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList, - int aZpos, int aThickness, double aBiuTo3DUnits, - bool aUseTextures, - float aNormal_Z_Orientation ) -{ - // for Tess callback functions: - s_biuTo3Dunits = aBiuTo3DUnits; - s_useTextures = aUseTextures; - - GLUtesselator* tess = gluNewTess(); - - gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )tessBeginCB ); - gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )tessEndCB ); - gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )tessErrorCB ); - gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )tessCPolyPt2Vertex ); - - GLdouble v_data[3]; - double zpos = ( aZpos + (aThickness / 2.0) ) * aBiuTo3DUnits; - s_currentZpos = zpos; // for Tess callback functions - v_data[2] = zpos; - - // Set normal toward positive Z axis, for a solid object on the top side - - //gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE ); - gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD ); - - glNormal3f( 0.0, 0.0, aNormal_Z_Orientation ); - - // Draw solid areas contained in this list - SHAPE_POLY_SET polylist = aPolysList; // temporary copy for gluTessVertex - - for( int side = 0; side < 2; side++ ) - { - - for ( int idx = 0; idx < polylist.OutlineCount(); ++idx ) - { - gluTessBeginPolygon( tess, NULL ); - - SHAPE_POLY_SET::POLYGON& curr_polywithholes = polylist.Polygon( idx ); // a polygon with holes - - for( unsigned ipoly = 0; ipoly < curr_polywithholes.size(); ipoly++ ) - { - SHAPE_LINE_CHAIN& curr_poly = curr_polywithholes[ipoly]; // a simple polygon - - gluTessBeginContour( tess ); - - for( int ipt = 0; ipt < curr_poly.PointCount(); ipt++ ) - { - v_data[0] = curr_poly.Point( ipt ).x * aBiuTo3DUnits; - v_data[1] = -curr_poly.Point( ipt ).y * aBiuTo3DUnits; - // gluTessVertex store pointers on data, not data, so do not store - // different corners values in a temporary variable - // but send pointer on each CPolyPt value in polylist - // before calling gluDeleteTess - gluTessVertex( tess, v_data, &curr_poly.Point( ipt ) ); - } - - gluTessEndContour( tess ); - } - - gluTessEndPolygon( tess ); - } - - - if( aThickness == 0 ) - break; - - // Prepare the bottom side of solid areas - zpos = ( aZpos - (aThickness / 2.0) ) * aBiuTo3DUnits; - s_currentZpos = zpos; // for Tess callback functions - v_data[2] = zpos; - - glNormal3f( 0.0, 0.0, -aNormal_Z_Orientation ); - } - - gluDeleteTess( tess ); - - if( aThickness == 0 ) - return; - - // Build the 3D data : vertical side - Draw3D_VerticalPolygonalCylinder( polylist, aThickness, aZpos - (aThickness / 2.0), - true, aBiuTo3DUnits ); -} - - -/* draw a cylinder (a tube) using 3D primitives. - * the cylinder axis is parallel to the Z axis - * If aHeight = height of the cylinder is 0, only one ring will be drawn - * If aThickness = 0, only one cylinder will be drawn - */ -void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius, - int aHeight, int aThickness, - int aZpos, double aBiuTo3DUnits ) -{ - const int slice = SEGM_PER_CIRCLE; - SHAPE_POLY_SET outer_cornerBuffer; - - TransformCircleToPolygon( outer_cornerBuffer, aCenterPos, - aRadius + (aThickness / 2), slice ); - - std::vector coords; - coords.resize( 4 ); - - SHAPE_POLY_SET inner_cornerBuffer; - - if( aThickness ) // build the the vertical inner polygon (hole) - TransformCircleToPolygon( inner_cornerBuffer, aCenterPos, - aRadius - (aThickness / 2), slice ); - - if( aHeight ) - { - - // Draw the vertical outer side - Draw3D_VerticalPolygonalCylinder( outer_cornerBuffer, - aHeight, aZpos, false, aBiuTo3DUnits ); - - if( aThickness ) - // Draws the vertical inner side (hole) - Draw3D_VerticalPolygonalCylinder( inner_cornerBuffer, - aHeight, aZpos, true, aBiuTo3DUnits ); - } - - if( aThickness ) - { - // draw top (front) and bottom (back) horizontal sides (rings) - outer_cornerBuffer.AddHole( inner_cornerBuffer.COutline( 0 ) ); - - // draw top (front) horizontal ring - Draw3D_SolidHorizontalPolyPolygons( outer_cornerBuffer, aZpos + aHeight, - 0, aBiuTo3DUnits, false, 1.0f ); - - if( aHeight ) - { - // draw bottom (back) horizontal ring - Draw3D_SolidHorizontalPolyPolygons( outer_cornerBuffer, aZpos, - 0, aBiuTo3DUnits, false, -1.0f ); - } - } -} - - -/* - * Function Draw3D_ZaxisOblongCylinder: - * draw a segment with an oblong hole. - * Used to draw oblong holes - * If aHeight = height of the cylinder is 0, only one ring will be drawn - * If aThickness = 0, only one cylinder will be drawn - */ -void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos, - int aRadius, int aHeight, int aThickness, - int aZpos, double aBiuTo3DUnits ) -{ - const int slice = SEGM_PER_CIRCLE; - - // Build the points to approximate oblong cylinder by segments - SHAPE_POLY_SET cornerBuffer; - - int segm_width = (aRadius * 2) + aThickness; - TransformRoundedEndsSegmentToPolygon( cornerBuffer, aAxis1Pos, - aAxis2Pos, slice, segm_width ); - - // Draw the oblong outer cylinder - if( aHeight ) - Draw3D_VerticalPolygonalCylinder( cornerBuffer, aHeight, aZpos, - false, aBiuTo3DUnits ); - - if( aThickness ) - { - SHAPE_POLY_SET inner_cornerBuffer; - segm_width = aRadius * 2; - TransformRoundedEndsSegmentToPolygon( inner_cornerBuffer, aAxis1Pos, - aAxis2Pos, slice, segm_width ); - - // Draw the oblong inner cylinder - if( aHeight ) - Draw3D_VerticalPolygonalCylinder( inner_cornerBuffer, aHeight, - aZpos, true, aBiuTo3DUnits ); - - // Build the horizontal full polygon shape - // (outer polygon shape - inner polygon shape) - cornerBuffer.AddHole( inner_cornerBuffer.COutline( 0 ) ); - - // draw top (front) horizontal side (ring) - Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos + aHeight, 0, aBiuTo3DUnits, false, - 1.0f ); - - if( aHeight ) - { - // draw bottom (back) horizontal side (ring) - Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, 0, aBiuTo3DUnits, false, - -1.0f ); - } - } -} - - -/* draw a thick segment using 3D primitives, in a XY plane - * wxPoint aStart, wxPoint aEnd = YX position of end in board units - * aWidth = width of segment in board units - * aThickness = thickness of segment in board units - * aZpos = z position of segment in board units - */ -void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd, - int aWidth, int aThickness, int aZpos, double aBiuTo3DUnits ) -{ - SHAPE_POLY_SET cornerBuffer; - const int slice = SEGM_PER_CIRCLE; - - TransformRoundedEndsSegmentToPolygon( cornerBuffer, aStart, aEnd, slice, aWidth ); - - Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, aThickness, aBiuTo3DUnits, false, 1.0f ); -} - - -void Draw3D_ArcSegment( const wxPoint& aCenterPos, const wxPoint& aStartPoint, - double aArcAngle, int aWidth, int aThickness, - int aZpos, double aBiuTo3DUnits ) -{ - const int slice = SEGM_PER_CIRCLE; - - SHAPE_POLY_SET cornerBuffer; - TransformArcToPolygon( cornerBuffer, aCenterPos, aStartPoint, aArcAngle, - slice, aWidth ); - - Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, aThickness, aBiuTo3DUnits, false, 1.0f ); -} - - -// ///////////////////////////////////////////////////////////////////////////// -// GLU_TESS CALLBACKS -// ///////////////////////////////////////////////////////////////////////////// - -void CALLBACK tessBeginCB( GLenum which ) -{ - glBegin( which ); -} - - -void CALLBACK tessEndCB() -{ - glEnd(); -} - - -void CALLBACK tessCPolyPt2Vertex( const GLvoid* data ) -{ - // cast back to double type - const VECTOR2I* ptr = (const VECTOR2I*) data; - - if( s_useTextures ) - { - glTexCoord2f( ptr->x * s_biuTo3Dunits * s_textureScale, - -ptr->y * s_biuTo3Dunits * s_textureScale); - } - - glVertex3d( ptr->x * s_biuTo3Dunits, -ptr->y * s_biuTo3Dunits, s_currentZpos ); -} - - -void CALLBACK tessErrorCB( GLenum errorCode ) -{ -#if defined(DEBUG) - const GLubyte* errorStr; - - errorStr = gluErrorString( errorCode ); - - // DEBUG // - DBG( printf( "Tess ERROR: %s\n", errorStr ); ) -#endif -} diff --git a/3d-viewer/3d_draw_basic_functions.h b/3d-viewer/3d_draw_basic_functions.h deleted file mode 100644 index f0063b70f1..0000000000 --- a/3d-viewer/3d_draw_basic_functions.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_draw_basic_functions.h - */ -#ifndef _3D_DRAW_BASIC_FUNCTIONS_H_ -#define _3D_DRAW_BASIC_FUNCTIONS_H_ - -// angle increment to draw a circle, approximated by segments -#define ANGLE_INC( x ) ( 3600 / (x) ) - -/** draw all solid polygons found in aPolysList - * @param aPolysList = the poligon list to draw - * @param aZpos = z position in board internal units - * @param aThickness = thickness in board internal units - * @param aBiuTo3DUnits = board internal units to 3D units scaling value - * @param aUseTextures = true to use textxures for the polygons - * @param aNormal_Z_Orientation = the normal Z orientation to apply - * If aThickness = 0, a polygon area is drawn in a XY plane at Z position = aZpos. - * If aThickness > 0, a solid object is drawn. - * The top side is located at aZpos + aThickness / 2 - * The bottom side is located at aZpos - aThickness / 2 - */ -void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList, - int aZpos, int aThickness, double aBiuTo3DUnits, - bool aUseTextures, - float aNormal_Z_Orientation ); - -/** draw a thick segment using 3D primitives, in a XY plane - * @param aStart = YX position of start point in board units - * @param aEnd = YX position of end point in board units - * @param aWidth = width of segment in board units - * @param aThickness = thickness of segment in board units - * @param aZpos = z position of segment in board units - * @param aBiuTo3DUnits = board internal units to 3D units scaling value - * If aThickness = 0, a polygon area is drawn in a XY plane at Z position = aZpos. - * If aThickness > 0, a solid object is drawn. - * The top side is located at aZpos + aThickness / 2 - * The bottom side is located at aZpos - aThickness / 2 - */ -void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd, - int aWidth, int aThickness, int aZpos, - double aBiuTo3DUnits ); - -/** draw an arc using 3D primitives, in a XY plane - * @param aCenterPos = XY position of the center in board units - * @param aStartPoint = start point coordinate of arc in board units - * @param aWidth = width of the circle in board units - * @param aArcAngle = arc angle in 1/10 degrees - * @param aWidth = thickness of arc - * @param aThickness = thickness of segment in board units - * @param aZpos = z position of segment in board units - * @param aBiuTo3DUnits = board internal units to 3D units scaling value - */ -void Draw3D_ArcSegment( const wxPoint& aCenterPos, const wxPoint& aStartPoint, - double aArcAngle, int aWidth, int aThickness, - int aZpos, double aBiuTo3DUnits ); - - -/** draw a thick cylinder (a tube) using 3D primitives. - * the cylinder axis is parallel to the Z axis - * @param aCenterPos = XY position of the axis cylinder ( board internal units) - * @param aRadius = radius of the cylinder ( board internal units) - * @param aHeight = height of the cylinder ( boardinternal units) - * @param aThickness = tichkness of tube ( boardinternal units) - * @param aZpos = Z position of the bottom side of the cylinder ( board internal units) - * @param aBiuTo3DUnits = board internal units to 3D units scaling value - * - * If aHeight = height of the cylinder is 0, only one ring will be drawn - * If aThickness = 0, only one cylinder (not a tube) will be drawn - */ -void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius, - int aHeight, int aThickness, - int aZpos, double aBiuTo3DUnits ); - -/** draw an oblong cylinder (oblong tube) using 3D primitives. - * the cylinder axis are parallel to the Z axis - * @param aAxis1Pos = position of the first axis cylinder - * @param aAxis2Pos = position of the second axis cylinder - * @param aRadius = radius of the cylinder ( board internal units ) - * @param aHeight = height of the cylinder ( board internal units ) - * @param aThickness = tichkness of tube ( board internal units ) - * @param aZpos = Z position of the bottom side of the cylinder ( board internal units ) - * @param aBiuTo3DUnits = board internal units to 3D units scaling value - */ -void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos, - int aRadius, int aHeight, int aThickness, - int aZpos, double aBiuTo3DUnits ); -/** - * Set the current 3D color from a Kicad color, with optional transparency - * @param aColor = a EDA_COLOR_T kicad color index - * @param aTransparency = the color transparency (default = 1.0 = no transparency) - */ -void SetGLColor( EDA_COLOR_T aColor, double aTransparency = 1.0 ); - -/** - * Set the current 3D color from a S3D_COLOR color, with optional transparency - * @param aColor = a S3D_COLOR RGB color index - * @param aTransparency = the color transparency (default = 1.0 = no transparency) - */ -void SetGLColor( S3D_COLOR& aColor, float aTransparency ); - - -/** - * Set a texture id and a scale to apply when rendering the polygons - * @param text_id = texture ID created by glGenTextures - * @param scale = scale to apply to texture coords - */ -void SetGLTexture( GLuint text_id, float scale ); - - -#endif // _3D_DRAW_BASIC_FUNCTIONS_H_ diff --git a/3d-viewer/3d_draw_board_body.cpp b/3d-viewer/3d_draw_board_body.cpp deleted file mode 100644 index 722098f545..0000000000 --- a/3d-viewer/3d_draw_board_body.cpp +++ /dev/null @@ -1,640 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_draw_board_body.cpp - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define GLM_FORCE_RADIANS -#include -#include -#ifdef __WINDOWS__ -#include // must be included before gl.h -#endif - -#include <3d_viewer.h> -#include <3d_canvas.h> -#include -#include -#include <3d_draw_basic_functions.h> -#include -#include - - -#include -#include - -// An option for all operations on polygons: -// when polygonsCalcMode = true, calculations can be *a lot* faster. -// but created polygons can be not stricty simple (can share edges) -// Although stricty simple are better for glu tesselation functions, I do not see -// any issue when allowing not stricty simple polygons. -// But I see *very* long calculations when setting useFastMode to false. -// So, be careful if changing thie option -SHAPE_POLY_SET::POLYGON_MODE polygonsCalcMode = SHAPE_POLY_SET::PM_FAST; - -/* returns the Z orientation parameter 1.0 or -1.0 for aLayer - * Z orientation is 1.0 for all layers but "back" layers: - * B_Cu , B_Adhes, B_Paste ), B_SilkS - * used to calculate the Z orientation parameter for glNormal3f - */ -GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer ); - -void EDA_3D_CANVAS::buildBoardThroughHolesPolygonList( SHAPE_POLY_SET& allBoardHoles, - int aSegCountPerCircle, bool aOptimizeLargeCircles ) -{ - // hole diameter value to change seg count by circle: - int small_hole_limit = Millimeter2iu( 1.0 ); - int copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU(); - - BOARD* pcb = GetBoard(); - - // Build holes of through vias: - for( TRACK* track = pcb->m_Track; track; track = track->Next() ) - { - if( track->Type() != PCB_VIA_T ) - continue; - - VIA *via = static_cast( track ); - - if( via->GetViaType() != VIA_THROUGH ) - continue; - - int holediameter = via->GetDrillValue(); - int hole_outer_radius = (holediameter + copper_thickness) / 2; - - TransformCircleToPolygon( allBoardHoles, - via->GetStart(), hole_outer_radius, - aSegCountPerCircle ); - } - - // Build holes of through pads: - for( MODULE* footprint = pcb->m_Modules; footprint; footprint = footprint->Next() ) - { - for( D_PAD* pad = footprint->Pads(); pad; pad = pad->Next() ) - { - // Calculate a factor to apply to segcount for large holes ( > 1 mm) - // (bigger pad drill size -> more segments) because holes in pads can have - // very different sizes and optimizing this segcount gives a better look - // Mainly mounting holes have a size bigger than small_hole_limit - wxSize padHole = pad->GetDrillSize(); - - if( ! padHole.x ) // Not drilled pad like SMD pad - continue; - - // we use the hole diameter to calculate the seg count. - // for round holes, padHole.x == padHole.y - // for oblong holes, the diameter is the smaller of (padHole.x, padHole.y) - int diam = std::min( padHole.x, padHole.y ); - int segcount = aSegCountPerCircle; - - if( diam > small_hole_limit ) - { - double segFactor = (double)diam / small_hole_limit; - segcount = (int)(aSegCountPerCircle * segFactor); - - // limit segcount to 48. For a circle this is a very good approx. - if( segcount > 48 ) - segcount = 48; - } - - // The hole in the body is inflated by copper thickness. - int inflate = copper_thickness; - - // If not plated, no copper. - if( pad->GetAttribute () == PAD_ATTRIB_HOLE_NOT_PLATED ) - inflate = 0; - - pad->BuildPadDrillShapePolygon( allBoardHoles, inflate, segcount ); - } - } - - allBoardHoles.Simplify( polygonsCalcMode ); -} - - -void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, - REPORTER* aErrorMessages, REPORTER* aActivity ) -{ - BOARD* pcb = GetBoard(); - - // If FL_RENDER_SHOW_HOLES_IN_ZONES is true, holes are correctly removed from copper zones areas. - // If FL_RENDER_SHOW_HOLES_IN_ZONES is false, holes are not removed from copper zones areas, - // but the calculation time is twice shorter. - bool remove_Holes = isEnabled( FL_RENDER_SHOW_HOLES_IN_ZONES ); - - bool realistic_mode = isRealisticMode(); - bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ); - - // Number of segments to convert a circle to polygon - // We use 2 values: the first gives a good shape (for instanes rond pads) - // the second is used to speed up calculations, when a poor approximation is acceptable (holes) - const int segcountforcircle = 18; - double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2.0) ); - const int segcountLowQuality = 12; // segments to draw a circle with low quality - // to reduce time calculations - // for holes and items which do not need - // a fine representation - double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) ); - - SHAPE_POLY_SET bufferPolys; // copper areas: tracks, pads and filled zones areas - // when holes are removed from zones - SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines - SHAPE_POLY_SET bufferZonesPolys; // copper filled zones areas - // when holes are not removed from zones - SHAPE_POLY_SET currLayerHoles; // Contains holes for the current layer - SHAPE_POLY_SET allLayerHoles; // Contains holes for all layers - - // Build a polygon from edge cut items - wxString msg; - - if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) ) - { - if( aErrorMessages ) - { - msg << wxT("\n") << _("Unable to calculate the board outlines.\n" - "Therefore use the board boundary box.") << wxT("\n\n"); - - aErrorMessages->Report( msg, REPORTER::RPT_WARNING ); - } - } - - // Build board holes, with optimization of large holes shape. - buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, true ); - - LSET cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount ); - - glNewList( aBoardList, GL_COMPILE ); - - for( LSEQ cu = cu_set.CuStack(); cu; ++cu ) - { - LAYER_ID layer = *cu; - - // Skip non enabled layers in normal mode, - // and internal layers in realistic mode - if( !is3DLayerEnabled( layer ) ) - continue; - - if( aActivity ) - aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); - - bufferPolys.RemoveAllContours(); - bufferZonesPolys.RemoveAllContours(); - currLayerHoles.RemoveAllContours(); - - // Draw track shapes: - for( TRACK* track = pcb->m_Track; track; track = track->Next() ) - { - if( !track->IsOnLayer( layer ) ) - continue; - - track->TransformShapeWithClearanceToPolygon( bufferPolys, - 0, segcountforcircle, - correctionFactor ); - - // Add blind/buried via holes - if( track->Type() == PCB_VIA_T ) - { - VIA *via = static_cast( track ); - - if( via->GetViaType() == VIA_THROUGH ) - continue; // already done - - int holediameter = via->GetDrillValue(); - int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); - int hole_outer_radius = (holediameter + thickness) / 2; - - TransformCircleToPolygon( currLayerHoles, - via->GetStart(), hole_outer_radius, - segcountLowQuality ); - } - } - - // draw pad shapes - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - { - // Note: NPTH pads are not drawn on copper layers when the pad - // has same shape as its hole - module->TransformPadsShapesWithClearanceToPolygon( layer, - bufferPolys, - 0, - segcountforcircle, - correctionFactor, true ); - - // Micro-wave modules may have items on copper layers - module->TransformGraphicShapesWithClearanceToPolygonSet( layer, - bufferPolys, - 0, - segcountforcircle, - correctionFactor ); - - // pad holes are already in list. - } - - // Draw copper zones. Note: - // * if the holes are removed from copper zones - // the polygons are stored in bufferPolys (which contains all other polygons) - // * if the holes are NOT removed from copper zones - // the polygons are stored in bufferZonesPolys - if( isEnabled( FL_ZONE ) ) - { - for( int ii = 0; ii < pcb->GetAreaCount(); ii++ ) - { - ZONE_CONTAINER* zone = pcb->GetArea( ii ); - LAYER_NUM zonelayer = zone->GetLayer(); - - if( zonelayer == layer ) - { - zone->TransformSolidAreasShapesToPolygonSet( - remove_Holes ? bufferPolys : bufferZonesPolys, - segcountLowQuality, correctionFactorLQ ); - } - } - } - - // draw graphic items on copper layers (texts) - for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) - { - if( !item->IsOnLayer( layer ) ) - continue; - - switch( item->Type() ) - { - case PCB_LINE_T: // should not exist on copper layers - ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( - bufferPolys, 0, segcountforcircle, correctionFactor ); - break; - - case PCB_TEXT_T: - ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( - bufferPolys, 0, segcountLowQuality, correctionFactor ); - break; - - default: - break; - } - } - - // bufferPolys contains polygons to merge. Many overlaps . - // Calculate merged polygons - if( bufferPolys.IsEmpty() ) - continue; - - // Use Clipper lib to subtract holes to copper areas - if( currLayerHoles.OutlineCount() ) - { - currLayerHoles.Append(allLayerHoles); - currLayerHoles.Simplify( polygonsCalcMode ); - bufferPolys.BooleanSubtract( currLayerHoles, polygonsCalcMode ); - } - else - bufferPolys.BooleanSubtract( allLayerHoles, polygonsCalcMode ); - - int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); - int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); - - float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted) - - // If we are not using thickness, then the z-normal has to match the layer direction - // because just one plane will be drawn - if( !thickness ) - zNormal = Get3DLayer_Z_Orientation( layer ); - - if( realistic_mode ) - { - setGLCopperColor(); - } - else - { - EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer ); - SetGLColor( color ); - } - - // If holes are removed from copper zones, bufferPolys contains all polygons - // to draw (tracks+zones+texts). - Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness, - GetPrm3DVisu().m_BiuTo3Dunits, useTextures, - zNormal ); - - // If holes are not removed from copper zones (for calculation time reasons, - // the zone polygons are stored in bufferZonesPolys and have to be drawn now: - if( !bufferZonesPolys.IsEmpty() ) - { - Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness, - GetPrm3DVisu().m_BiuTo3Dunits, useTextures, - zNormal ); - } - } - - if( aActivity ) - aActivity->Report( _( "Build board body" ) ); - - // Draw plated vertical holes inside the board, but not always. They are drawn: - // - if the board body is not shown, to show the holes. - // - or if the copper thickness is shown - if( !isEnabled( FL_SHOW_BOARD_BODY ) || isEnabled( FL_USE_COPPER_THICKNESS ) ) - { - // Draw vias holes (vertical cylinders) - for( const TRACK* track = pcb->m_Track; track; track = track->Next() ) - { - if( track->Type() == PCB_VIA_T ) - { - const VIA *via = static_cast(track); - draw3DViaHole( via ); - } - } - - // Draw pads holes (vertical cylinders) - for( const MODULE* module = pcb->m_Modules; module; module = module->Next() ) - { - for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) - if( pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED ) - draw3DPadHole( pad ); - } - } - - glEndList(); - - // Build the body board: - glNewList( aBodyOnlyList, GL_COMPILE ); - - if( isRealisticMode() ) - { - setGLEpoxyColor( 1.00 ); - } - else - { - EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( Edge_Cuts ); - SetGLColor( color, 0.7 ); - } - - float copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU(); - - // a small offset between substrate and external copper layer to avoid artifacts - // when drawing copper items on board - float epsilon = Millimeter2iu( 0.01 ); - float zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); - float board_thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) - - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); - - // items on copper layers and having a thickness = copper_thickness - // are drawn from zpos - copper_thickness/2 to zpos + copper_thickness - // therefore substrate position is copper_thickness/2 to - // substrate_height - copper_thickness/2 - zpos += (copper_thickness + epsilon) / 2.0f; - board_thickness -= copper_thickness + epsilon; - - bufferPcbOutlines.BooleanSubtract( allLayerHoles, polygonsCalcMode ); - - if( !bufferPcbOutlines.IsEmpty() ) - { - Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0, - board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, - 1.0f ); - } - - glEndList(); -} - - -void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* aActivity ) -{ - BOARD* pcb = GetBoard(); - bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ); - - // Number of segments to draw a circle using segments - const int segcountforcircle = 18; - double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) ); - const int segcountLowQuality = 12; // segments to draw a circle with low quality - // to reduce time calculations - // for holes and items which do not need - // a fine representation - - double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2) ); - - // segments to draw a circle to build texts. Is is used only to build - // the shape of each segment of the stroke font, therefore no need to have - // many segments per circle. - const int segcountInStrokeFont = 8; - - SHAPE_POLY_SET bufferPolys; - SHAPE_POLY_SET allLayerHoles; // Contains through holes, calculated only once - SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines - - // Build a polygon from edge cut items - wxString msg; - - if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) ) - { - if( aErrorMessages ) - { - msg << wxT("\n") << - _("Unable to calculate the board outlines.\n" - "Therefore use the board boundary box.") << wxT("\n\n"); - aErrorMessages->Report( msg, REPORTER::RPT_WARNING ); - } - } - - // Build board holes, with no optimization of large holes shape. - buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, false ); - - // draw graphic items, on technical layers - - static const LAYER_ID teckLayerList[] = { - B_Adhes, - F_Adhes, - B_Paste, - F_Paste, - B_SilkS, - F_SilkS, - B_Mask, - F_Mask, - }; - - // User layers are not drawn here, only technical layers - for( LSEQ seq = LSET::AllTechMask().Seq( teckLayerList, DIM( teckLayerList ) ); seq; ++seq ) - { - LAYER_ID layer = *seq; - - if( !is3DLayerEnabled( layer ) ) - continue; - - if( layer == Edge_Cuts && isEnabled( FL_SHOW_BOARD_BODY ) ) - continue; - - if( aActivity ) - aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); - - bufferPolys.RemoveAllContours(); - - for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) - { - if( !item->IsOnLayer( layer ) ) - continue; - - switch( item->Type() ) - { - case PCB_LINE_T: - ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( - bufferPolys, 0, segcountforcircle, correctionFactor ); - break; - - case PCB_TEXT_T: - ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( - bufferPolys, 0, segcountLowQuality, 1.0 ); - break; - - default: - break; - } - } - - for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) - { - if( layer == F_SilkS || layer == B_SilkS ) - { - // On silk screen layers, the pad shape is only the pad outline - // never a filled shape - D_PAD* pad = module->Pads(); - int linewidth = g_DrawDefaultLineThickness; - - for( ; pad; pad = pad->Next() ) - { - if( !pad->IsOnLayer( layer ) ) - continue; - - buildPadShapeThickOutlineAsPolygon( pad, bufferPolys, - linewidth, segcountforcircle, correctionFactor ); - } - } - else - module->TransformPadsShapesWithClearanceToPolygon( layer, - bufferPolys, 0, segcountforcircle, correctionFactor ); - - // On tech layers, use a poor circle approximation, only for texts (stroke font) - module->TransformGraphicShapesWithClearanceToPolygonSet( layer, - bufferPolys, 0, segcountforcircle, correctionFactor, segcountInStrokeFont ); - } - - // Draw non copper zones - if( isEnabled( FL_ZONE ) ) - { - for( int ii = 0; ii < pcb->GetAreaCount(); ii++ ) - { - ZONE_CONTAINER* zone = pcb->GetArea( ii ); - - if( !zone->IsOnLayer( layer ) ) - continue; - - zone->TransformSolidAreasShapesToPolygonSet( - bufferPolys, segcountLowQuality, correctionFactorLQ ); - } - } - - // bufferPolys contains polygons to merge. Many overlaps . - // Calculate merged polygons and remove pads and vias holes - if( layer != B_Mask && layer != F_Mask && bufferPolys.IsEmpty() ) - // if a layer has no item to draw, skip it - // However solder mask layers are negative layers, so no item - // means only a full layer mask - continue; - - // Solder mask layers are "negative" layers. - // Shapes should be removed from the full board area. - if( layer == B_Mask || layer == F_Mask ) - { - SHAPE_POLY_SET cuts = bufferPolys; - bufferPolys = bufferPcbOutlines; - - cuts.Append(allLayerHoles); - cuts.Simplify( polygonsCalcMode ); - - bufferPolys.BooleanSubtract( cuts, polygonsCalcMode ); - } - // Remove holes from Solder paste layers and silkscreen - else if( layer == B_Paste || layer == F_Paste - || layer == B_SilkS || layer == F_SilkS ) - { - bufferPolys.BooleanSubtract( allLayerHoles, polygonsCalcMode ); - } - - int thickness = 0; - - if( layer != B_Mask && layer != F_Mask ) - thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); - - int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); - - if( layer == Edge_Cuts ) - { - thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) - - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); - zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ) - + (thickness / 2); - } - else - { - // for Draw3D_SolidHorizontalPolyPolygons, zpos it the middle between bottom and top - // sides. - // However for top layers, zpos should be the bottom layer pos, - // and for bottom layers, zpos should be the top layer pos. - if( Get3DLayer_Z_Orientation( layer ) > 0 ) - zpos += thickness/2; - else - zpos -= thickness/2 ; - } - - - float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted) - - // If we are not using thickness, then the znormal must face the layer direction - // because it will draw just one plane - if( !thickness ) - zNormal = Get3DLayer_Z_Orientation( layer ); - - - setGLTechLayersColor( layer ); - Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, - thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, - zNormal ); - } -} diff --git a/3d-viewer/3d_draw_helper_functions.cpp b/3d-viewer/3d_draw_helper_functions.cpp deleted file mode 100644 index 34fc009604..0000000000 --- a/3d-viewer/3d_draw_helper_functions.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_draw_helper_functions.cpp - */ - -#include - -#include -#include -#include -#include -#include - -#include - -#include <3d_viewer.h> -#include <3d_canvas.h> -#include -#include <3d_draw_basic_functions.h> - -#define TEXTURE_PCB_SCALE 5.0 - -// ----------------- -// helper function (from wxWidgets, opengl/cube.cpp sample -// ----------------- -void CheckGLError(const char *aFileName, int aLineNumber) -{ - GLenum errLast = GL_NO_ERROR; - - for ( ; ; ) - { - GLenum err = glGetError(); - if ( err == GL_NO_ERROR ) - return; - - // normally the error is reset by the call to glGetError() but if - // glGetError() itself returns an error, we risk looping forever here - // so check that we get a different error than the last time - if ( err == errLast ) - { - wxLogError(wxT("OpenGL error state couldn't be reset.")); - return; - } - - errLast = err; - - wxLogError( wxT( "OpenGL error %d At: %s, line: %d" ), err, - GetChars( FROM_UTF8( aFileName ) ), aLineNumber ); - } -} - - -INFO3D_VISU& EDA_3D_CANVAS::GetPrm3DVisu() const -{ - return Parent()->GetPrm3DVisu(); -} - -wxSize EDA_3D_CANVAS::getBoardSize() const -{ - // return the size of the board in pcb units - return GetPrm3DVisu().m_BoardSize; -} - - -wxPoint EDA_3D_CANVAS::getBoardCenter() const -{ - // return the position of the board center in pcb units - return GetPrm3DVisu().m_BoardPos; -} - -// return true if we are in realistic mode render -bool EDA_3D_CANVAS::isRealisticMode() const -{ - return GetPrm3DVisu().IsRealisticMode(); -} - -// return true if aItem should be displayed -bool EDA_3D_CANVAS::isEnabled( DISPLAY3D_FLG aItem ) const -{ - return GetPrm3DVisu().GetFlag( aItem ); -} - - -// Helper function: initialize the copper color to draw the board -// in realistic mode. -void EDA_3D_CANVAS::setGLCopperColor() -{ - glDisable( GL_TEXTURE_2D ); - SetGLColor( GetPrm3DVisu().m_CopperColor, 1.0 ); -} - -// Helper function: initialize the color to draw the epoxy -// body board in realistic mode. -void EDA_3D_CANVAS::setGLEpoxyColor( float aTransparency ) -{ - // Generates an epoxy color, near board color - SetGLColor( GetPrm3DVisu().m_BoardBodyColor, aTransparency ); - - if( isEnabled( FL_RENDER_TEXTURES ) ) - { - SetGLTexture( m_text_pcb, TEXTURE_PCB_SCALE ); - } -} - -// Helper function: initialize the color to draw the -// solder mask layers in realistic mode. -void EDA_3D_CANVAS::setGLSolderMaskColor( float aTransparency ) -{ - // Generates a solder mask color - SetGLColor( GetPrm3DVisu().m_SolderMaskColor, aTransparency ); - - if( isEnabled( FL_RENDER_TEXTURES ) ) - { - SetGLTexture( m_text_pcb, TEXTURE_PCB_SCALE ); - } -} - -// Helper function: initialize the color to draw the non copper layers -// in realistic mode and normal mode. -void EDA_3D_CANVAS::setGLTechLayersColor( LAYER_NUM aLayer ) -{ - EDA_COLOR_T color; - - if( isRealisticMode() ) - { - switch( aLayer ) - { - case B_Paste: - case F_Paste: - SetGLColor( GetPrm3DVisu().m_SolderPasteColor, 1 ); - break; - - case B_SilkS: - case F_SilkS: - SetGLColor( GetPrm3DVisu().m_SilkScreenColor, 0.96 ); - - if( isEnabled( FL_RENDER_TEXTURES ) ) - { - SetGLTexture( m_text_silk, 10.0f ); - } - - break; - - case B_Mask: - case F_Mask: - setGLSolderMaskColor( 0.90 ); - break; - - default: - color = g_ColorsSettings.GetLayerColor( aLayer ); - SetGLColor( color, 0.7 ); - break; - } - } - else - { - color = g_ColorsSettings.GetLayerColor( aLayer ); - SetGLColor( color, 0.7 ); - } -} - -void EDA_3D_CANVAS::draw3DAxis() -{ - if( ! m_glLists[GL_ID_AXIS] ) - { - m_glLists[GL_ID_AXIS] = glGenLists( 1 ); - glNewList( m_glLists[GL_ID_AXIS], GL_COMPILE ); - - glEnable( GL_COLOR_MATERIAL ); - glBegin( GL_LINES ); - SetGLColor( RED ); - glNormal3f( 0.0f, 0.0f, 1.0f ); // Normal is Z axis - glVertex3f( 0.0f, 0.0f, 0.0f ); - glVertex3f( -10.0f, 0.0f, 0.0f ); - glVertex3f( 0.0f, 0.0f, 0.0f ); - glVertex3f( 10.0f, 0.0f, 0.0f ); // X axis - SetGLColor( GREEN ); - glVertex3f( 0.0f, 0.0f, 0.0f ); - glVertex3f( 0.0f, -10.0f, 0.0f ); // Y axis - glVertex3f( 0.0f, 0.0f, 0.0f ); - glVertex3f( 0.0f, 10.0f, 0.0f ); - SetGLColor( BLUE ); - glNormal3f( 1.0f, 0.0f, 0.0f ); // Normal is Y axis - glVertex3f( 0.0f, 0.0f, 0.0f ); - glVertex3f( 0.0f, 0.0f, -10.0f ); - glVertex3f( 0.0f, 0.0f, 0.0f ); - glVertex3f( 0.0f, 0.0f, 10.0f ); // Z axis - glEnd(); - - glEndList(); - } -} - -// draw a 3D grid: an horizontal grid (XY plane and Z = 0, -// and a vertical grid (XZ plane and Y = 0) -void EDA_3D_CANVAS::draw3DGrid( double aGriSizeMM ) -{ - double zpos = 0.0; - EDA_COLOR_T gridcolor = DARKGRAY; // Color of grid lines - EDA_COLOR_T gridcolor_marker = LIGHTGRAY; // Color of grid lines every 5 lines - const double scale = GetPrm3DVisu().m_BiuTo3Dunits; - const double transparency = 0.3; - - glNormal3f( 0.0, 0.0, 1.0 ); - - wxSize brd_size = getBoardSize(); - wxPoint brd_center_pos = getBoardCenter(); - brd_center_pos.y = -brd_center_pos.y; - - int xsize = std::max( brd_size.x, Millimeter2iu( 100 ) ) * 1.2; - int ysize = std::max( brd_size.y, Millimeter2iu( 100 ) ) * 1.2; - - // Grid limits, in 3D units - double xmin = (brd_center_pos.x - xsize / 2) * scale; - double xmax = (brd_center_pos.x + xsize / 2) * scale; - double ymin = (brd_center_pos.y - ysize / 2) * scale; - double ymax = (brd_center_pos.y + ysize / 2) * scale; - double zmin = Millimeter2iu( -50 ) * scale; - double zmax = Millimeter2iu( 100 ) * scale; - - // Draw horizontal grid centered on 3D origin (center of the board) - for( int ii = 0; ; ii++ ) - { - if( (ii % 5) ) - SetGLColor( gridcolor, transparency ); - else - SetGLColor( gridcolor_marker, transparency ); - - int delta = KiROUND( ii * aGriSizeMM * IU_PER_MM ); - - if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis - { - glBegin( GL_LINES ); - glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos ); - glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos ); - glEnd(); - - if( ii != 0 ) - { - glBegin( GL_LINES ); - glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos ); - glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos ); - glEnd(); - } - } - - if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis - { - glBegin( GL_LINES ); - glVertex3f( xmin, -(brd_center_pos.y + delta) * scale, zpos ); - glVertex3f( xmax, -(brd_center_pos.y + delta) * scale, zpos ); - glEnd(); - - if( ii != 0 ) - { - glBegin( GL_LINES ); - glVertex3f( xmin, -(brd_center_pos.y - delta) * scale, zpos ); - glVertex3f( xmax, -(brd_center_pos.y - delta) * scale, zpos ); - glEnd(); - } - } - - if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) ) - break; - } - - // Draw vertical grid on Z axis - glNormal3f( 0.0, -1.0, 0.0 ); - - // Draw vertical grid lines (parallel to Z axis) - double posy = -brd_center_pos.y * scale; - - for( int ii = 0; ; ii++ ) - { - if( (ii % 5) ) - SetGLColor( gridcolor, transparency ); - else - SetGLColor( gridcolor_marker, transparency ); - - double delta = ii * aGriSizeMM * IU_PER_MM; - - glBegin( GL_LINES ); - xmax = (brd_center_pos.x + delta) * scale; - - glVertex3f( xmax, posy, zmin ); - glVertex3f( xmax, posy, zmax ); - glEnd(); - - if( ii != 0 ) - { - glBegin( GL_LINES ); - xmin = (brd_center_pos.x - delta) * scale; - glVertex3f( xmin, posy, zmin ); - glVertex3f( xmin, posy, zmax ); - glEnd(); - } - - if( delta > xsize / 2.0f ) - break; - } - - // Draw horizontal grid lines on Z axis (parallel to X axis) - for( int ii = 0; ; ii++ ) - { - if( (ii % 5) ) - SetGLColor( gridcolor, transparency); - else - SetGLColor( gridcolor_marker, transparency ); - - double delta = ii * aGriSizeMM * IU_PER_MM * scale; - - if( delta <= zmax ) - { - // Draw grid lines on Z axis (positive Z axis coordinates) - glBegin( GL_LINES ); - glVertex3f( xmin, posy, delta ); - glVertex3f( xmax, posy, delta ); - glEnd(); - } - - if( delta <= -zmin && ( ii != 0 ) ) - { - // Draw grid lines on Z axis (negative Z axis coordinates) - glBegin( GL_LINES ); - glVertex3f( xmin, posy, -delta ); - glVertex3f( xmax, posy, -delta ); - glEnd(); - } - - if( ( delta > zmax ) && ( delta > -zmin ) ) - break; - } -} - - -// Draw 3D pads. -void EDA_3D_CANVAS::draw3DPadHole( const D_PAD* aPad ) -{ - // Draw the pad hole - wxSize drillsize = aPad->GetDrillSize(); - bool hasHole = drillsize.x && drillsize.y; - - if( !hasHole ) - return; - - // Store here the points to approximate hole by segments - SHAPE_POLY_SET holecornersBuffer; - int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); - int height = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) - - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); - - if( isRealisticMode() ) - setGLCopperColor(); - else - SetGLColor( DARKGRAY ); - - int holeZpoz = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ) - thickness / 2; - int holeHeight = height + thickness; - - if( drillsize.x == drillsize.y ) // usual round hole - { - int hole_radius = ( drillsize.x + thickness ) / 2; - Draw3D_ZaxisCylinder( aPad->GetPosition(), - hole_radius, holeHeight, - thickness, holeZpoz, GetPrm3DVisu().m_BiuTo3Dunits ); - } - else // Oblong hole - { - wxPoint ends_offset; - int width; - - if( drillsize.x > drillsize.y ) // Horizontal oval - { - ends_offset.x = ( drillsize.x - drillsize.y ) / 2; - width = drillsize.y; - } - else // Vertical oval - { - ends_offset.y = ( drillsize.y - drillsize.x ) / 2; - width = drillsize.x; - } - - RotatePoint( &ends_offset, aPad->GetOrientation() ); - - wxPoint start = aPad->GetPosition() + ends_offset; - wxPoint end = aPad->GetPosition() - ends_offset; - int hole_radius = ( width + thickness ) / 2; - - // Draw the hole - Draw3D_ZaxisOblongCylinder( start, end, hole_radius, holeHeight, - thickness, holeZpoz, GetPrm3DVisu().m_BiuTo3Dunits ); - } -} - - -void EDA_3D_CANVAS::draw3DViaHole( const VIA* aVia ) -{ - LAYER_ID top_layer, bottom_layer; - int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); - int inner_radius = (int)((float)aVia->GetDrillValue() * 1.01f) / 2.0f; // This add a bit more in order to correct a draw artifact while using thickness - - aVia->LayerPair( &top_layer, &bottom_layer ); - - // Drawing via hole: - if( isRealisticMode() ) - setGLCopperColor(); - else - { - EDA_COLOR_T color = g_ColorsSettings.GetItemColor( VIAS_VISIBLE + aVia->GetViaType() ); - SetGLColor( color ); - } - - int height = GetPrm3DVisu().GetLayerZcoordBIU( top_layer ) - - GetPrm3DVisu().GetLayerZcoordBIU( bottom_layer ) + thickness; - int zpos = GetPrm3DVisu().GetLayerZcoordBIU( bottom_layer ) - thickness / 2; - - Draw3D_ZaxisCylinder( aVia->GetStart(), inner_radius, height, - thickness, zpos, GetPrm3DVisu().m_BiuTo3Dunits ); -} - -/* Build a pad outline as non filled polygon, to draw pads on silkscreen layer - * Used only to draw pads outlines on silkscreen layers. - */ -void EDA_3D_CANVAS::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad, - SHAPE_POLY_SET& aCornerBuffer, - int aWidth, - int aCircleToSegmentsCount, - double aCorrectionFactor ) -{ - if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring - { - TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), - aPad->GetSize().x / 2, aCircleToSegmentsCount, aWidth ); - return; - } - - // For other shapes, draw polygon outlines - SHAPE_POLY_SET corners; - aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ), - aCircleToSegmentsCount, aCorrectionFactor ); - - // Add outlines as thick segments in polygon buffer - - const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); - - for( int ii = 0; ii < path.PointCount(); ii++ ) - { - const VECTOR2I& a = path.CPoint( ii ); - const VECTOR2I& b = path.CPoint( ii + 1 ); - - TransformRoundedEndsSegmentToPolygon( aCornerBuffer, - wxPoint( a.x, a.y ), - wxPoint( b.x, b.y ), - aCircleToSegmentsCount, aWidth ); - } -} - - -GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer ) -{ - double nZ = 1.0; - - if( ( aLayer == B_Cu ) - || ( aLayer == B_Adhes ) - || ( aLayer == B_Paste ) - || ( aLayer == B_SilkS ) - || ( aLayer == B_Mask ) ) - nZ = -1.0; - - return nZ; -} diff --git a/3d-viewer/3d_enums.h b/3d-viewer/3d_enums.h new file mode 100644 index 0000000000..3f41d6c74e --- /dev/null +++ b/3d-viewer/3d_enums.h @@ -0,0 +1,97 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file 3d_enums.h + * @brief declared enumerations and flags + */ + +#ifndef _3D_ENUMS_H_ +#define _3D_ENUMS_H_ + +/// Flags used in rendering options +enum DISPLAY3D_FLG { + FL_AXIS=0, FL_ZONE, + FL_ADHESIVE, FL_SILKSCREEN, FL_SOLDERMASK, FL_SOLDERPASTE, + FL_COMMENTS, FL_ECO, + + FL_MODULE_ATTRIBUTES_NORMAL, + FL_MODULE_ATTRIBUTES_NORMAL_INSERT, + FL_MODULE_ATTRIBUTES_VIRTUAL, + + FL_SHOW_BOARD_BODY, + FL_MOUSEWHEEL_PANNING, + FL_USE_REALISTIC_MODE, + FL_RENDER_SHOW_HOLES_IN_ZONES, + + // OpenGL options + FL_RENDER_OPENGL_SHOW_MODEL_BBOX, + FL_RENDER_OPENGL_COPPER_THICKNESS, + + // Raytracing options + FL_RENDER_RAYTRACING_SHADOWS, + FL_RENDER_RAYTRACING_BACKFLOOR, + FL_RENDER_RAYTRACING_REFRACTIONS, + FL_RENDER_RAYTRACING_REFLECTIONS, + FL_RENDER_RAYTRACING_POST_PROCESSING, + FL_RENDER_RAYTRACING_ANTI_ALIASING, + FL_LAST +}; + + +/// Camera types +enum CAMERA_TYPE +{ + CAMERA_TRACKBALL +}; + + +/// Grid types +enum GRID3D_TYPE +{ + GRID3D_NONE, + GRID3D_1MM, + GRID3D_2P5MM, + GRID3D_5MM, + GRID3D_10MM +}; + + +/// Render engine mode +enum RENDER_ENGINE +{ + RENDER_ENGINE_OPENGL_LEGACY, + RENDER_ENGINE_RAYTRACING, +}; + + +/// Render 3d model shape materials mode +enum MATERIAL_MODE +{ + MATERIAL_MODE_NORMAL, ///< Use all material properties from model file + MATERIAL_MODE_DIFFUSE_ONLY, ///< Use only diffuse material properties + MATERIAL_MODE_CAD_MODE ///< Use a gray shading based on diffuse material +}; + +#endif // _3D_ENUMS_H_ diff --git a/3d-viewer/3d_fastmath.cpp b/3d-viewer/3d_fastmath.cpp new file mode 100644 index 0000000000..e84cfb35ac --- /dev/null +++ b/3d-viewer/3d_fastmath.cpp @@ -0,0 +1,68 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file 3d_fastmath.cpp + * @brief + */ + + +#include "3d_fastmath.h" + + +// Fast Float Random Numbers +// a small and fast implementation for random float numbers in the range [-1,1] +// References : Posted by dominik.ries[AT]gmail[DOT]com +// http://www.musicdsp.org/showone.php?id=273 + + +// set the initial seed to whatever you like +static int s_randSeed = 1; + +// fast rand float, using full 32bit precision +// returns in the range [-1, 1] (not confirmed) +float Fast_RandFloat() +{ + s_randSeed *= 16807; + + return (float)s_randSeed * 4.6566129e-010f; +} + + +// Fast rand, as described here: +// http://wiki.osdev.org/Random_Number_Generator + +static unsigned long int s_nextRandSeed = 1; + +int Fast_rand( void ) // RAND_MAX assumed to be 32767 +{ + s_nextRandSeed = s_nextRandSeed * 1103515245 + 12345; + + return (unsigned int)(s_nextRandSeed >> 16) & 0x7FFF; +} + +void Fast_srand( unsigned int seed ) +{ + s_nextRandSeed = seed; +} diff --git a/3d-viewer/3d_math/3d_fastmath.h b/3d-viewer/3d_fastmath.h similarity index 75% rename from 3d-viewer/3d_math/3d_fastmath.h rename to 3d-viewer/3d_fastmath.h index 683082b302..24e5a9f7b4 100644 --- a/3d-viewer/3d_math/3d_fastmath.h +++ b/3d-viewer/3d_fastmath.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,6 +47,14 @@ #endif +// Fast Float Random Numbers +// a small and fast implementation for random float numbers in the range [-1,1] +// This is not thread safe +float Fast_RandFloat(); + +int Fast_rand( void ); +void Fast_srand( unsigned int seed ); + /** * This part contains some functions from the PBRT 3 source code. * https://github.com/mmp/pbrt-v3/blob/master/src/core/pbrt.h @@ -85,54 +93,84 @@ // Global Inline Functions -inline uint32_t FloatToBits(float f) { +inline uint32_t FloatToBits( float f ) +{ uint32_t ui; - memcpy(&ui, &f, sizeof(float)); + + memcpy( &ui, &f, sizeof( float ) ); + return ui; } -inline float BitsToFloat(uint32_t ui) { + +inline float BitsToFloat( uint32_t ui ) +{ float f; - memcpy(&f, &ui, sizeof(uint32_t)); + + memcpy( &f, &ui, sizeof (uint32_t ) ); + return f; } -inline uint64_t FloatToBits(double f) { + +inline uint64_t FloatToBits( double f ) +{ uint64_t ui; - memcpy(&ui, &f, sizeof(double)); + + memcpy( &ui, &f, sizeof( double ) ); + return ui; } -inline double BitsToFloat(uint64_t ui) { + +inline double BitsToFloat( uint64_t ui ) +{ double f; - memcpy(&f, &ui, sizeof(uint64_t)); + + memcpy( &f, &ui, sizeof( uint64_t ) ); + return f; } -inline float NextFloatUp(float v) { + +inline float NextFloatUp( float v ) +{ // Handle infinity and negative zero for _NextFloatUp()_ - if (std::isinf(v) && v > 0.) return v; - if (v == -0.f) v = 0.f; + if( std::isinf( v ) && (v > 0.) ) + return v; + + if( v == -0.f ) + v = 0.f; // Advance _v_ to next higher float - uint32_t ui = FloatToBits(v); - if (v >= 0.) + uint32_t ui = FloatToBits( v ); + + if( v >= 0. ) ++ui; else --ui; - return BitsToFloat(ui); + + return BitsToFloat( ui ); } -inline float NextFloatDown(float v) { + +inline float NextFloatDown( float v ) +{ // Handle infinity and positive zero for _NextFloatDown()_ - if (std::isinf(v) && v < 0.) return v; - if (v == 0.f) v = -0.f; - uint32_t ui = FloatToBits(v); - if (v > 0.) + if( std::isinf( v ) && (v < 0.) ) + return v; + + if( v == 0.f ) + v = -0.f; + + uint32_t ui = FloatToBits( v ); + + if( v > 0. ) --ui; else ++ui; - return BitsToFloat(ui); + + return BitsToFloat( ui ); } #endif // 3D_FASTMATH_H diff --git a/3d-viewer/3d_frame.cpp b/3d-viewer/3d_frame.cpp deleted file mode 100644 index f1525ed88e..0000000000 --- a/3d-viewer/3d_frame.cpp +++ /dev/null @@ -1,913 +0,0 @@ -/** - * @file 3d_frame.cpp - */ - -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include -#include -#include -#include - -#include <3d_viewer.h> -#include <3d_canvas.h> -#include -#include - -#include -#include <3d_viewer_id.h> -#include - -INFO3D_VISU g_Parm_3D_Visu; - -// Key to store 3D Viewer config: -static const wxChar keyBgColor_Red[] = wxT( "BgColor_Red" ); -static const wxChar keyBgColor_Green[] = wxT( "BgColor_Green" ); -static const wxChar keyBgColor_Blue[] = wxT( "BgColor_Blue" ); - -static const wxChar keyBgColor_Red_Top[] = wxT( "BgColor_Red_Top" ); -static const wxChar keyBgColor_Green_Top[] = wxT( "BgColor_Green_Top" ); -static const wxChar keyBgColor_Blue_Top[] = wxT( "BgColor_Blue_Top" ); - -static const wxChar keySMaskColor_Red[] = wxT( "SMaskColor_Red" ); -static const wxChar keySMaskColor_Green[] = wxT( "SMaskColor_Green" ); -static const wxChar keySMaskColor_Blue[] = wxT( "SMaskColor_Blue" ); - -static const wxChar keySPasteColor_Red[] = wxT( "SPasteColor_Red" ); -static const wxChar keySPasteColor_Green[] = wxT( "SPasteColor_Green" ); -static const wxChar keySPasteColor_Blue[] = wxT( "SPasteColor_Blue" ); - -static const wxChar keySilkColor_Red[] = wxT( "SilkColor_Red" ); -static const wxChar keySilkColor_Green[] = wxT( "SilkColor_Green" ); -static const wxChar keySilkColor_Blue[] = wxT( "SilkColor_Blue" ); - -static const wxChar keyCopperColor_Red[] = wxT( "CopperColor_Red" ); -static const wxChar keyCopperColor_Green[] = wxT( "CopperColor_Green" ); -static const wxChar keyCopperColor_Blue[] = wxT( "CopperColor_Blue" ); - -static const wxChar keyBoardBodyColor_Red[] = wxT( "BoardBodyColor_Red" ); -static const wxChar keyBoardBodyColor_Green[] = wxT( "BoardBodyColor_Green" ); -static const wxChar keyBoardBodyColor_Blue[]= wxT( "BoardBodyColor_Blue" ); - -static const wxChar keyMousewheelPanning[] = wxT( "MousewheelPAN3D" ); - -static const wxChar keyShowRealisticMode[] = wxT( "ShowRealisticMode" ); -static const wxChar keyRenderShadows[] = wxT( "Render_Shadows" ); -static const wxChar keyRenderRemoveHoles[] = wxT( "Render_RemoveHoles" ); -static const wxChar keyRenderTextures[] = wxT( "Render_Textures" ); -static const wxChar keyRenderSmoothNormals[] = wxT( "Render_Smooth_Normals" ); -static const wxChar keyRenderUseModelNormals[] =wxT( "Render_Use_Model_Normals" ); -static const wxChar keyRenderMaterial[] = wxT( "Render_Material" ); -static const wxChar keyRenderShowModelBBox[] = wxT( "Render_ShowModelBoudingBoxes" ); - -static const wxChar keyShowAxis[] = wxT( "ShowAxis" ); -static const wxChar keyShowGrid[] = wxT( "ShowGrid3D" ); -static const wxChar keyShowGridSize[] = wxT( "Grid3DSize" ); -static const wxChar keyShowZones[] = wxT( "ShowZones" ); -static const wxChar keyShowFootprints[] = wxT( "ShowFootprints" ); -static const wxChar keyShowCopperThickness[] = wxT( "ShowCopperThickness" ); -static const wxChar keyShowAdhesiveLayers[] = wxT( "ShowAdhesiveLayers" ); -static const wxChar keyShowSilkScreenLayers[] = wxT( "ShowSilkScreenLayers" ); -static const wxChar keyShowSolderMaskLayers[] = wxT( "ShowSolderMasLayers" ); -static const wxChar keyShowSolderPasteLayers[] =wxT( "ShowSolderPasteLayers" ); -static const wxChar keyShowCommentsLayer[] = wxT( "ShowCommentsLayers" ); -static const wxChar keyShowBoardBody[] = wxT( "ShowBoardBody" ); -static const wxChar keyShowEcoLayers[] = wxT( "ShowEcoLayers" ); - - -BEGIN_EVENT_TABLE( EDA_3D_FRAME, EDA_BASE_FRAME ) -EVT_ACTIVATE( EDA_3D_FRAME::OnActivate ) - -EVT_TOOL_RANGE( ID_ZOOM_IN, ID_ZOOM_PAGE, EDA_3D_FRAME::Process_Zoom ) -EVT_TOOL_RANGE( ID_START_COMMAND_3D, ID_END_COMMAND_3D, - EDA_3D_FRAME::Process_Special_Functions ) -EVT_TOOL( ID_TOOL_SET_VISIBLE_ITEMS, EDA_3D_FRAME::Process_Special_Functions ) -EVT_MENU( wxID_EXIT, EDA_3D_FRAME::Exit3DFrame ) -EVT_MENU( ID_MENU_SCREENCOPY_PNG, EDA_3D_FRAME::Process_Special_Functions ) -EVT_MENU( ID_MENU_SCREENCOPY_JPEG, EDA_3D_FRAME::Process_Special_Functions ) - -EVT_MENU_RANGE( ID_MENU3D_GRID, ID_MENU3D_GRID_END, - EDA_3D_FRAME::On3DGridSelection ) - -EVT_CLOSE( EDA_3D_FRAME::OnCloseWindow ) - -END_EVENT_TABLE() - - -EDA_3D_FRAME::EDA_3D_FRAME( KIWAY* aKiway, PCB_BASE_FRAME* aParent, - const wxString& aTitle, long style ) : - KIWAY_PLAYER( aKiway, aParent, FRAME_PCB_DISPLAY3D, aTitle, - wxDefaultPosition, wxDefaultSize, style, VIEWER3D_FRAMENAME ) -{ - m_canvas = NULL; - m_reloadRequest = false; - m_ortho = false; - - // Give it an icon - wxIcon icon; - icon.CopyFromBitmap( KiBitmap( icon_3d_xpm ) ); - SetIcon( icon ); - - LoadSettings( config() ); - SetSize( m_FramePos.x, m_FramePos.y, m_FrameSize.x, m_FrameSize.y ); - - // Create the status line - static const int status_dims[4] = { -1, 130, 130, 170 }; - - CreateStatusBar( DIM( status_dims ) ); - SetStatusWidths( DIM( status_dims ), status_dims ); - - CreateMenuBar(); - ReCreateMainToolbar(); - - // Make a EDA_3D_CANVAS - // Note: We try to use anti aliasing if the graphic card allows that, - // but only on wxWidgets >= 3.0.0 (this option does not exist on wxWidgets 2.8) - int attrs[] = { // This array should be 2*n+1 - // Sadly wxwidgets / glx < 13 allowed - // a thing named "boolean attributes" that don't take a value. - // (See src/unix/glx11.cpp -> wxGLCanvasX11::ConvertWXAttrsToGL() ). - // To avoid problems due to this, just specify those attributes twice. - // Only WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_STEREO are such boolean - // attributes. - - // Boolean attributes (using itself at padding): - WX_GL_RGBA, WX_GL_RGBA, - WX_GL_DOUBLEBUFFER, WX_GL_DOUBLEBUFFER, - - // Normal attributes with values: - WX_GL_DEPTH_SIZE, 16, - WX_GL_STENCIL_SIZE, 1, - WX_GL_SAMPLE_BUFFERS, 1, // Enable multisampling support (antialiasing). - WX_GL_SAMPLES, 0, // Disable AA for the start. - 0 }; // NULL termination - - - // Check if the canvas supports multisampling. - if( EDA_3D_CANVAS::IsDisplaySupported( attrs ) ) - { - // Check for possible sample sizes, start form the top. - int maxSamples = 8; // Any higher doesn't change anything. - int samplesOffset = 0; - - for( unsigned int ii = 0; ii < DIM( attrs ); ii += 2 ) - { - if( attrs[ii] == WX_GL_SAMPLES ) - { - samplesOffset = ii+1; - break; - } - } - - attrs[samplesOffset] = maxSamples; - - for( ; maxSamples > 0 && !EDA_3D_CANVAS::IsDisplaySupported( attrs ); - maxSamples = maxSamples>>1 ) - { - attrs[samplesOffset] = maxSamples; - } - } - else - { - // Disable multisampling - for( unsigned int ii = 0; ii < DIM( attrs ); ii += 2 ) - { - if( attrs[ii] == WX_GL_SAMPLE_BUFFERS ) - { - attrs[ii+1] = 0; - break; - } - } - } - - m_canvas = new EDA_3D_CANVAS( this, attrs ); - - m_auimgr.SetManagedWindow( this ); - - - EDA_PANEINFO horiztb; - horiztb.HorizontalToolbarPane(); - - m_auimgr.AddPane( m_mainToolBar, - wxAuiPaneInfo( horiztb ).Name( wxT( "m_mainToolBar" ) ).Top() ); - - m_auimgr.AddPane( m_canvas, - wxAuiPaneInfo().Name( wxT( "DrawFrame" ) ).CentrePane() ); - - m_auimgr.Update(); - - // Fixes bug in Windows (XP and possibly others) where the canvas requires the focus - // in order to receive mouse events. Otherwise, the user has to click somewhere on - // the canvas before it will respond to mouse wheel events. - m_canvas->SetFocus(); -} - - -EDA_3D_FRAME::~EDA_3D_FRAME() -{ - m_auimgr.UnInit(); -} - - -void EDA_3D_FRAME::Exit3DFrame( wxCommandEvent& event ) -{ - Close( true ); -} - - -void EDA_3D_FRAME::OnCloseWindow( wxCloseEvent& Event ) -{ - Destroy(); -} - - -void EDA_3D_FRAME::LoadSettings( wxConfigBase* aCfg ) -{ - EDA_BASE_FRAME::LoadSettings( aCfg ); - - INFO3D_VISU& prms = GetPrm3DVisu(); - - aCfg->Read( keyBgColor_Red, &GetPrm3DVisu().m_BgColor.m_Red, 0.4 ); - aCfg->Read( keyBgColor_Green, &GetPrm3DVisu().m_BgColor.m_Green, 0.4 ); - aCfg->Read( keyBgColor_Blue, &GetPrm3DVisu().m_BgColor.m_Blue, 0.5 ); - - aCfg->Read( keyBgColor_Red_Top, &GetPrm3DVisu().m_BgColor_Top.m_Red, 0.8 ); - aCfg->Read( keyBgColor_Green_Top, &GetPrm3DVisu().m_BgColor_Top.m_Green, 0.8 ); - aCfg->Read( keyBgColor_Blue_Top, &GetPrm3DVisu().m_BgColor_Top.m_Blue, 0.9 ); - - // m_SolderMaskColor default value = dark grey-green - aCfg->Read( keySMaskColor_Red, &GetPrm3DVisu().m_SolderMaskColor.m_Red, 100.0 * 0.2 / 255.0 ); - aCfg->Read( keySMaskColor_Green, &GetPrm3DVisu().m_SolderMaskColor.m_Green, 255.0 * 0.2 / 255.0 ); - aCfg->Read( keySMaskColor_Blue, &GetPrm3DVisu().m_SolderMaskColor.m_Blue, 180.0 * 0.2 / 255.0 ); - - // m_SolderPasteColor default value = light grey - aCfg->Read( keySPasteColor_Red, &GetPrm3DVisu().m_SolderPasteColor.m_Red, 128.0 /255.0 ); - aCfg->Read( keySPasteColor_Green, &GetPrm3DVisu().m_SolderPasteColor.m_Green, 128.0 /255.0 ); - aCfg->Read( keySPasteColor_Blue, &GetPrm3DVisu().m_SolderPasteColor.m_Blue, 128.0 /255.0 ); - - // m_SilkScreenColor default value = white - aCfg->Read( keySilkColor_Red, &GetPrm3DVisu().m_SilkScreenColor.m_Red, 0.9 ); - aCfg->Read( keySilkColor_Green, &GetPrm3DVisu().m_SilkScreenColor.m_Green, 0.9 ); - aCfg->Read( keySilkColor_Blue, &GetPrm3DVisu().m_SilkScreenColor.m_Blue, 0.9 ); - - // m_CopperColor default value = gold - aCfg->Read( keyCopperColor_Red, &GetPrm3DVisu().m_CopperColor.m_Red, 255.0 * 0.7 / 255.0 ); - aCfg->Read( keyCopperColor_Green, &GetPrm3DVisu().m_CopperColor.m_Green, 223.0 * 0.7 / 255.0 ); - aCfg->Read( keyCopperColor_Blue, &GetPrm3DVisu().m_CopperColor.m_Blue, 0.0 /255.0 ); - - // m_BoardBodyColor default value = FR4, in realistic mode - aCfg->Read( keyBoardBodyColor_Red, &GetPrm3DVisu().m_BoardBodyColor.m_Red, 51.0 / 255.0 ); - aCfg->Read( keyBoardBodyColor_Green, &GetPrm3DVisu().m_BoardBodyColor.m_Green, 43.0 / 255.0 ); - aCfg->Read( keyBoardBodyColor_Blue, &GetPrm3DVisu().m_BoardBodyColor.m_Blue, 22.0 /255.0 ); - - bool tmp; - aCfg->Read( keyMousewheelPanning, &tmp, false ); - prms.SetFlag( FL_MOUSEWHEEL_PANNING, tmp ); - - aCfg->Read( keyShowRealisticMode, &tmp, false ); - prms.SetFlag( FL_USE_REALISTIC_MODE, tmp ); - - aCfg->Read( keyRenderShadows, &tmp, false ); - prms.SetFlag( FL_RENDER_SHADOWS, tmp ); - - aCfg->Read( keyRenderRemoveHoles, &tmp, false ); - prms.SetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES, tmp ); - - aCfg->Read( keyRenderTextures, &tmp, false ); - prms.SetFlag( FL_RENDER_TEXTURES, tmp ); - - aCfg->Read( keyRenderSmoothNormals, &tmp, false ); - prms.SetFlag( FL_RENDER_SMOOTH_NORMALS, tmp ); - - aCfg->Read( keyRenderUseModelNormals, &tmp, false ); - prms.SetFlag( FL_RENDER_USE_MODEL_NORMALS, tmp ); - - aCfg->Read( keyRenderMaterial, &tmp, false ); - prms.SetFlag( FL_RENDER_MATERIAL, tmp ); - - aCfg->Read( keyRenderShowModelBBox, &tmp, false ); - prms.SetFlag( FL_RENDER_SHOW_MODEL_BBOX, tmp ); - - aCfg->Read( keyShowAxis, &tmp, true ); - prms.SetFlag( FL_AXIS, tmp ); - - aCfg->Read( keyShowGrid, &tmp, true ); - prms.SetFlag( FL_GRID, tmp ); - - aCfg->Read( keyShowGridSize, &prms.m_3D_Grid, 10.0 ); - - aCfg->Read( keyShowFootprints, &tmp, true ); - prms.SetFlag( FL_MODULE, tmp ); - - aCfg->Read( keyShowCopperThickness, &tmp, false ); - prms.SetFlag( FL_USE_COPPER_THICKNESS, tmp ); - - aCfg->Read( keyShowZones, &tmp, true ); - prms.SetFlag( FL_ZONE, tmp ); - - aCfg->Read( keyShowAdhesiveLayers, &tmp, true ); - prms.SetFlag( FL_ADHESIVE, tmp ); - - aCfg->Read( keyShowSilkScreenLayers, &tmp, true ); - prms.SetFlag( FL_SILKSCREEN, tmp ); - - aCfg->Read( keyShowSolderMaskLayers, &tmp, true ); - prms.SetFlag( FL_SOLDERMASK, tmp ); - - aCfg->Read( keyShowSolderPasteLayers, &tmp, true ); - prms.SetFlag( FL_SOLDERPASTE, tmp ); - - aCfg->Read( keyShowCommentsLayer, &tmp, true ); - prms.SetFlag( FL_COMMENTS, tmp ); - - aCfg->Read( keyShowEcoLayers, &tmp, true ); - prms.SetFlag( FL_ECO, tmp ); - - aCfg->Read( keyShowBoardBody, &tmp, true ); - prms.SetFlag( FL_SHOW_BOARD_BODY, tmp ); -} - - -void EDA_3D_FRAME::SaveSettings( wxConfigBase* aCfg ) -{ - EDA_BASE_FRAME::SaveSettings( aCfg ); - - INFO3D_VISU& prms = GetPrm3DVisu(); - - aCfg->Write( keyBgColor_Red, GetPrm3DVisu().m_BgColor.m_Red ); - aCfg->Write( keyBgColor_Green, GetPrm3DVisu().m_BgColor.m_Green ); - aCfg->Write( keyBgColor_Blue, GetPrm3DVisu().m_BgColor.m_Blue ); - - aCfg->Write( keyBgColor_Red_Top, GetPrm3DVisu().m_BgColor_Top.m_Red ); - aCfg->Write( keyBgColor_Green_Top, GetPrm3DVisu().m_BgColor_Top.m_Green ); - aCfg->Write( keyBgColor_Blue_Top, GetPrm3DVisu().m_BgColor_Top.m_Blue ); - - aCfg->Write( keySMaskColor_Red, GetPrm3DVisu().m_SolderMaskColor.m_Red ); - aCfg->Write( keySMaskColor_Green, GetPrm3DVisu().m_SolderMaskColor.m_Green ); - aCfg->Write( keySMaskColor_Blue, GetPrm3DVisu().m_SolderMaskColor.m_Blue ); - - aCfg->Write( keySPasteColor_Red, GetPrm3DVisu().m_SolderPasteColor.m_Red ); - aCfg->Write( keySPasteColor_Green, GetPrm3DVisu().m_SolderPasteColor.m_Green ); - aCfg->Write( keySPasteColor_Blue, GetPrm3DVisu().m_SolderPasteColor.m_Blue ); - - aCfg->Write( keySilkColor_Red, GetPrm3DVisu().m_SilkScreenColor.m_Red ); - aCfg->Write( keySilkColor_Green, GetPrm3DVisu().m_SilkScreenColor.m_Green ); - aCfg->Write( keySilkColor_Blue, GetPrm3DVisu().m_SilkScreenColor.m_Blue ); - - aCfg->Write( keyCopperColor_Red, GetPrm3DVisu().m_CopperColor.m_Red ); - aCfg->Write( keyCopperColor_Green, GetPrm3DVisu().m_CopperColor.m_Green ); - aCfg->Write( keyCopperColor_Blue, GetPrm3DVisu().m_CopperColor.m_Blue ); - - aCfg->Write( keyBoardBodyColor_Red, GetPrm3DVisu().m_BoardBodyColor.m_Red ); - aCfg->Write( keyBoardBodyColor_Green, GetPrm3DVisu().m_BoardBodyColor.m_Green ); - aCfg->Write( keyBoardBodyColor_Blue, GetPrm3DVisu().m_BoardBodyColor.m_Blue ); - - aCfg->Write( keyMousewheelPanning, prms.GetFlag( FL_MOUSEWHEEL_PANNING ) ); - - aCfg->Write( keyShowRealisticMode, prms.GetFlag( FL_USE_REALISTIC_MODE ) ); - - aCfg->Write( keyRenderShadows, prms.GetFlag( FL_RENDER_SHADOWS ) ); - aCfg->Write( keyRenderRemoveHoles, prms.GetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES ) ); - aCfg->Write( keyRenderTextures, prms.GetFlag( FL_RENDER_TEXTURES ) ); - aCfg->Write( keyRenderSmoothNormals, prms.GetFlag( FL_RENDER_SMOOTH_NORMALS ) ); - aCfg->Write( keyRenderUseModelNormals, prms.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ); - aCfg->Write( keyRenderMaterial, prms.GetFlag( FL_RENDER_MATERIAL ) ); - aCfg->Write( keyRenderShowModelBBox, prms.GetFlag( FL_RENDER_SHOW_MODEL_BBOX ) ); - - aCfg->Write( keyShowAxis, prms.GetFlag( FL_AXIS ) ); - aCfg->Write( keyShowGrid, prms.GetFlag( FL_GRID ) ); - aCfg->Write( keyShowGridSize, prms.m_3D_Grid ); - aCfg->Write( keyShowFootprints, prms.GetFlag( FL_MODULE ) ); - aCfg->Write( keyShowCopperThickness, prms.GetFlag( FL_USE_COPPER_THICKNESS ) ); - aCfg->Write( keyShowZones, prms.GetFlag( FL_ZONE ) ); - aCfg->Write( keyShowAdhesiveLayers, prms.GetFlag( FL_ADHESIVE ) ); - aCfg->Write( keyShowSilkScreenLayers, prms.GetFlag( FL_SILKSCREEN ) ); - aCfg->Write( keyShowSolderMaskLayers, prms.GetFlag( FL_SOLDERMASK ) ); - aCfg->Write( keyShowSolderPasteLayers, prms.GetFlag( FL_SOLDERPASTE ) ); - aCfg->Write( keyShowCommentsLayer, prms.GetFlag( FL_COMMENTS ) ); - aCfg->Write( keyShowEcoLayers, prms.GetFlag( FL_ECO ) ); - aCfg->Write( keyShowBoardBody, prms.GetFlag( FL_SHOW_BOARD_BODY ) ); -} - - -void EDA_3D_FRAME::Process_Zoom( wxCommandEvent& event ) -{ - int ii; - - switch( event.GetId() ) - { - case ID_ZOOM_PAGE: - - for( ii = 0; ii < 4; ii++ ) - GetPrm3DVisu().m_Rot[ii] = 0.0; - - GetPrm3DVisu().m_Zoom = 1.0; - m_canvas->SetOffset( 0.0, 0.0 ); - trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 ); - break; - - case ID_ZOOM_IN: - GetPrm3DVisu().m_Zoom /= 1.2; - - if( GetPrm3DVisu().m_Zoom <= 0.01 ) - GetPrm3DVisu().m_Zoom = 0.01; - - break; - - case ID_ZOOM_OUT: - GetPrm3DVisu().m_Zoom *= 1.2; - break; - - case ID_ZOOM_REDRAW: - break; - - default: - return; - } - - m_canvas->Refresh( false ); - m_canvas->DisplayStatus(); -} - - -void EDA_3D_FRAME::OnLeftClick( wxDC* DC, const wxPoint& MousePos ) -{ -} - - -void EDA_3D_FRAME::OnRightClick( const wxPoint& MousePos, wxMenu* PopMenu ) -{ -} - - -double EDA_3D_FRAME::BestZoom() -{ - return 1.0; -} - - -void EDA_3D_FRAME::RedrawActiveWindow( wxDC* DC, bool EraseBg ) -{ -} - - -void EDA_3D_FRAME::Process_Special_Functions( wxCommandEvent& event ) -{ -#define ROT_ANGLE 10.0 - int id = event.GetId(); - bool isChecked = event.IsChecked(); - - switch( id ) - { - case ID_TOOL_SET_VISIBLE_ITEMS: - Install_3D_ViewOptionDialog( event ); - break; - - case ID_RELOAD3D_BOARD: - m_reloadRequest = true; - NewDisplay(); - return; - break; - - case ID_ROTATE3D_X_POS: - GetPrm3DVisu().m_ROTX += ROT_ANGLE; - break; - - case ID_ROTATE3D_X_NEG: - GetPrm3DVisu().m_ROTX -= ROT_ANGLE; - break; - - case ID_ROTATE3D_Y_POS: - GetPrm3DVisu().m_ROTY += ROT_ANGLE; - break; - - case ID_ROTATE3D_Y_NEG: - GetPrm3DVisu().m_ROTY -= ROT_ANGLE; - break; - - case ID_ROTATE3D_Z_POS: - GetPrm3DVisu().m_ROTZ += ROT_ANGLE; - break; - - case ID_ROTATE3D_Z_NEG: - GetPrm3DVisu().m_ROTZ -= ROT_ANGLE; - break; - - case ID_MOVE3D_LEFT: - m_canvas->SetView3D( WXK_LEFT ); - return; - - case ID_MOVE3D_RIGHT: - m_canvas->SetView3D( WXK_RIGHT ); - return; - - case ID_MOVE3D_UP: - m_canvas->SetView3D( WXK_UP ); - return; - - case ID_MOVE3D_DOWN: - m_canvas->SetView3D( WXK_DOWN ); - return; - - case ID_ORTHO: - ToggleOrtho(); - return; - - case ID_TOOL_SCREENCOPY_TOCLIBBOARD: - case ID_MENU_SCREENCOPY_PNG: - case ID_MENU_SCREENCOPY_JPEG: - m_canvas->TakeScreenshot( event ); - break; - - case ID_MENU3D_BGCOLOR_BOTTOM_SELECTION: - if( Set3DColorFromUser( GetPrm3DVisu().m_BgColor, _( "Background Color, Bottom" ) ) ) - m_canvas->Refresh( true ); - return; - - case ID_MENU3D_BGCOLOR_TOP_SELECTION: - if( Set3DColorFromUser( GetPrm3DVisu().m_BgColor_Top, _( "Background Color, Top" ) ) ) - m_canvas->Refresh( true ); - return; - - case ID_MENU3D_SILKSCREEN_COLOR_SELECTION: - Set3DSilkScreenColorFromUser(); - return; - - case ID_MENU3D_SOLDERMASK_COLOR_SELECTION: - Set3DSolderMaskColorFromUser(); - return; - - case ID_MENU3D_SOLDERPASTE_COLOR_SELECTION: - Set3DSolderPasteColorFromUser(); - return; - - case ID_MENU3D_COPPER_COLOR_SELECTION: - Set3DCopperColorFromUser(); - break; - - case ID_MENU3D_PCB_BODY_COLOR_SELECTION: - Set3DBoardBodyColorFromUser(); - break; - - case ID_MENU3D_MOUSEWHEEL_PANNING: - GetPrm3DVisu().SetFlag( FL_MOUSEWHEEL_PANNING, isChecked ); - return; - - case ID_MENU3D_REALISTIC_MODE: - GetPrm3DVisu().SetFlag( FL_USE_REALISTIC_MODE, isChecked ); - GetMenuBar()->FindItem( ID_MENU3D_COMMENTS_ONOFF )->Enable( !isChecked ); - GetMenuBar()->FindItem( ID_MENU3D_ECO_ONOFF )->Enable( !isChecked ); - NewDisplay(); - return; - - case ID_MENU3D_FL_RENDER_SHADOWS: - GetPrm3DVisu().SetFlag( FL_RENDER_SHADOWS, isChecked ); - NewDisplay(); - return; - - case ID_MENU3D_FL_RENDER_SHOW_HOLES_IN_ZONES: - GetPrm3DVisu().SetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES, isChecked ); - NewDisplay(); - return; - - case ID_MENU3D_FL_RENDER_TEXTURES: - GetPrm3DVisu().SetFlag( FL_RENDER_TEXTURES, isChecked ); - NewDisplay(GL_ID_BOARD); - NewDisplay(GL_ID_TECH_LAYERS); - return; - - case ID_MENU3D_FL_RENDER_SMOOTH_NORMALS: - GetPrm3DVisu().SetFlag( FL_RENDER_SMOOTH_NORMALS, isChecked ); - NewDisplay(); - return; - - case ID_MENU3D_FL_RENDER_USE_MODEL_NORMALS: - GetPrm3DVisu().SetFlag( FL_RENDER_USE_MODEL_NORMALS, isChecked ); - NewDisplay(); - return; - - case ID_MENU3D_FL_RENDER_MATERIAL: - GetPrm3DVisu().SetFlag( FL_RENDER_MATERIAL, isChecked ); - NewDisplay(); - return; - - case ID_MENU3D_FL_RENDER_SHOW_MODEL_BBOX: - GetPrm3DVisu().SetFlag( FL_RENDER_SHOW_MODEL_BBOX, isChecked ); - NewDisplay(); - return; - - case ID_MENU3D_SHOW_BOARD_BODY: - GetPrm3DVisu().SetFlag( FL_SHOW_BOARD_BODY, isChecked ); - NewDisplay(); - return; - - case ID_MENU3D_AXIS_ONOFF: - GetPrm3DVisu().SetFlag( FL_AXIS, isChecked ); - m_canvas->Refresh(); - return; - - case ID_MENU3D_MODULE_ONOFF: - GetPrm3DVisu().SetFlag( FL_MODULE, isChecked ); - m_canvas->Refresh(); - return; - - case ID_MENU3D_USE_COPPER_THICKNESS: - GetPrm3DVisu().SetFlag( FL_USE_COPPER_THICKNESS, isChecked ); - NewDisplay( GL_ID_BOARD ); - NewDisplay( GL_ID_TECH_LAYERS ); - return; - - case ID_MENU3D_ZONE_ONOFF: - GetPrm3DVisu().SetFlag( FL_ZONE, isChecked ); - NewDisplay( GL_ID_BOARD ); - return; - - case ID_MENU3D_ADHESIVE_ONOFF: - GetPrm3DVisu().SetFlag( FL_ADHESIVE, isChecked ); - NewDisplay( GL_ID_TECH_LAYERS ); - return; - - case ID_MENU3D_SILKSCREEN_ONOFF: - GetPrm3DVisu().SetFlag( FL_SILKSCREEN, isChecked ); - NewDisplay( GL_ID_TECH_LAYERS ); - return; - - case ID_MENU3D_SOLDER_MASK_ONOFF: - GetPrm3DVisu().SetFlag( FL_SOLDERMASK, isChecked ); - NewDisplay( GL_ID_TECH_LAYERS ); - return; - - case ID_MENU3D_SOLDER_PASTE_ONOFF: - GetPrm3DVisu().SetFlag( FL_SOLDERPASTE, isChecked ); - NewDisplay( GL_ID_TECH_LAYERS ); - return; - - case ID_MENU3D_COMMENTS_ONOFF: - GetPrm3DVisu().SetFlag( FL_COMMENTS, isChecked ); - NewDisplay( GL_ID_AUX_LAYERS ); - return; - - case ID_MENU3D_ECO_ONOFF: - GetPrm3DVisu().SetFlag( FL_ECO, isChecked ); - NewDisplay( GL_ID_AUX_LAYERS ); - return; - - default: - wxLogMessage( wxT( "EDA_3D_FRAME::Process_Special_Functions() error: unknown command" ) ); - return; - } - - m_canvas->Refresh( true ); - m_canvas->DisplayStatus(); -} - - -void EDA_3D_FRAME::On3DGridSelection( wxCommandEvent& event ) -{ - int id = event.GetId(); - - for( int ii = ID_MENU3D_GRID_NOGRID; ii < ID_MENU3D_GRID_END; ii++ ) - { - if( event.GetId() == ii ) - continue; - - GetMenuBar()->Check( ii, false ); - } - - switch( id ) - { - case ID_MENU3D_GRID_NOGRID: - GetPrm3DVisu().SetFlag( FL_GRID, false ); - break; - - case ID_MENU3D_GRID_10_MM: - GetPrm3DVisu().SetFlag( FL_GRID, true ); - GetPrm3DVisu().m_3D_Grid = 10.0; - break; - - case ID_MENU3D_GRID_5_MM: - GetPrm3DVisu().SetFlag( FL_GRID, true ); - GetPrm3DVisu().m_3D_Grid = 5.0; - break; - - case ID_MENU3D_GRID_2P5_MM: - GetPrm3DVisu().SetFlag( FL_GRID, true ); - GetPrm3DVisu().m_3D_Grid = 2.5; - break; - - case ID_MENU3D_GRID_1_MM: - GetPrm3DVisu().SetFlag( FL_GRID, true ); - GetPrm3DVisu().m_3D_Grid = 1.0; - break; - - default: - wxLogMessage( wxT( "EDA_3D_FRAME::On3DGridSelection() error: unknown command" ) ); - return; - } - - NewDisplay( GL_ID_GRID ); -} - - -void EDA_3D_FRAME::NewDisplay( int aGlList ) -{ - m_canvas->ClearLists( aGlList ); - - // Rebuild the 3D board and refresh the view on reload request: - if( m_reloadRequest ) - m_canvas->ReportWarnings( true ); - - m_canvas->Refresh( true ); - - m_canvas->DisplayStatus(); - m_reloadRequest = false; -} - - -void EDA_3D_FRAME::OnActivate( wxActivateEvent& event ) -{ - // Reload data if 3D frame shows a board, - // because it can be changed since last frame activation - if( m_reloadRequest ) - NewDisplay(); - - event.Skip(); // required under wxMAC -} - - -bool EDA_3D_FRAME::Set3DColorFromUser( S3D_COLOR &aColor, const wxString& aTitle, - wxColourData* aPredefinedColors ) -{ - wxColour newcolor, oldcolor; - - oldcolor.Set( KiROUND( aColor.m_Red * 255 ), - KiROUND( aColor.m_Green * 255 ), - KiROUND( aColor.m_Blue * 255 ) ); - - wxColourData emptyColorSet; // Provides a empty predefined set of colors - // if no color set available to avoid use of an - // old color set - - if( aPredefinedColors == NULL ) - aPredefinedColors = &emptyColorSet; - - newcolor = wxGetColourFromUser( this, oldcolor, aTitle, aPredefinedColors ); - - if( !newcolor.IsOk() ) // Cancel command - return false; - - if( newcolor != oldcolor ) - { - aColor.m_Red = (double) newcolor.Red() / 255.0; - aColor.m_Green = (double) newcolor.Green() / 255.0; - aColor.m_Blue = (double) newcolor.Blue() / 255.0; - } - - return true; -} - - -bool EDA_3D_FRAME::Set3DSilkScreenColorFromUser() -{ - wxColourData definedColors; - - definedColors.SetCustomColour(0, wxColour( 241, 241, 241 ) ); // White - definedColors.SetCustomColour(1, wxColour( 180, 180, 180 ) ); // Gray - - bool change = Set3DColorFromUser( GetPrm3DVisu().m_SilkScreenColor, - _( "Silk Screen Color" ), - &definedColors ); - - if( change ) - NewDisplay( GL_ID_TECH_LAYERS ); - - return change; -} - - -bool EDA_3D_FRAME::Set3DSolderMaskColorFromUser() -{ - wxColourData definedColors; - - definedColors.SetCustomColour(0, wxColour( 20, 51, 36 ) ); // Green - definedColors.SetCustomColour(1, wxColour( 43, 10, 65 ) ); // Purple - definedColors.SetCustomColour(2, wxColour( 117, 19, 21 ) ); // Red - definedColors.SetCustomColour(3, wxColour( 54, 79, 116) ); // Light blue - definedColors.SetCustomColour(4, wxColour( 11, 11, 11 ) ); // Black - definedColors.SetCustomColour(5, wxColour( 241, 241,241) ); // White - - bool change = Set3DColorFromUser( GetPrm3DVisu().m_SolderMaskColor, - _( "Solder Mask Color" ), - &definedColors ); - - if( change ) - NewDisplay( GL_ID_TECH_LAYERS ); - - return change; -} - - -bool EDA_3D_FRAME::Set3DCopperColorFromUser() -{ - wxColourData definedColors; - - definedColors.SetCustomColour( 0, wxColour( 184, 115, 50 ) ); // Copper - definedColors.SetCustomColour( 1, wxColour( 233, 221, 82 ) ); // Gold - definedColors.SetCustomColour( 2, wxColour( 213, 213, 213) ); // Silver - definedColors.SetCustomColour( 3, wxColour( 160, 160, 160) ); // tin - - - bool change = Set3DColorFromUser( GetPrm3DVisu().m_CopperColor, - _( "Copper Color" ), - &definedColors ); - - if( change ) - NewDisplay( GL_ID_BOARD ); - - return change; -} - - -bool EDA_3D_FRAME::Set3DBoardBodyColorFromUser() -{ - wxColourData definedColors; - - definedColors.SetCustomColour( 0, wxColour( 51, 43, 22 ) ); // FR4 natural, dark - definedColors.SetCustomColour( 1, wxColour( 109, 116, 75 ) ); // FR4 natural - definedColors.SetCustomColour( 2, wxColour( 78, 14, 5 ) ); // brown/red - definedColors.SetCustomColour( 3, wxColour( 146, 99, 47 ) ); // brown 1 - definedColors.SetCustomColour( 4, wxColour( 160, 123, 54 ) ); // brown 2 - definedColors.SetCustomColour( 5, wxColour( 146, 99, 47 ) ); // brown 3 - definedColors.SetCustomColour( 6, wxColour( 63, 126, 71 ) ); // green 1 - definedColors.SetCustomColour( 7, wxColour( 117, 122, 90 ) ); // green 2 - - bool change = Set3DColorFromUser( GetPrm3DVisu().m_BoardBodyColor, - _( "Board Body Color" ), - &definedColors ); - - if( change ) - NewDisplay( GL_ID_BOARD ); - - return change; -} - - -bool EDA_3D_FRAME::Set3DSolderPasteColorFromUser() -{ - wxColourData definedColors; - - definedColors.SetCustomColour(0, wxColour( 128, 128, 128 ) ); // grey - definedColors.SetCustomColour(1, wxColour( 213, 213, 213 ) ); // Silver - definedColors.SetCustomColour(2, wxColour( 90, 90, 90 ) ); // grey 2 - - bool change = Set3DColorFromUser( GetPrm3DVisu().m_SolderPasteColor, - _( "Solder Paste Color" ), - &definedColors ); - - if( change ) - NewDisplay( GL_ID_TECH_LAYERS ); - - return change; -} - - -BOARD* EDA_3D_FRAME::GetBoard() -{ - return Parent()->GetBoard(); -} - - -INFO3D_VISU& EDA_3D_FRAME::GetPrm3DVisu() const -{ - // return the INFO3D_VISU which contains the current parameters - // to draw the 3D view of the board - return g_Parm_3D_Visu; -} - - -bool EDA_3D_FRAME::IsEnabled( DISPLAY3D_FLG aItem ) const -{ - // return true if aItem must be displayed - return GetPrm3DVisu().GetFlag( aItem ); -} diff --git a/3d-viewer/3d_material.cpp b/3d-viewer/3d_material.cpp deleted file mode 100644 index 20d48038d0..0000000000 --- a/3d-viewer/3d_material.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ -/** - * @file 3d_class.cpp - */ - -#include -#include <3d_struct.h> -#include <3d_material.h> -#include - -#ifdef __WXMAC__ -# ifdef __DARWIN__ -# include -# else -# include -# endif -#else -# include -#endif - -S3D_MATERIAL::S3D_MATERIAL( S3D_MASTER* father, const wxString& name ) : - EDA_ITEM( father, NOT_USED ) -{ - m_Name = name; - m_AmbientColor.clear(); - m_DiffuseColor.clear(); - m_EmissiveColor.clear(); - m_SpecularColor.clear(); - m_Shininess.clear(); - m_Transparency.clear(); - m_ColorPerVertex = false; -} - - -void SetOpenGlDefaultMaterial() -{ - glm::vec4 ambient( 0.2f, 0.2f, 0.2f, 1.0f ); - glm::vec4 specular( 0.0f, 0.0f, 0.0f, 1.0f ); - glm::vec4 emissive( 0.0f, 0.0f, 0.0f, 1.0f ); - glm::vec4 diffuse( 0.0f, 0.0f, 0.0f, 1.0f ); - GLint shininess_value = 0; - - glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); - glMateriali ( GL_FRONT_AND_BACK, GL_SHININESS, shininess_value ); - glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.x ); - glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.x ); - glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.x ); - glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.x ); - -} - - -bool S3D_MATERIAL::SetOpenGLMaterial( unsigned int aMaterialIndex, bool aUseMaterial ) -{ - if( aUseMaterial ) - { - float transparency_value = 0.0f; - - if( m_Transparency.size() > aMaterialIndex ) - { - transparency_value = m_Transparency[aMaterialIndex]; - } - else - { - if( m_Transparency.size() > 0 ) - transparency_value = m_Transparency[0]; - } - - if( m_DiffuseColor.size() > aMaterialIndex ) - { - glm::vec3 color = m_DiffuseColor[aMaterialIndex]; - - glColor4f( color.x, color.y, color.z, 1.0f - transparency_value ); - } - else - { - if( m_DiffuseColor.size() == 0 ) - { - glColor4f( 0.8f, 0.8f, 0.8f, 1.0f ); - } - } - - if( m_Shininess.size() > 0 ) - { - glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m_Shininess[0] ); - } - - // emissive - if( m_EmissiveColor.size() > aMaterialIndex ) - { - glm::vec4 emissive; - emissive[0] = m_EmissiveColor[aMaterialIndex].x; - emissive[1] = m_EmissiveColor[aMaterialIndex].y; - emissive[2] = m_EmissiveColor[aMaterialIndex].z; - emissive[3] = 1.0f; - glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.x ); - } - - // specular - if( m_SpecularColor.size() > aMaterialIndex ) - { - glm::vec4 specular; - specular[0] = m_SpecularColor[aMaterialIndex].x; - specular[1] = m_SpecularColor[aMaterialIndex].y; - specular[2] = m_SpecularColor[aMaterialIndex].z; - specular[3] = 1.0f; - glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.x ); - } - - // ambient - if( m_AmbientColor.size() > aMaterialIndex ) - { - glm::vec4 ambient; - ambient[0] = m_AmbientColor[aMaterialIndex].x; - ambient[1] = m_AmbientColor[aMaterialIndex].y; - ambient[2] = m_AmbientColor[aMaterialIndex].z; - ambient[3] = 1.0f; - glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.x ); - } - - return (transparency_value != 0.0f); - } - else - { - if( m_DiffuseColor.size() > aMaterialIndex ) - { - glm::vec3 color = m_DiffuseColor[aMaterialIndex]; - glColor4f( color.x, color.y, color.z, 1.0 ); - } - } - - return false; -} - diff --git a/3d-viewer/3d_material.h b/3d-viewer/3d_material.h deleted file mode 100644 index 3bff7e600c..0000000000 --- a/3d-viewer/3d_material.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_material.h - */ - -#ifndef STRUCT_3D_MATERIAL_H -#define STRUCT_3D_MATERIAL_H - -#include -#include -#define GLM_FORCE_RADIANS -#include - -class S3D_MASTER; - -class S3D_MATERIAL : public EDA_ITEM // openGL "material" data -{ -public: - wxString m_Name; - - // Material list - std::vector< glm::vec3 > m_AmbientColor; - std::vector< glm::vec3 > m_DiffuseColor; - std::vector< glm::vec3 > m_EmissiveColor; - std::vector< glm::vec3 > m_SpecularColor; - std::vector< float > m_Shininess; - std::vector< float > m_Transparency; - bool m_ColorPerVertex; - -public: - S3D_MATERIAL( S3D_MASTER* father, const wxString& name ); - - S3D_MATERIAL* Next() const { return (S3D_MATERIAL*) Pnext; } - S3D_MATERIAL* Back() const { return (S3D_MATERIAL*) Pback; } - - /** - * Initialize the material prms. - * @param aMaterialIndex = the index in list of available materials - * @param aUseMaterial = true to use the values found in the available material - * = false to use only the color, and other prms are fixed - * @return true if the material is transparency - */ - bool SetOpenGLMaterial(unsigned int aMaterialIndex, bool aUseMaterial); - -#if defined(DEBUG) - void Show( int nestLevel, std::ostream& os ) const { ShowDummy( os ); } // override -#endif - - /** Get class name - * @return string "S3D_MATERIAL" - */ - virtual wxString GetClass() const - { - return wxT( "S3D_MATERIAL" ); - } -}; - -void SetOpenGlDefaultMaterial(); - -#endif diff --git a/3d-viewer/3d_math.cpp b/3d-viewer/3d_math.cpp new file mode 100644 index 0000000000..f4201ca6d6 --- /dev/null +++ b/3d-viewer/3d_math.cpp @@ -0,0 +1,31 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file 3d_math.cpp + * @brief + */ + + +#include "3d_math.h" diff --git a/3d-viewer/3d_math/3d_math.h b/3d-viewer/3d_math.h similarity index 53% rename from 3d-viewer/3d_math/3d_math.h rename to 3d-viewer/3d_math.h index 02ee6ce4e1..00c0aba7b7 100644 --- a/3d-viewer/3d_math/3d_math.h +++ b/3d-viewer/3d_math.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,8 +30,8 @@ #ifndef _3D_MATH_H #define _3D_MATH_H -#include "plugins/3dapi/xv3d_types.h" - +#include +#include "3d_fastmath.h" // https://en.wikipedia.org/wiki/Spherical_coordinate_system /** @@ -53,13 +53,21 @@ inline SFVEC3F SphericalToCartesian( float aInclination, float aAzimuth ) // !TODO: this is not correct because it is not a gaussian random inline SFVEC3F UniformRandomHemisphereDirection( ) { - SFVEC3F b( (rand()/(float)RAND_MAX) - 0.5f, (rand()/(float)RAND_MAX) - 0.5f, (rand()/(float)RAND_MAX) - 0.5f); + // It was experienced that this function is slow! do not use it :/ + // SFVEC3F b( (rand()/(float)RAND_MAX) - 0.5f, + // (rand()/(float)RAND_MAX) - 0.5f, + // (rand()/(float)RAND_MAX) - 0.5f ); + + SFVEC3F b( Fast_RandFloat() * 0.5f, + Fast_RandFloat() * 0.5f, + Fast_RandFloat() * 0.5f ); + return b; } // https://pathtracing.wordpress.com/2011/03/03/cosine-weighted-hemisphere/ -inline SFVEC3F CosWeightedRandomHemisphereDirection( SFVEC3F n ) +inline SFVEC3F CosWeightedRandomHemisphereDirection( const SFVEC3F &n ) { const float Xi1 = (float)rand() / (float)RAND_MAX; const float Xi2 = (float)rand() / (float)RAND_MAX; @@ -74,9 +82,9 @@ inline SFVEC3F CosWeightedRandomHemisphereDirection( SFVEC3F n ) const SFVEC3F y( n.x, n.y, n.z ); SFVEC3F h = y; - if (fabs( h.x ) <= fabs( h.y ) && fabs( h.x ) <= fabs( h.z ) ) + if( fabs( h.x ) <= fabs( h.y ) && fabs( h.x ) <= fabs( h.z ) ) h.x= 1.0f; - else if (fabs(h.y)<=fabs(h.x) && fabs(h.y)<=fabs(h.z)) + else if( fabs( h.y ) <= fabs( h.x ) && fabs( h.y ) <= fabs( h.z ) ) h.y= 1.0f; else h.z= 1.0f; @@ -92,15 +100,20 @@ inline SFVEC3F CosWeightedRandomHemisphereDirection( SFVEC3F n ) /** * @brief Refract - * Based on: https://github.com/mmp/pbrt-v3/blob/master/src/core/reflection.h - * See also: http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_3_Refractions_and_Beers_Law.shtml + * Based on: + * https://github.com/mmp/pbrt-v3/blob/master/src/core/reflection.h + * See also: + * http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_3_Refractions_and_Beers_Law.shtml * @param aInVector incoming vector * @param aNormal normal in the intersection point * @param aRin_over_Rout incoming refraction index / out refraction index * @param aOutVector the refracted vector * @return true */ -inline bool Refract( const SFVEC3F &aInVector, const SFVEC3F &aNormal, float aRin_over_Rout, SFVEC3F &aOutVector ) +inline bool Refract( const SFVEC3F &aInVector, + const SFVEC3F &aNormal, + float aRin_over_Rout, + SFVEC3F &aOutVector ) { float cosThetaI = -glm::dot( aNormal, aInVector ); float sin2ThetaI = glm::max( 0.0f, 1.0f - cosThetaI * cosThetaI ); @@ -111,15 +124,66 @@ inline bool Refract( const SFVEC3F &aInVector, const SFVEC3F &aNormal, float aRi return false; float cosThetaT = sqrtf( 1.0f - sin2ThetaT ); - aOutVector = glm::normalize( aRin_over_Rout * aInVector + ( aRin_over_Rout * cosThetaI - cosThetaT ) * aNormal ); + + aOutVector = glm::normalize( aRin_over_Rout * aInVector + + ( aRin_over_Rout * cosThetaI - cosThetaT ) * + aNormal ); return true; } -inline float mapf( float x, float in_min, float in_max, float out_min, float out_max) + +inline float mapf( float x, + float in_min, + float in_max, + float out_min, + float out_max) { x = glm::clamp( x, in_min, in_max ); + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } + +inline SFVEC3F MaterialDiffuseToColorCAD( const SFVEC3F &aDiffuseColor ) +{ + // convert to a discret scale of grays + const float luminance = glm::min( (((float)((unsigned int) ( 4.0f * + (aDiffuseColor.r * 0.2126f + + aDiffuseColor.g * 0.7152f + + aDiffuseColor.b * 0.0722f))) + 0.5f) / + 4.0f) * 1.0f, + 1.0f ); + + const float maxValue = glm::max( glm::max( glm::max( aDiffuseColor.r, + aDiffuseColor.g), + aDiffuseColor.b ), + FLT_EPSILON ); + + return (aDiffuseColor / SFVEC3F(maxValue) ) * 0.125f + luminance* 0.875f; +} + + +// http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJ4KngqMiIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MCwiZXEiOiItKCh4LTEpXjIpKjIrMSIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0xLjM4NzUwMDAwMDAwMDAwMDIiLCIxLjg2MjQ5OTk5OTk5OTk5OTgiLCItMC43IiwiMS4zIl19XQ-- +inline float QuadricEasingInOut( float t ) +{ + if( t <= 0.5f ) + { + return t * t * 2.0f; + } + else + { + t = t - 1.0f; + + return -2.0f * (t * t) + 1.0f; + } +} + + +// http://www.wolframalpha.com/input/?i=t%5E2(3-2t) +inline float BezierBlend( float t ) +{ + return t * t * ( 3.0f - 2.0f * t ); +} + #endif // 3D_MATH_H diff --git a/3d-viewer/3d_mesh_model.cpp b/3d-viewer/3d_mesh_model.cpp deleted file mode 100644 index 914d4a4f5a..0000000000 --- a/3d-viewer/3d_mesh_model.cpp +++ /dev/null @@ -1,1027 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_mesh_model.cpp - * @brief - */ - -#include -#include <3d_mesh_model.h> -#include -#define GLM_FORCE_RADIANS -#include -#include - -#ifdef __WXMAC__ -# ifdef __DARWIN__ -# include -# else -# include -# endif -#else -# include -#endif - -#ifdef USE_OPENMP -#include -#endif // USE_OPENMP - -#include "info3d_visu.h" - - -S3D_MESH::S3D_MESH() -{ - isPerFaceNormalsComputed = false; - isPointNormalizedComputed = false; - isPerPointNormalsComputed = false; - isPerVertexNormalsVerified = false; - m_Materials = NULL; - childs.clear(); - - m_translation = glm::vec3( 0.0f, 0.0f, 0.0f ); - m_rotation = glm::vec4( 0.0f, 0.0f, 0.0f, 0.0f ); - m_scale = glm::vec3( 1.0f, 1.0f, 1.0f ); -} - - -S3D_MESH::~S3D_MESH() -{ -} - - -CBBOX &S3D_MESH::getBBox( ) -{ - if( !m_BBox.IsInitialized() ) - calcBBoxAllChilds(); - - return m_BBox; -} - - -void S3D_MESH::calcBBoxAllChilds( ) -{ - // Calc your own boudingbox - calcBBox(); - - for( unsigned int idx = 0; idx < childs.size(); idx++ ) - m_BBox.Union( childs[idx]->getBBox() ); - - CBBOX tmpBBox = m_BBox; - - // Calc transformation matrix - glm::mat4 fullTransformMatrix; - glm::mat4 translationMatrix = glm::translate( glm::mat4(), m_translation ); - - if( m_rotation[3] != 0.0f ) - { - glm::mat4 rotationMatrix = glm::rotate( translationMatrix, glm::radians( m_rotation[3] ), - S3D_VERTEX( m_rotation[0], m_rotation[1], m_rotation[2] ) ); - fullTransformMatrix = glm::scale( rotationMatrix, m_scale ); - } - else - fullTransformMatrix = glm::scale( translationMatrix, m_scale ); - - - // Apply transformation - m_BBox.Set( S3D_VERTEX( fullTransformMatrix * glm::vec4( tmpBBox.Min(), 1.0f ) ), - S3D_VERTEX( fullTransformMatrix * glm::vec4( tmpBBox.Max(), 1.0f ) ) ); -} - - -void S3D_MESH::calcBBox( ) -{ - m_BBox.Reset(); - - // Calc boudingbox for all coords - for( unsigned int idx = 0; idx < m_CoordIndex.size(); idx++ ) - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - m_BBox.Union( m_Point[m_CoordIndex[idx][ii]] ); -} - - -void S3D_MESH::openGL_RenderAllChilds( bool aIsRenderingJustNonTransparentObjects, - bool aIsRenderingJustTransparentObjects ) -{ - glEnable( GL_COLOR_MATERIAL ) ; - SetOpenGlDefaultMaterial(); - - glPushMatrix(); - glTranslatef( m_translation.x, m_translation.y, m_translation.z ); - glRotatef( m_rotation[3], m_rotation[0], m_rotation[1], m_rotation[2] ); - glScalef( m_scale.x, m_scale.y, m_scale.z ); - - // Render your self - openGL_Render( aIsRenderingJustNonTransparentObjects, - aIsRenderingJustTransparentObjects ); - - // Render childs recursively - for( unsigned int idx = 0; idx < childs.size(); idx++ ) - { - childs[idx]->openGL_RenderAllChilds( aIsRenderingJustNonTransparentObjects, - aIsRenderingJustTransparentObjects ); - } - - SetOpenGlDefaultMaterial(); - - glPopMatrix(); -} - - -void S3D_MESH::openGL_Render( bool aIsRenderingJustNonTransparentObjects, - bool aIsRenderingJustTransparentObjects ) -{ - if( (aIsRenderingJustNonTransparentObjects == true) && - (aIsRenderingJustTransparentObjects == true) ) - { - return; - } - - //DBG( printf( "openGL_Render" ) ); - bool useMaterial = g_Parm_3D_Visu.GetFlag( FL_RENDER_MATERIAL ); - bool smoothShapes = g_Parm_3D_Visu.IsRealisticMode() - && g_Parm_3D_Visu.GetFlag( FL_RENDER_SMOOTH_NORMALS ); - - if( m_CoordIndex.size() == 0 ) - { - return; - } -/* - // DEBUG INFO - printf("aIsRenderingJustNonTransparentObjects %d aIsRenderingJustTransparentObjects %d\n", aIsRenderingJustNonTransparentObjects, aIsRenderingJustTransparentObjects); - - printf("m_CoordIndex.size() %lu\n", m_CoordIndex.size() ); - printf("m_MaterialIndexPerFace.size() %lu\n", m_MaterialIndexPerFace.size() ); - printf("m_MaterialIndexPerVertex.size() %lu\n", m_MaterialIndexPerVertex.size() ); - printf("m_PerVertexNormalsNormalized.size() %lu\n", m_PerVertexNormalsNormalized.size() ); - printf("m_PerFaceVertexNormals.size() %lu\n", m_PerFaceVertexNormals.size() ); - printf("m_PerFaceNormalsNormalized.size() %lu\n", m_PerFaceNormalsNormalized.size() ); - - printf("smoothShapes %d\n", smoothShapes ); - - if( m_Materials ) - { - printf(" m_Name %s\n", static_cast(m_Materials->m_Name.c_str()) ); - printf(" m_ColorPerVertex %d\n", m_Materials->m_ColorPerVertex ); - printf(" m_Transparency.size() %lu\n", m_Materials->m_Transparency.size() ); - printf(" m_DiffuseColor.size() %lu\n", m_Materials->m_DiffuseColor.size() ); - printf(" m_Shininess.size() %lu\n", m_Materials->m_Shininess.size() ); - printf(" m_EmissiveColor.size() %lu\n", m_Materials->m_EmissiveColor.size() ); - printf(" m_SpecularColor.size() %lu\n", m_Materials->m_SpecularColor.size() ); - printf(" m_AmbientColor.size() %lu\n", m_Materials->m_AmbientColor.size() ); - } - printf("m_Materials %p\n", ( void * )m_Materials ); -*/ - - float lastTransparency_value = 0.0f; - - if( m_Materials ) - { - bool isTransparent = m_Materials->SetOpenGLMaterial( 0, useMaterial ); - - if( isTransparent && aIsRenderingJustNonTransparentObjects ) - return; - - if( !isTransparent && aIsRenderingJustTransparentObjects ) - return; - - // Skip total transparent models - if( useMaterial ) - if( m_Materials->m_Transparency.size() > 0 ) - { - lastTransparency_value = m_Materials->m_Transparency[0]; - - if( lastTransparency_value >= 1.0f ) - return; - } - } - - glPushMatrix(); - glTranslatef( m_translation.x, m_translation.y, m_translation.z ); - glRotatef( m_rotation[3], m_rotation[0], m_rotation[1], m_rotation[2] ); - glScalef( m_scale.x, m_scale.y, m_scale.z ); - - calcPointNormalized(); - calcPerFaceNormals(); - - if( smoothShapes ) - { - if( (m_PerVertexNormalsNormalized.size() > 0) && - g_Parm_3D_Visu.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ) - perVertexNormalsVerify_and_Repair(); - else - calcPerPointNormals(); - - } -/* -#if defined(DEBUG) - // Debug Normals - glColor4f( 1.0, 0.0, 1.0, 0.7 ); - for( unsigned int idx = 0; idx < m_CoordIndex.size(); idx++ ) - { - if( m_PerFaceNormalsNormalized.size() > 0 ) - { - S3D_VERTEX normal = m_PerFaceNormalsNormalized[idx]; - //glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][0]]; - for( unsigned int ii = 1; ii < m_CoordIndex[idx].size(); ii++ ) - { - point += m_Point[m_CoordIndex[idx][ii]]; - } - - point /= m_CoordIndex[idx].size(); - - glBegin( GL_LINES ); - glVertex3fv( &point.x ); - point += normal * 0.01f; - glVertex3fv( &point.x ); - glEnd(); - } - } - - // Restore material - if( m_Materials ) - m_Materials->SetOpenGLMaterial( 0, useMaterial ); -#endif -*/ - -/* -#if defined(DEBUG) - if( smoothShapes ) - { - // Debug Per Vertex Normals - glColor4f( 0.0, 1.0, 1.0, 0.7 ); - for( unsigned int idx = 0; idx < m_CoordIndex.size(); idx++ ) - { - if( (m_PerVertexNormalsNormalized.size() > 0) && - g_Parm_3D_Visu.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ) - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - glm::vec3 normal = m_PerVertexNormalsNormalized[m_NormalIndex[idx][ii]]; - //glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glBegin( GL_LINES ); - glVertex3fv( &point.x ); - point += normal * 1.00f; - glVertex3fv( &point.x ); - glEnd(); - } - } - else - { - std::vector< glm::vec3 > normals_list; - normals_list = m_PerFaceVertexNormals[idx]; - - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - glm::vec3 normal = normals_list[ii]; - printf("normal(%f, %f, %f), ", normal.x, normal.y, normal.z ); - - //glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glBegin( GL_LINES ); - glVertex3fv( &point.x ); - point += normal * 1.00f; - glVertex3fv( &point.x ); - glEnd(); - } - printf("\n"); - } - } - - // Restore material - if( m_Materials ) - m_Materials->SetOpenGLMaterial( 0, useMaterial ); - } -#endif -*/ - - if( m_Materials && m_Materials->m_ColorPerVertex == false ) - { - if( m_Materials->m_DiffuseColor.size() == m_Point.size() ) - m_Materials->m_ColorPerVertex = true; - } - - for( unsigned int idx = 0; idx < m_CoordIndex.size(); idx++ ) - { - if( m_Materials ) - { - // http://accad.osu.edu/~pgerstma/class/vnv/resources/info/AnnotatedVrmlRef/ch3-323.htm - // "If colorPerVertex is FALSE, colours are applied to each face, as follows:" - if( ( m_Materials->m_ColorPerVertex == false ) && - ( m_Materials->m_DiffuseColor.size() > 1 ) ) - { - bool isTransparent; - - // "If the colorIndex field is not empty, then one colour is - // used for each face of the IndexedFaceSet. There must be - // at least as many indices in the colorIndex field as - // there are faces in the IndexedFaceSet." - if ( m_MaterialIndexPerFace.size() == m_CoordIndex.size() ) - { - isTransparent = m_Materials->SetOpenGLMaterial( m_MaterialIndexPerFace[idx], useMaterial ); - - // Skip total transparent faces - if( useMaterial ) - if( (int)m_Materials->m_Transparency.size() > m_MaterialIndexPerFace[idx] ) - { - if( m_Materials->m_Transparency[m_MaterialIndexPerFace[idx]] >= 1.0f ) - continue; - } - } - else - { - // "If the colorIndex field is empty, then the colours in the - // Color node are applied to each face of the IndexedFaceSet - // in order. There must be at least as many colours in the - // Color node as there are faces." - isTransparent = m_Materials->SetOpenGLMaterial( idx, useMaterial ); - - // Skip total transparent faces - if( useMaterial ) - if( m_Materials->m_Transparency.size() > idx ) - { - if( m_Materials->m_Transparency[idx] >= 1.0f ) - continue; - } - } - - if( isTransparent && aIsRenderingJustNonTransparentObjects ) - continue; - - if( !isTransparent && aIsRenderingJustTransparentObjects ) - continue; - } - } - - switch( m_CoordIndex[idx].size() ) - { - case 3: - glBegin( GL_TRIANGLES ); - break; - case 4: - glBegin( GL_QUADS ); - break; - default: - glBegin( GL_POLYGON ); - break; - } - - - if( smoothShapes ) - { - if( m_Materials ) - { - // for VRML2: - // http://accad.osu.edu/~pgerstma/class/vnv/resources/info/AnnotatedVrmlRef/ch3-323.htm - // "If colorPerVertex is TRUE, colours are applied to each vertex, as follows: - if( ( m_Materials->m_ColorPerVertex == true ) && - ( m_Materials->m_DiffuseColor.size() > 1 ) ) - { - // "If the colorIndex field is not empty, then colours - // are applied to each vertex of the IndexedFaceSet in - // exactly the same manner that the coordIndex field is - // used to choose coordinates for each vertex from the - // Coordinate node. The colorIndex field must contain at - // least as many indices as the coordIndex field, and - // must contain end-of-face markers (-1) in exactly the - // same places as the coordIndex field. If the greatest - // index in the colorIndex field is N, then there must - // be N+1 colours in the Color node." - if ( m_MaterialIndexPerVertex.size() != 0 ) - { - if( (m_PerVertexNormalsNormalized.size() > 0) && - g_Parm_3D_Visu.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ) - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX color = m_Materials->m_DiffuseColor[m_MaterialIndexPerVertex[idx][ii]]; - glColor4f( color.x, color.y, color.z, 1.0f - lastTransparency_value ); - - glm::vec3 normal = m_PerVertexNormalsNormalized[m_NormalIndex[idx][ii]]; - glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - else - { - std::vector< glm::vec3 > normals_list; - normals_list = m_PerFaceVertexNormals[idx]; - - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX color = m_Materials->m_DiffuseColor[m_MaterialIndexPerVertex[idx][ii]]; - glColor4f( color.x, color.y, color.z, 1.0f - lastTransparency_value ); - - glm::vec3 normal = normals_list[ii]; - glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - } - else - { - // "If the colorIndex field is empty, then the - // coordIndex field is used to choose colours from - // the Color node. If the greatest index in the - // coordIndex field is N, then there must be N+1 - // colours in the Color node." - - - if( (m_PerVertexNormalsNormalized.size() > 0) && - g_Parm_3D_Visu.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ) - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX color = m_Materials->m_DiffuseColor[m_CoordIndex[idx][ii]]; - glColor4f( color.x, color.y, color.z, 1.0f - lastTransparency_value ); - - glm::vec3 normal = m_PerVertexNormalsNormalized[m_NormalIndex[idx][ii]]; - glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - else - { - std::vector< glm::vec3 > normals_list; - normals_list = m_PerFaceVertexNormals[idx]; - - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX color = m_Materials->m_DiffuseColor[m_CoordIndex[idx][ii]]; - glColor4f( color.x, color.y, color.z, 1.0f - lastTransparency_value ); - - glm::vec3 normal = normals_list[ii]; - glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - } - } - else - { - if( (m_PerVertexNormalsNormalized.size() > 0) && - g_Parm_3D_Visu.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ) - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - glm::vec3 normal = m_PerVertexNormalsNormalized[m_NormalIndex[idx][ii]]; - glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - else - { - std::vector< glm::vec3 > normals_list; - normals_list = m_PerFaceVertexNormals[idx]; - - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - glm::vec3 normal = normals_list[ii]; - glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - } - } - else - { - if( (m_PerVertexNormalsNormalized.size() > 0) && - g_Parm_3D_Visu.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ) - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - glm::vec3 normal = m_PerVertexNormalsNormalized[m_NormalIndex[idx][ii]]; - glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - else - { - std::vector< glm::vec3 > normals_list; - normals_list = m_PerFaceVertexNormals[idx]; - - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - glm::vec3 normal = normals_list[ii]; - glNormal3fv( &normal.x ); - - glm::vec3 point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - } - } - else - { - // Flat - if( m_PerFaceNormalsNormalized.size() > 0 ) - { - S3D_VERTEX normal = m_PerFaceNormalsNormalized[idx]; - glNormal3fv( &normal.x ); - - if( m_Materials ) - { - // for VRML2: - // http://accad.osu.edu/~pgerstma/class/vnv/resources/info/AnnotatedVrmlRef/ch3-323.htm - // "If colorPerVertex is TRUE, colours are applied to each vertex, as follows: - if( ( m_Materials->m_ColorPerVertex == true ) && - ( m_Materials->m_DiffuseColor.size() > 1 ) ) - { - // "If the colorIndex field is not empty, then colours - // are applied to each vertex of the IndexedFaceSet in - // exactly the same manner that the coordIndex field is - // used to choose coordinates for each vertex from the - // Coordinate node. The colorIndex field must contain at - // least as many indices as the coordIndex field, and - // must contain end-of-face markers (-1) in exactly the - // same places as the coordIndex field. If the greatest - // index in the colorIndex field is N, then there must - // be N+1 colours in the Color node." - if ( m_MaterialIndexPerVertex.size() != 0 ) - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX color = m_Materials->m_DiffuseColor[m_MaterialIndexPerVertex[idx][ii]]; - glColor4f( color.x, color.y, color.z, 1.0f - lastTransparency_value ); - - S3D_VERTEX point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - else - { - // "If the colorIndex field is empty, then the - // coordIndex field is used to choose colours from - // the Color node. If the greatest index in the - // coordIndex field is N, then there must be N+1 - // colours in the Color node." - - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX color = m_Materials->m_DiffuseColor[m_CoordIndex[idx][ii]]; - glColor4f( color.x, color.y, color.z, 1.0f - lastTransparency_value ); - - S3D_VERTEX point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - } - else - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - } - else - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - } - else - { - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - S3D_VERTEX point = m_Point[m_CoordIndex[idx][ii]]; - glVertex3fv( &point.x ); - } - } - } - - glEnd(); - } - - glPopMatrix(); -} - - -void S3D_MESH::perVertexNormalsVerify_and_Repair() -{ - if( isPerVertexNormalsVerified == true ) - return; - - isPerVertexNormalsVerified = true; - - //DBG( printf( "perVertexNormalsVerify_and_Repair\n" ) ); - - for( unsigned int idx = 0; idx < m_PerVertexNormalsNormalized.size(); idx++ ) - { - glm::vec3 normal = m_PerVertexNormalsNormalized[idx]; - - if( (normal.x == 1.0f) && ((normal.y != 0.0f) || (normal.z != 0.0f)) ) - { - normal.y = 0.0f; - normal.z = 0.0f; - } - else - if( (normal.y == 1.0f) && ((normal.x != 0.0f) || (normal.z != 0.0f)) ) - { - normal.x = 0.0f; - normal.z = 0.0f; - } - else - if( (normal.z == 1.0f) && ((normal.x != 0.0f) || (normal.y != 0.0f)) ) - { - normal.x = 0.0f; - normal.y = 0.0f; - } - else - if( (normal.x < FLT_EPSILON) && (normal.x > -FLT_EPSILON) ) - { - normal.x = 0.0f; - } - else - if( (normal.y < FLT_EPSILON) && (normal.y > -FLT_EPSILON) ) - { - normal.y = 0.0f; - } - else - if( (normal.z < FLT_EPSILON) && (normal.z > -FLT_EPSILON) ) - { - normal.z = 0.0f; - } - - float l = glm::length( normal ); - - if( l > FLT_EPSILON ) // avoid division by zero - { - normal = normal / l; - } - else - { - DBG( printf( " Cannot normalize precomputed normal at idx:%u\n", idx ) ); - } - - m_PerVertexNormalsNormalized[idx] = normal; - } -} - - -void S3D_MESH::calcPointNormalized() -{ - //DBG( printf( "calcPointNormalized\n" ) ); - - if( isPointNormalizedComputed == true ) - return; - - isPointNormalizedComputed = true; - - m_PointNormalized.clear(); - m_PointNormalized.resize( m_Point.size() ); - - float biggerPoint = 0.0f; - for( unsigned int i = 0; i < m_Point.size(); i++ ) - { - float v; - v = fabs( m_Point[i].x ); - if( v > biggerPoint ) - biggerPoint = v; - - v = fabs( m_Point[i].y ); - if( v > biggerPoint ) - biggerPoint = v; - - v = fabs( m_Point[i].z ); - if( v > biggerPoint ) - biggerPoint = v; - } - - for( unsigned int i = 0; i < m_Point.size(); i++ ) - { - m_PointNormalized[i] = m_Point[i] / biggerPoint; - } -} - - -void S3D_MESH::calcPerFaceNormals() -{ - //DBG( printf( "calcPerFaceNormals" ) ); - - if( isPerFaceNormalsComputed == true ) - return; - - isPerFaceNormalsComputed = true; - - bool haveAlreadyNormals_from_model_file = false; - - if( ( m_PerFaceNormalsNormalized.size() > 0 ) && - g_Parm_3D_Visu.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ) - { - haveAlreadyNormals_from_model_file = true; - - // !TODO: this is a workarround for some VRML2 modules files (ex: from we-online.de website) - // are using (incorrectly) the normals with m_CoordIndex as per face normal. - // This maybe be addressed by the parser in the future. - if( ( m_PerFaceNormalsNormalized.size() == m_Point.size() ) && - ( m_PerFaceNormalsNormalized.size() != m_CoordIndex.size() ) ) - { - //DBG( printf("m_PerFaceNormalsNormalized.size() != m_CoordIndex.size() Appling a workarroudn recover\n") ); - m_NormalIndex = m_CoordIndex; - m_PerVertexNormalsNormalized = m_PerFaceNormalsNormalized; - m_PerFaceNormalsNormalized.clear(); - haveAlreadyNormals_from_model_file = false; - } - } - else - { - m_PerFaceNormalsNormalized.clear(); - } - - m_PerFaceNormalsNormalized.resize( m_CoordIndex.size() ); - - m_PerFaceNormalsRaw_X_PerFaceSquaredArea.clear(); - m_PerFaceNormalsRaw_X_PerFaceSquaredArea.resize( m_CoordIndex.size() ); - - // There are no points defined for the coordIndex - if( m_PointNormalized.size() == 0 ) - { - m_CoordIndex.clear(); - return; - } - - for( unsigned int idx = 0; idx < m_CoordIndex.size(); idx++ ) - { - glm::dvec3 cross_prod = glm::dvec3( 0.0, 0.0, 0.0 ); - - // Newell's Method - // http://www.opengl.org/wiki/Calculating_a_Surface_Normal - // http://tog.acm.org/resources/GraphicsGems/gemsiii/newell.c - // http://www.iquilezles.org/www/articles/areas/areas.htm - - for( unsigned int i = 0; i < m_CoordIndex[idx].size(); i++ ) - { - - glm::dvec3 u = glm::dvec3( m_PointNormalized[m_CoordIndex[idx][i]] ); - glm::dvec3 v = glm::dvec3( m_PointNormalized[m_CoordIndex[idx][(i + 1) % m_CoordIndex[idx].size()]] ); - - cross_prod.x += (u.y - v.y) * (u.z + v.z); - cross_prod.y += (u.z - v.z) * (u.x + v.x); - cross_prod.z += (u.x - v.x) * (u.y + v.y); - } - - double area = glm::dot( cross_prod, cross_prod ); - - area = fabs( area ); - - m_PerFaceNormalsRaw_X_PerFaceSquaredArea[idx] = glm::vec3( cross_prod * area ); - - //printf("cross_prod(%g, %g, %g), area:%g m_PerFaceNormalsRaw_X_PerFaceSquaredArea(%f, %f, %f)\n", cross_prod.x, cross_prod.y, cross_prod.z, area, - //m_PerFaceNormalsRaw_X_PerFaceSquaredArea[idx].x, - //m_PerFaceNormalsRaw_X_PerFaceSquaredArea[idx].y, - //m_PerFaceNormalsRaw_X_PerFaceSquaredArea[idx].z); - - if( haveAlreadyNormals_from_model_file == false ) - { - if( g_Parm_3D_Visu.GetFlag( FL_RENDER_USE_MODEL_NORMALS ) && - (m_PerVertexNormalsNormalized.size() > 0) ) - { - glm::dvec3 normalSum; - - for( unsigned int ii = 0; ii < m_CoordIndex[idx].size(); ii++ ) - { - normalSum += glm::dvec3( m_PerVertexNormalsNormalized[m_NormalIndex[idx][ii]] ); - } - - double l = glm::length( normalSum ); - - if( l > DBL_EPSILON ) // avoid division by zero - { - normalSum = normalSum / l; - } - else - { - if( ( normalSum.x > normalSum.y ) && ( normalSum.x > normalSum.z ) ) - { - normalSum.x = 0.0; - normalSum.y = 1.0; - normalSum.z = 0.0; - } - else if( ( normalSum.y > normalSum.x ) && ( normalSum.y > normalSum.z ) ) - { - normalSum.x = 0.0; - normalSum.y = 1.0; - normalSum.z = 0.0; - } - else if( ( normalSum.z > normalSum.x ) && ( normalSum.z > normalSum.y ) ) - { - normalSum.x = 0.0; - normalSum.y = 0.0; - normalSum.z = 1.0; - } - else - { - normalSum.x = 0.0; - normalSum.y = 0.0; - normalSum.z = 0.0; - } - } - - m_PerFaceNormalsNormalized[idx] = glm::vec3( normalSum ); - } - else - { - // normalize vertex normal - double l = glm::length( cross_prod ); - - if( l > DBL_EPSILON ) // avoid division by zero - { - cross_prod = cross_prod / l; - } - else - { - - /* - for( unsigned int i = 0; i < m_CoordIndex[idx].size(); i++ ) - { - glm::vec3 v = m_Point[m_CoordIndex[idx][i]]; - DBG( printf( "v[%u](%f, %f, %f)", i, v.x, v.y, v.z ) ); - } - DBG( printf( "Cannot calc normal idx: %u cross(%g, %g, %g) l:%g m_CoordIndex[idx].size: %u\n", - idx, - cross_prod.x, cross_prod.y, cross_prod.z, - l, - (unsigned int)m_CoordIndex[idx].size()) ); - - */ - - if( ( cross_prod.x > cross_prod.y ) && ( cross_prod.x > cross_prod.z ) ) - { - cross_prod.x = 0.0; - cross_prod.y = 1.0; - cross_prod.z = 0.0; - } - else if( ( cross_prod.y > cross_prod.x ) && ( cross_prod.y > cross_prod.z ) ) - { - cross_prod.x = 0.0; - cross_prod.y = 1.0; - cross_prod.z = 0.0; - } - else if( ( cross_prod.z > cross_prod.x ) && ( cross_prod.z > cross_prod.y ) ) - { - cross_prod.x = 0.0; - cross_prod.y = 0.0; - cross_prod.z = 1.0; - } - else - { - cross_prod.x = 0.0; - cross_prod.y = 0.0; - cross_prod.z = 0.0; - } - } - - m_PerFaceNormalsNormalized[idx] = glm::vec3( cross_prod ); - //printf("normal(%g, %g, %g)\n", m_PerFaceNormalsNormalized[idx].x, m_PerFaceNormalsNormalized[idx].y, m_PerFaceNormalsNormalized[idx].z ); - } - } - } -} - - -// Documentation literature -// http://www.bytehazard.com/code/vertnorm.html -// http://www.emeyex.com/site/tuts/VertexNormals.pdf -void S3D_MESH::calcPerPointNormals() -{ - //DBG( printf( "calcPerPointNormals" ) ); - - if( isPerPointNormalsComputed == true ) - return; - - isPerPointNormalsComputed = true; - - m_PerFaceVertexNormals.clear(); - - // Pre-allocate space for the entire vector of vertex normals so we can do parallel writes - m_PerFaceVertexNormals.resize( m_CoordIndex.size() ); - - for( unsigned int each_face_A_idx = 0; each_face_A_idx < m_CoordIndex.size(); each_face_A_idx++ ) - { - m_PerFaceVertexNormals[each_face_A_idx].resize( m_CoordIndex[each_face_A_idx].size() ); - } - - - // Initialize each vertex normal - for( unsigned int each_face_A_idx = 0; each_face_A_idx < m_CoordIndex.size(); each_face_A_idx++ ) - { - glm::vec3 initVertexFaceNormal = m_PerFaceNormalsRaw_X_PerFaceSquaredArea[each_face_A_idx]; - - std::vector< glm::vec3 >& face_A_normals = m_PerFaceVertexNormals[each_face_A_idx]; - - for( unsigned int each_vert_A_idx = 0; each_vert_A_idx < m_CoordIndex[each_face_A_idx].size(); each_vert_A_idx++ ) - { - face_A_normals[each_vert_A_idx] = initVertexFaceNormal; - } - } - - - #ifdef USE_OPENMP - #pragma omp parallel for - #endif /* USE_OPENMP */ - - // for each face A in mesh - for( unsigned int each_face_A_idx = 0; each_face_A_idx < m_CoordIndex.size(); each_face_A_idx++ ) - { - // n = face A facet normal - std::vector< glm::vec3 >& face_A_normals = m_PerFaceVertexNormals[each_face_A_idx]; - - // loop through all vertices - // for each vert in face A - for( unsigned int each_vert_A_idx = 0; each_vert_A_idx < m_CoordIndex[each_face_A_idx].size(); each_vert_A_idx++ ) - { - int vertexIndexFromFaceA = (int)(m_CoordIndex[each_face_A_idx][each_vert_A_idx]); - glm::vec3 vector_face_A = m_PerFaceNormalsNormalized[each_face_A_idx]; - - // for each face B in mesh - for( unsigned int each_face_B_idx = 0; each_face_B_idx < m_CoordIndex.size(); each_face_B_idx++ ) - { - //if A != B { // ignore self - if( each_face_A_idx != each_face_B_idx ) - { - for( unsigned int ii = 0; ii < m_CoordIndex[each_face_B_idx].size(); ii++ ) - { - // Check if there is any vertice in the face B that touch the vertice in face A - if( m_CoordIndex[each_face_B_idx][ii] == vertexIndexFromFaceA ) - { - glm::vec3 vector_face_B = m_PerFaceNormalsNormalized[each_face_B_idx]; - - float dot_prod = glm::dot( vector_face_A, vector_face_B ); - - if( dot_prod > 0.05f ) - face_A_normals[each_vert_A_idx] += m_PerFaceNormalsRaw_X_PerFaceSquaredArea[each_face_B_idx] * dot_prod; - - // For each face, only one vertice can touch / share - // another vertice from the other face, so we exit here - break; - } - } - } - } - } - } - - - #ifdef USE_OPENMP - #pragma omp parallel for - #endif /* USE_OPENMP */ - - // Normalize - for( unsigned int each_face_A_idx = 0; each_face_A_idx < m_CoordIndex.size(); each_face_A_idx++ ) - { - std::vector< glm::vec3 >& face_A_normals = m_PerFaceVertexNormals[each_face_A_idx]; - - for( unsigned int each_vert_A_idx = 0; each_vert_A_idx < m_CoordIndex[each_face_A_idx].size(); each_vert_A_idx++ ) - { - float l = glm::length( face_A_normals[each_vert_A_idx] ); - - if( l > FLT_EPSILON ) // avoid division by zero - face_A_normals[each_vert_A_idx] /= l; - } - } -} diff --git a/3d-viewer/3d_mesh_model.h b/3d-viewer/3d_mesh_model.h deleted file mode 100644 index bc35e43c2c..0000000000 --- a/3d-viewer/3d_mesh_model.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_mesh_model.h - * @brief - */ - -#ifndef __3D_MESH_MODEL_H__ -#define __3D_MESH_MODEL_H__ - -#include -#include -#define GLM_FORCE_RADIANS -#include -#include "3d_struct.h" -#include "3d_material.h" -#include "3d_rendering/3d_render_raytracing/shapes3D/cbbox.h" - - -class S3D_MESH; - -/** A smart pointer to an S3D_MESH object */ -typedef std::shared_ptr S3D_MESH_PTR; - -/** A container of smar S3D_MESH object pointers */ -typedef std::vector S3D_MESH_PTRS; - -class S3D_MESH -{ -public: - - S3D_MESH(); - ~S3D_MESH(); - - void openGL_RenderAllChilds( bool aIsRenderingJustNonTransparentObjects, - bool aIsRenderingJustTransparentObjects ); - - S3D_MATERIAL *m_Materials; - - // Point and index list - std::vector< S3D_VERTEX > m_Point; - std::vector< std::vector > m_CoordIndex; - std::vector< std::vector > m_NormalIndex; - std::vector< S3D_VERTEX > m_PerFaceColor; - std::vector< S3D_VERTEX > m_PerFaceNormalsNormalized; - std::vector< S3D_VERTEX > m_PerVertexNormalsNormalized; - std::vector< int > m_MaterialIndexPerFace; - std::vector< std::vector > m_MaterialIndexPerVertex; - S3D_MESH_PTRS childs; - - S3D_VERTEX m_translation; - glm::vec4 m_rotation; - S3D_VERTEX m_scale; - - CBBOX &getBBox(); - -private: - std::vector< S3D_VERTEX > m_PerFaceNormalsRaw_X_PerFaceSquaredArea; - std::vector< std::vector< S3D_VERTEX > > m_PerFaceVertexNormals; - std::vector< S3D_VERTEX > m_PointNormalized; - - std::vector< std::vector > m_InvalidCoordIndexes; //!TODO: check for invalid CoordIndex in file and remove the index and the same material index - - bool isPerFaceNormalsComputed; - void calcPerFaceNormals (); - - bool isPointNormalizedComputed; - void calcPointNormalized(); - - bool isPerPointNormalsComputed; - void calcPerPointNormals(); - - bool isPerVertexNormalsVerified; - void perVertexNormalsVerify_and_Repair(); - - void calcBBox(); - void calcBBoxAllChilds(); - - CBBOX m_BBox; - - void openGL_Render( bool aIsRenderingJustNonTransparentObjects, - bool aIsRenderingJustTransparentObjects ); -}; - -#endif diff --git a/3d-viewer/3d_model_viewer/c3d_model_viewer.cpp b/3d-viewer/3d_model_viewer/c3d_model_viewer.cpp index ee2f4355dd..ce28cce230 100644 --- a/3d-viewer/3d_model_viewer/c3d_model_viewer.cpp +++ b/3d-viewer/3d_model_viewer/c3d_model_viewer.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,13 +30,18 @@ */ #include +#include "3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.h" #include "c3d_model_viewer.h" -#include "3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h" +#include "../3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h" +#include "../3d_cache/3d_cache.h" #include "common_ogl/ogl_utils.h" #include -#include "convert_to_biu.h" +#include #include +/** + * Scale convertion from 3d model units to pcb units + */ #define UNITS3D_TO_UNITSPCB (IU_PER_MM) /** @@ -69,14 +74,14 @@ BEGIN_EVENT_TABLE( C3D_MODEL_VIEWER, wxGLCanvas ) END_EVENT_TABLE() -/// This defines the range that all coord will have to be rendered. -/// It will use this value to convert to a normalized value between -/// -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2) +// This defines the range that all coord will have to be rendered. +// It will use this value to convert to a normalized value between +// -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2) #define RANGE_SCALE_3D 8.0f -C3D_MODEL_VIEWER::C3D_MODEL_VIEWER( wxWindow *aParent, - const int *aAttribList ) : +C3D_MODEL_VIEWER::C3D_MODEL_VIEWER(wxWindow *aParent, + const int *aAttribList , S3D_CACHE *aCacheManager) : wxGLCanvas( aParent, wxID_ANY, @@ -84,7 +89,8 @@ C3D_MODEL_VIEWER::C3D_MODEL_VIEWER( wxWindow *aParent, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ), - m_trackBallCamera( RANGE_SCALE_3D * 2.0f ) + m_trackBallCamera( RANGE_SCALE_3D * 2.0f ), + m_cacheManager(aCacheManager) { wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::C3D_MODEL_VIEWER" ) ); @@ -94,28 +100,30 @@ C3D_MODEL_VIEWER::C3D_MODEL_VIEWER( wxWindow *aParent, m_3d_model = NULL; m_BiuTo3Dunits = 1.0; - // Explicitly create a new rendering context instance for this canvas. - m_glRC = GL_CONTEXT_MANAGER::Get().CreateCtx( this ); + m_glRC = NULL; } C3D_MODEL_VIEWER::~C3D_MODEL_VIEWER() { - GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this ); - wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::~C3D_MODEL_VIEWER" ) ); - delete m_ogl_3dmodel; - m_ogl_3dmodel = NULL; + if( m_glRC ) + { + GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this ); - GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); - GL_CONTEXT_MANAGER::Get().DestroyCtx( m_glRC ); + delete m_ogl_3dmodel; + m_ogl_3dmodel = NULL; + + GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); + GL_CONTEXT_MANAGER::Get().DestroyCtx( m_glRC ); + } } void C3D_MODEL_VIEWER::Set3DModel( const S3DMODEL &a3DModel ) { - wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::Set3DModel" ) ); + wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::Set3DModel with a S3DMODEL" ) ); // Validate a3DModel pointers wxASSERT( a3DModel.m_Materials != NULL ); @@ -135,16 +143,38 @@ void C3D_MODEL_VIEWER::Set3DModel( const S3DMODEL &a3DModel ) m_3d_model = &a3DModel; m_reload_is_needed = true; } + + Refresh(); } + +void C3D_MODEL_VIEWER::Set3DModel(const wxString &aModelPathName) +{ + wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::Set3DModel with a wxString" ) ); + + if( m_cacheManager ) + { + const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName ); + + if( model ) + Set3DModel( (const S3DMODEL &)*model ); + else + Clear3DModel(); + } +} + + void C3D_MODEL_VIEWER::Clear3DModel() { // Delete the old model m_reload_is_needed = false; + delete m_ogl_3dmodel; m_ogl_3dmodel = NULL; m_3d_model = NULL; + + Refresh(); } @@ -168,7 +198,10 @@ void C3D_MODEL_VIEWER::ogl_initialize() const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f }; const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; - const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f }; // defines a directional light that points along the negative z-axis + + // defines a directional light that points along the negative z-axis + const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f }; + const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; glLightfv( GL_LIGHT0, GL_AMBIENT, ambient ); @@ -195,6 +228,8 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event ) { wxPaintDC( this ); + event.Skip( false ); + // SwapBuffer requires the window to be shown before calling if( !IsShownOnScreen() ) { @@ -205,6 +240,9 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event ) // "Makes the OpenGL state that is represented by the OpenGL rendering // context context current, i.e. it will be used by all subsequent OpenGL calls. // This function may only be called when the window is shown on screen" + if( m_glRC == NULL ) + m_glRC = GL_CONTEXT_MANAGER::Get().CreateCtx( this ); + GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this ); // Set the OpenGL viewport according to the client size of this canvas. @@ -226,11 +264,13 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event ) wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) ); m_reload_is_needed = false; - m_ogl_3dmodel = new C_OGL_3DMODEL( *m_3d_model ); + m_ogl_3dmodel = new C_OGL_3DMODEL( *m_3d_model, MATERIAL_MODE_NORMAL ); // It convert a model as it was a board, so get the max size dimension of the board // and compute the conversion scale - m_BiuTo3Dunits = (double)RANGE_SCALE_3D / ((double)m_ogl_3dmodel->GetBBox().GetMaxDimension() * UNITS3D_TO_UNITSPCB); + m_BiuTo3Dunits = (double)RANGE_SCALE_3D / + ( (double)m_ogl_3dmodel->GetBBox().GetMaxDimension() * + UNITS3D_TO_UNITSPCB ); } glViewport( 0, 0, clientSize.x, clientSize.y ); @@ -253,8 +293,8 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event ) glMatrixMode( GL_MODELVIEW ); glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) ); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); + glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); // Render Model if( m_ogl_3dmodel ) @@ -263,22 +303,24 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event ) double modelunit_to_3d_units_factor = m_BiuTo3Dunits * UNITS3D_TO_UNITSPCB; - glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor, modelunit_to_3d_units_factor); + glScaled( modelunit_to_3d_units_factor, + modelunit_to_3d_units_factor, + modelunit_to_3d_units_factor ); // Center model in the render viewport const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter(); + glTranslatef( -model_center.x, -model_center.y, -model_center.z ); - // !TODO: draw transparent models m_ogl_3dmodel->Draw_opaque(); m_ogl_3dmodel->Draw_transparent(); - //m_ogl_3dmodel->Draw_bboxes(); glPopMatrix(); } - glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 ); // YxY squared view port + // YxY squared view port + glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 ); glClear( GL_DEPTH_BUFFER_BIT ); glMatrixMode( GL_PROJECTION ); @@ -288,7 +330,9 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event ) glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); - const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f), SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) ); + const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f), + SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) ); + const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix(); glLoadMatrixf( glm::value_ptr( ViewMatrix ) ); @@ -314,9 +358,8 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event ) // front-buffer and vice versa, so that the output of the previous OpenGL // commands is displayed on the window." SwapBuffers(); - GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); - event.Skip(); + GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC ); } @@ -432,3 +475,4 @@ void C3D_MODEL_VIEWER::OnRightClick( wxMouseEvent &event ) { event.Skip(); } + diff --git a/3d-viewer/3d_model_viewer/c3d_model_viewer.h b/3d-viewer/3d_model_viewer/c3d_model_viewer.h index 8b794334d2..3c2e839e0e 100644 --- a/3d-viewer/3d_model_viewer/c3d_model_viewer.h +++ b/3d-viewer/3d_model_viewer/c3d_model_viewer.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,10 +32,11 @@ #ifndef _C3D_MODEL_VIEWER_H_ #define _C3D_MODEL_VIEWER_H_ -#include "3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.h" #include "3d_rendering/ctrack_ball.h" #include +class S3D_CACHE; +class C_OGL_3DMODEL; /** * Class C3D_MODEL_VIEWER @@ -50,10 +51,12 @@ public: /** * Creates a new 3D Canvas with a attribute list * @param aParent = the parent creator of this canvas - * @param aAttribList = a list of openGL options created by COGL_ATT_LIST::GetAttributesList + * @param aAttribList = a list of openGL options created by + * COGL_ATT_LIST::GetAttributesList */ C3D_MODEL_VIEWER( wxWindow *aParent, - const int *aAttribList = 0 ); + const int *aAttribList = 0, + S3D_CACHE *aCacheManager = NULL ); ~C3D_MODEL_VIEWER(); @@ -63,6 +66,12 @@ public: */ void Set3DModel( const S3DMODEL &a3DModel ); + /** + * @brief Set3DModel - Set this model to be displayed + * @param aModelPathName - 3d model path name + */ + void Set3DModel( wxString const& aModelPathName ); + /** * @brief Clear3DModel - Unloads the displayed 3d model */ @@ -91,6 +100,7 @@ private: void OnLeftUp( wxMouseEvent &event ); void OnMiddleUp( wxMouseEvent &event ); + void OnMiddleDown( wxMouseEvent &event ); void OnRightClick( wxMouseEvent &event ); @@ -117,10 +127,14 @@ private: /// Flag if open gl was initialized bool m_ogl_initialized; - /// factor to convert the model or any other items to keep it in relation to the +/-RANGE_SCALE_3D + /// factor to convert the model or any other items to keep it in relation to + /// the +/-RANGE_SCALE_3D /// (it is named same as the board render for better understanding proposes) double m_BiuTo3Dunits; + /// Optional cache manager + S3D_CACHE* m_cacheManager; + /** * Trace mask used to enable or disable the trace output of this class. * The debug output can be turned on by setting the WXTRACE environment variable to diff --git a/3d-viewer/3d_read_mesh.cpp b/3d-viewer/3d_read_mesh.cpp deleted file mode 100644 index d99b42148e..0000000000 --- a/3d-viewer/3d_read_mesh.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 2015 Jean-Pierre Charras, jp.charras@wanadoo.fr - * Copyright (C) 2011 Wayne Stambaugh - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_read_mesh.cpp - */ - -#include -#include -#include -#include -#include -#define GLM_FORCE_RADIANS -#include -#include <3d_viewer.h> -#include -#include "3d_struct.h" -#include "modelparsers.h" - - -S3D_MODEL_PARSER *S3D_MODEL_PARSER::Create( S3D_MASTER* aMaster, - const wxString aExtension ) -{ - if ( aExtension == wxT( "x3d" ) ) - return new X3D_MODEL_PARSER( aMaster ); - else if ( aExtension == wxT( "wrl" ) ) - return new VRML_MODEL_PARSER( aMaster ); - - return NULL; - } - - -int S3D_MASTER::ReadData( S3D_MODEL_PARSER* aParser ) -{ - if( m_Shape3DFullFilename.IsEmpty() || aParser == NULL ) - return -1; - - wxString filename = m_Shape3DFullFilename; - -#ifdef __WINDOWS__ - filename.Replace( wxT( "/" ), wxT( "\\" ) ); -#else - filename.Replace( wxT( "\\" ), wxT( "/" ) ); -#endif - - if( wxFileName::FileExists( filename ) ) - { - wxFileName fn( filename ); - - if( aParser->Load( filename ) ) - { - // Invalidate bounding boxes - m_fastAABBox.Reset(); - m_BBox.Reset(); - - m_parser = aParser; - - return 0; - } - } - - wxLogDebug( wxT( "3D shape '%s' not found, even tried '%s' after env var substitution." ), - GetChars( m_Shape3DName ), - GetChars( filename ) ); - - return -1; -} - - -void S3D_MASTER::Render( bool aIsRenderingJustNonTransparentObjects, - bool aIsRenderingJustTransparentObjects ) -{ - if( m_parser == NULL ) - return; - - double aVrmlunits_to_3Dunits = g_Parm_3D_Visu.m_BiuTo3Dunits * UNITS3D_TO_UNITSPCB; - - glScalef( aVrmlunits_to_3Dunits, aVrmlunits_to_3Dunits, aVrmlunits_to_3Dunits ); - - glTranslatef( m_MatPosition.x * SCALE_3D_CONV, - m_MatPosition.y * SCALE_3D_CONV, - m_MatPosition.z * SCALE_3D_CONV ); - - glRotatef( -m_MatRotation.z, 0.0f, 0.0f, 1.0f ); - glRotatef( -m_MatRotation.y, 0.0f, 1.0f, 0.0f ); - glRotatef( -m_MatRotation.x, 1.0f, 0.0f, 0.0f ); - - glScalef( m_MatScale.x, m_MatScale.y, m_MatScale.z ); - - for( unsigned int idx = 0; idx < m_parser->childs.size(); idx++ ) - m_parser->childs[idx]->openGL_RenderAllChilds( aIsRenderingJustNonTransparentObjects, - aIsRenderingJustTransparentObjects ); -} - - -CBBOX &S3D_MASTER::getBBox( ) -{ - if( !m_BBox.IsInitialized() ) - calcBBox(); - - return m_BBox; -} - - -CBBOX &S3D_MASTER::getFastAABBox( ) -{ - if( !m_fastAABBox.IsInitialized() ) - calcBBox(); - - return m_fastAABBox; -} - - -void S3D_MASTER::calcBBox() -{ - if( m_parser == NULL ) - return; - - m_BBox.Reset(); - - for( unsigned int idx = 0; idx < m_parser->childs.size(); idx++ ) - m_BBox.Union( m_parser->childs[idx]->getBBox() ); - - // Calc transformation matrix to apply in AABBox - - float aVrmlunits_to_3Dunits = g_Parm_3D_Visu.m_BiuTo3Dunits * UNITS3D_TO_UNITSPCB; - - glm::mat4 fullTransformMatrix; - - fullTransformMatrix = glm::scale( glm::mat4(), S3D_VERTEX( aVrmlunits_to_3Dunits, - aVrmlunits_to_3Dunits, - aVrmlunits_to_3Dunits ) ); - - fullTransformMatrix = glm::translate( fullTransformMatrix, S3D_VERTEX( m_MatPosition.x * SCALE_3D_CONV, - m_MatPosition.y * SCALE_3D_CONV, - m_MatPosition.z * SCALE_3D_CONV) ); - - if( m_MatRotation.z != 0.0 ) - fullTransformMatrix = glm::rotate( fullTransformMatrix, glm::radians(-(float)m_MatRotation.z), S3D_VERTEX( 0.0f, 0.0f, 1.0f ) ); - if( m_MatRotation.y != 0.0 ) - fullTransformMatrix = glm::rotate( fullTransformMatrix, glm::radians(-(float)m_MatRotation.y), S3D_VERTEX( 0.0f, 1.0f, 0.0f ) ); - if( m_MatRotation.x != 0.0 ) - fullTransformMatrix = glm::rotate( fullTransformMatrix, glm::radians(-(float)m_MatRotation.x), S3D_VERTEX( 1.0f, 0.0f, 0.0f ) ); - - fullTransformMatrix = glm::scale( fullTransformMatrix, S3D_VERTEX( m_MatScale.x, m_MatScale.y, m_MatScale.z ) ); - - // Apply transformation - m_fastAABBox = m_BBox; - m_fastAABBox.ApplyTransformationAA( fullTransformMatrix ); -} diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp index e9523441d9..d2cf21e66c 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,18 +29,312 @@ #include "c3d_render_ogl_legacy.h" #include "ogl_legacy_utils.h" -#include "geometry/shape_poly_set.h" - -#include "../3d_render_raytracing/shapes2D/cpolygon2d.h" -#include "../3d_render_raytracing/shapes2D/ctriangle2d.h" -#include "../3d_render_raytracing/shapes2D/cpolygon4pts2d.h" -#include "../3d_render_raytracing/shapes2D/cfilledcircle2d.h" -#include "../3d_render_raytracing/shapes2D/cring2d.h" -#include "3d_math/3d_math.h" -#include "3d_math/3d_fastmath.h" +#include +#include +#include "../../3d_math.h" +#include "../../3d_fastmath.h" #include +#include -void C3D_RENDER_OGL_LEGACY::reload() + +void C3D_RENDER_OGL_LEGACY::add_object_to_triangle_layer( const CFILLEDCIRCLE2D * aFilledCircle, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ) +{ + const SFVEC2F ¢er = aFilledCircle->GetCenter(); + const float radius = aFilledCircle->GetRadius() * + 2.0f; // Double because the render triangle + + // This is a small adjustment to the circle texture + const float texture_factor = (8.0f / (float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f; + const float f = (sqrtf(2.0f) / 2.0f) * radius * texture_factor; + + // Top and Bot segments ends are just triangle semi-circles, so need to add + // it in duplicated + aDstLayer->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, aZtop ), + SFVEC3F( center.x - f, center.y, aZtop ), + SFVEC3F( center.x, + center.y - f, aZtop ) ); + + aDstLayer->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, aZtop ), + SFVEC3F( center.x + f, center.y, aZtop ), + SFVEC3F( center.x, + center.y + f, aZtop ) ); + + aDstLayer->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, aZbot ), + SFVEC3F( center.x + f, center.y, aZbot ), + SFVEC3F( center.x, + center.y - f, aZbot ) ); + + aDstLayer->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, aZbot ), + SFVEC3F( center.x - f, center.y, aZbot ), + SFVEC3F( center.x, + center.y + f, aZbot ) ); +} + + +void C3D_RENDER_OGL_LEGACY::add_object_to_triangle_layer( const CPOLYGON4PTS2D * aPoly, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ) +{ + const SFVEC2F &v0 = aPoly->GetV0(); + const SFVEC2F &v1 = aPoly->GetV1(); + const SFVEC2F &v2 = aPoly->GetV2(); + const SFVEC2F &v3 = aPoly->GetV3(); + + add_triangle_top_bot( aDstLayer, v0, v2, v1, aZtop, aZbot ); + add_triangle_top_bot( aDstLayer, v2, v0, v3, aZtop, aZbot ); +} + + +void C3D_RENDER_OGL_LEGACY::generate_ring_contour( const SFVEC2F &aCenter, + float aInnerRadius, + float aOuterRadius, + unsigned int aNr_sides_per_circle, + std::vector< SFVEC2F > &aInnerContourResult, + std::vector< SFVEC2F > &aOuterContourResult, + bool aInvertOrder ) +{ + aInnerContourResult.clear(); + aInnerContourResult.reserve( aNr_sides_per_circle + 2 ); + + aOuterContourResult.clear(); + aOuterContourResult.reserve( aNr_sides_per_circle + 2 ); + + const int delta = 3600 / aNr_sides_per_circle; + + for( int ii = 0; ii < 3600; ii += delta ) + { + const SFVEC2F rotatedDir = glm::rotate( SFVEC2F( 1.0f, 0.0f ), + (float)(aInvertOrder?(3600 - ii):ii) * + 2.0f * glm::pi() / 3600.0f ); + + aInnerContourResult.push_back( SFVEC2F( aCenter.x + rotatedDir.x * aInnerRadius, + aCenter.y + rotatedDir.y * aInnerRadius ) ); + + aOuterContourResult.push_back( SFVEC2F( aCenter.x + rotatedDir.x * aOuterRadius, + aCenter.y + rotatedDir.y * aOuterRadius ) ); + } + + aInnerContourResult.push_back( aInnerContourResult[0] ); + aOuterContourResult.push_back( aOuterContourResult[0] ); + + wxASSERT( aInnerContourResult.size() == aOuterContourResult.size() ); +} + + +void C3D_RENDER_OGL_LEGACY::add_object_to_triangle_layer( const CRING2D * aRing, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ) +{ + const SFVEC2F ¢er = aRing->GetCenter(); + const float inner = aRing->GetInnerRadius(); + const float outer = aRing->GetOuterRadius(); + + std::vector< SFVEC2F > innerContour; + std::vector< SFVEC2F > outerContour; + + generate_ring_contour( center, + inner, + outer, + m_settings.GetNrSegmentsCircle( outer * 2.0f ), + innerContour, + outerContour, + false ); + + // This will add the top and bot quads that will form the approximated ring + + for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i ) + { + const SFVEC2F &vi0 = innerContour[i + 0]; + const SFVEC2F &vi1 = innerContour[i + 1]; + const SFVEC2F &vo0 = outerContour[i + 0]; + const SFVEC2F &vo1 = outerContour[i + 1]; + + aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ), + SFVEC3F( vi0.x, vi0.y, aZtop ), + SFVEC3F( vo0.x, vo0.y, aZtop ), + SFVEC3F( vo1.x, vo1.y, aZtop ) ); + + aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ), + SFVEC3F( vo1.x, vo1.y, aZbot ), + SFVEC3F( vo0.x, vo0.y, aZbot ), + SFVEC3F( vi0.x, vi0.y, aZbot ) ); + } +} + + +void C3D_RENDER_OGL_LEGACY::add_object_to_triangle_layer( const CTRIANGLE2D * aTri, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ) +{ + const SFVEC2F &v1 = aTri->GetP1(); + const SFVEC2F &v2 = aTri->GetP2(); + const SFVEC2F &v3 = aTri->GetP3(); + + add_triangle_top_bot( aDstLayer, v1, v2, v3, aZtop, aZbot ); +} + + +void C3D_RENDER_OGL_LEGACY::add_object_to_triangle_layer( const CROUNDSEGMENT2D * aSeg, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ) +{ + const SFVEC2F leftStart = aSeg->GetLeftStar(); + const SFVEC2F leftEnd = aSeg->GetLeftEnd(); + const SFVEC2F leftDir = aSeg->GetLeftDir(); + + const SFVEC2F rightStart = aSeg->GetRightStar(); + const SFVEC2F rightEnd = aSeg->GetRightEnd(); + const SFVEC2F rightDir = aSeg->GetRightDir(); + const float radius = aSeg->GetRadius(); + + const SFVEC2F start = aSeg->GetStart(); + const SFVEC2F end = aSeg->GetEnd(); + + const float texture_factor = (12.0f / (float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f; + const float texture_factorF= ( 6.0f / (float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f; + + const float radius_of_the_square = sqrtf( aSeg->GetRadiusSquared() * 2.0f ); + const float radius_triangle_factor = (radius_of_the_square - radius) / radius; + + const SFVEC2F factorS = SFVEC2F( -rightDir.y * radius * radius_triangle_factor, + rightDir.x * radius * radius_triangle_factor ); + + const SFVEC2F factorE = SFVEC2F( -leftDir.y * radius * radius_triangle_factor, + leftDir.x * radius * radius_triangle_factor ); + + // Top end segment triangles (semi-circles) + aDstLayer->m_layer_top_segment_ends->AddTriangle( + SFVEC3F( rightEnd.x + texture_factor * factorS.x, + rightEnd.y + texture_factor * factorS.y, + aZtop ), + SFVEC3F( leftStart.x + texture_factor * factorE.x, + leftStart.y + texture_factor * factorE.y, + aZtop ), + SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ), + start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ), + aZtop ) ); + + aDstLayer->m_layer_top_segment_ends->AddTriangle( + SFVEC3F( leftEnd.x + texture_factor * factorE.x, + leftEnd.y + texture_factor * factorE.y, aZtop ), + SFVEC3F( rightStart.x + texture_factor * factorS.x, + rightStart.y + texture_factor * factorS.y, aZtop ), + SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ), + end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ), + aZtop ) ); + + // Bot end segment triangles (semi-circles) + aDstLayer->m_layer_bot_segment_ends->AddTriangle( + SFVEC3F( leftStart.x + texture_factor * factorE.x, + leftStart.y + texture_factor * factorE.y, + aZbot ), + SFVEC3F( rightEnd.x + texture_factor * factorS.x, + rightEnd.y + texture_factor * factorS.y, + aZbot ), + SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ), + start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ), + aZbot ) ); + + aDstLayer->m_layer_bot_segment_ends->AddTriangle( + SFVEC3F( rightStart.x + texture_factor * factorS.x, + rightStart.y + texture_factor * factorS.y, aZbot ), + SFVEC3F( leftEnd.x + texture_factor * factorE.x, + leftEnd.y + texture_factor * factorE.y, aZbot ), + SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ), + end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ), + aZbot ) ); + + // Segment top and bot planes + aDstLayer->m_layer_top_triangles->AddQuad( + SFVEC3F( rightEnd.x, rightEnd.y, aZtop ), + SFVEC3F( rightStart.x, rightStart.y, aZtop ), + SFVEC3F( leftEnd.x, leftEnd.y, aZtop ), + SFVEC3F( leftStart.x, leftStart.y, aZtop ) ); + + aDstLayer->m_layer_bot_triangles->AddQuad( + SFVEC3F( rightEnd.x, rightEnd.y, aZbot ), + SFVEC3F( leftStart.x, leftStart.y, aZbot ), + SFVEC3F( leftEnd.x, leftEnd.y, aZbot ), + SFVEC3F( rightStart.x, rightStart.y, aZbot ) ); +} + + +CLAYERS_OGL_DISP_LISTS *C3D_RENDER_OGL_LEGACY::generate_holes_display_list( + const LIST_OBJECT2D &aListHolesObject2d, + const SHAPE_POLY_SET &aPoly, + float aZtop, + float aZbot, + bool aInvertFaces ) +{ + CLAYERS_OGL_DISP_LISTS *ret = NULL; + + if( aListHolesObject2d.size() > 0 ) + { + CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( aListHolesObject2d.size() * 2 ); + + // Convert the list of objects(filled circles) to triangle layer structure + for( LIST_OBJECT2D::const_iterator itemOnLayer = aListHolesObject2d.begin(); + itemOnLayer != aListHolesObject2d.end(); + ++itemOnLayer ) + { + const COBJECT2D *object2d_A = static_cast(*itemOnLayer); + + wxASSERT( (object2d_A->GetObjectType() == OBJ2D_FILLED_CIRCLE) || + (object2d_A->GetObjectType() == OBJ2D_ROUNDSEG) ); + + switch( object2d_A->GetObjectType() ) + { + case OBJ2D_FILLED_CIRCLE: + add_object_to_triangle_layer( (const CFILLEDCIRCLE2D *)object2d_A, + layerTriangles, + aZtop, aZbot ); + break; + + case OBJ2D_ROUNDSEG: + add_object_to_triangle_layer( (const CROUNDSEGMENT2D *) object2d_A, + layerTriangles, + aZtop, aZbot ); + break; + + default: + wxFAIL_MSG("C3D_RENDER_OGL_LEGACY::generate_holes_display_list: Object type is not implemented"); + break; + } + } + + // Note: he can have a aListHolesObject2d whith holes but without countours + // eg: when there are only NPTH on the list and the contours were not + // added + + if( aPoly.OutlineCount() > 0 ) + { + layerTriangles->AddToMiddleContourns( aPoly, + aZbot, + aZtop, + m_settings.BiuTo3Dunits(), + aInvertFaces ); + } + + ret = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, + m_ogl_circle_texture, + aZbot, + aZtop ); + + delete layerTriangles; + } + + return ret; +} + + +void C3D_RENDER_OGL_LEGACY::reload( REPORTER *aStatusTextReporter ) { m_reloadRequested = false; @@ -48,227 +342,219 @@ void C3D_RENDER_OGL_LEGACY::reload() COBJECT2D_STATS::Instance().ResetStats(); - m_settings.InitSettings(); +#ifdef PRINT_STATISTICS_3D_VIEWER + printf("InitSettings...\n"); +#endif + + unsigned stats_startReloadTime = GetRunningMicroSecs(); + + m_settings.InitSettings( aStatusTextReporter ); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_endReloadTime = GetRunningMicroSecs(); +#endif SFVEC3F camera_pos = m_settings.GetBoardCenter3DU(); m_settings.CameraGet().SetBoardLookAtPos( camera_pos ); +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_start_OpenGL_Load_Time = GetRunningMicroSecs(); +#endif + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Load OpenGL: board" ) ); + // Create Board // ///////////////////////////////////////////////////////////////////////// - printf("Create board...\n"); + CCONTAINER2D boardContainer; Convert_shape_line_polygon_to_triangles( m_settings.GetBoardPoly(), boardContainer, m_settings.BiuTo3Dunits(), (const BOARD_ITEM &)*m_settings.GetBoard() ); - const LIST_OBJECT2D listBoardObject2d = boardContainer.GetList(); + const LIST_OBJECT2D &listBoardObject2d = boardContainer.GetList(); if( listBoardObject2d.size() > 0 ) { - /* - float layer_z_top = m_settings.GetLayerBottomZpos3DU( F_Cu ); - float layer_z_bot = m_settings.GetLayerBottomZpos3DU( B_Cu ); -*/ - float layer_z_top = m_settings.GetLayerBottomZpos3DU( B_Mask ); - float layer_z_bot = m_settings.GetLayerTopZpos3DU( B_Mask ); + // We will set a unitary Z so it will in future used with transformations + // since the board poly will be used not only to draw itself but also the + // solder mask layers. + const float layer_z_top = 1.0f; + const float layer_z_bot = 0.0f; + CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( listBoardObject2d.size() ); + // Convert the list of objects(triangles) to triangle layer structure for( LIST_OBJECT2D::const_iterator itemOnLayer = listBoardObject2d.begin(); itemOnLayer != listBoardObject2d.end(); - itemOnLayer++ ) + ++itemOnLayer ) { const COBJECT2D *object2d_A = static_cast(*itemOnLayer); wxASSERT( object2d_A->GetObjectType() == OBJ2D_TRIANGLE ); const CTRIANGLE2D *tri = (const CTRIANGLE2D *)object2d_A; + const SFVEC2F &v1 = tri->GetP1(); const SFVEC2F &v2 = tri->GetP2(); const SFVEC2F &v3 = tri->GetP3(); - add_triangle_top_bot( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot ); + add_triangle_top_bot( layerTriangles, + v1, + v2, + v3, + layer_z_top, + layer_z_bot ); } - const SHAPE_POLY_SET boardPoly = m_settings.GetBoardPoly(); - SHAPE_POLY_SET boardPolyCopy = boardPoly; + const SHAPE_POLY_SET &boardPoly = m_settings.GetBoardPoly(); - boardPolyCopy.Simplify( SHAPE_POLY_SET::PM_FAST ); + wxASSERT( boardPoly.OutlineCount() > 0 ); - if( boardPolyCopy.OutlineCount() == 1 ) + if( boardPoly.OutlineCount() > 0 ) { - const SHAPE_LINE_CHAIN& outlinePath = boardPolyCopy.COutline( 0 ); + layerTriangles->AddToMiddleContourns( boardPoly, + layer_z_bot, + layer_z_top, + m_settings.BiuTo3Dunits(), + false ); - std::vector< SFVEC2F > contournPoints; - contournPoints.clear(); - contournPoints.reserve( outlinePath.PointCount() + 2 ); - - for( unsigned int i = 0; i < (unsigned int)outlinePath.PointCount(); ++i ) - { - const VECTOR2I& v = outlinePath.CPoint( i ); - contournPoints.push_back( SFVEC2F( v.x * m_settings.BiuTo3Dunits(), - -v.y * m_settings.BiuTo3Dunits() ) ); - } - contournPoints.push_back( contournPoints[0] ); - - if( contournPoints.size() > 4 ) - { - // Calculate normals of each segment of the contourn - std::vector< SFVEC2F > contournNormals; - contournNormals.clear(); - contournNormals.reserve( contournPoints.size() ); - for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i ) - { - const SFVEC2F &v0 = contournPoints[i + 0]; - const SFVEC2F &v1 = contournPoints[i + 1]; - - SFVEC2F n = glm::normalize( v1 - v0 ); - contournNormals.push_back( SFVEC2F( -n.y, n.x ) ); - } - - SFVEC2F lastNormal = contournNormals[contournPoints.size() - 2]; - for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i ) - { - const SFVEC2F &v0 = contournPoints[i + 0]; - const SFVEC2F &v1 = contournPoints[i + 1]; - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_top ) ), - SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_top ) ), - SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_bot ) ), - SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_bot ) ) ); - - SFVEC2F n0 = contournNormals[i]; - - if( glm::dot( n0, lastNormal ) > 0.5f ) - n0 += lastNormal; - else - n0 += contournNormals[i]; - - const SFVEC2F &nextNormal = contournNormals[ (i + 1) % (contournPoints.size() - 1) ]; - SFVEC2F n1 = contournNormals[i]; - - if( glm::dot( n1, nextNormal ) > 0.5f ) - n1 += nextNormal; - else - n1 += contournNormals[i]; - - n0 = glm::normalize( n0 ); - n1 = glm::normalize( n1 ); - - const SFVEC3F n3d0 = SFVEC3F( n0.x, n0.y, 0.0f ); - const SFVEC3F n3d1 = SFVEC3F( n1.x, n1.y, 0.0f ); - - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d0, n3d1, n3d1, n3d0 ); - - lastNormal = contournNormals[i]; -/* - const SFVEC2F n0 = glm::normalize( v0 - center ); - const SFVEC2F n1 = glm::normalize( v1 - center ); - const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); - const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z );*/ - } - } - contournPoints.clear(); + m_ogl_disp_list_board = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, + m_ogl_circle_texture, + layer_z_top, + layer_z_top ); } - m_ogl_disp_list_board = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, m_ogl_circle_texture, SFVEC3F(0.65f,0.55f,0.05f) ); - delete layerTriangles; } - - float calc_sides_min_factor = (float)( 10.0 * IU_PER_MILS * m_settings.BiuTo3Dunits() ); - float calc_sides_max_factor = (float)( 1000.0 * IU_PER_MILS * m_settings.BiuTo3Dunits() ); - - - // Add layers maps (except B_Mask and F_Mask) + // Create Through Holes and vias // ///////////////////////////////////////////////////////////////////////// - printf("Add layers maps...\n"); - for( MAP_CONTAINER_2D::const_iterator it = m_settings.GetMapLayers().begin(); - it != m_settings.GetMapLayers().end(); it++ ) + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Load OpenGL: holes and vias" ) ); + + m_ogl_disp_list_through_holes_outer = generate_holes_display_list( + m_settings.GetThroughHole_Outer().GetList(), + m_settings.GetThroughHole_Outer_poly(), + 1.0f, + 0.0f, + false ); + + SHAPE_POLY_SET bodyHoles = m_settings.GetThroughHole_Outer_poly(); + + bodyHoles.BooleanAdd( m_settings.GetThroughHole_Outer_poly_NPTH(), + SHAPE_POLY_SET::PM_FAST ); + + m_ogl_disp_list_through_holes_outer_with_npth = generate_holes_display_list( + m_settings.GetThroughHole_Outer().GetList(), + bodyHoles, + 1.0f, + 0.0f, + false ); + + m_ogl_disp_list_through_holes_inner = generate_holes_display_list( + m_settings.GetThroughHole_Inner().GetList(), + m_settings.GetThroughHole_Inner_poly(), + 1.0f, + 0.0f, + true ); + + + m_ogl_disp_list_through_holes_vias_outer = generate_holes_display_list( + m_settings.GetThroughHole_Vias_Outer().GetList(), + m_settings.GetThroughHole_Vias_Outer_poly(), + 1.0f, + 0.0f, + false ); + + // Not in use + //m_ogl_disp_list_through_holes_vias_inner = generate_holes_display_list( + // m_settings.GetThroughHole_Vias_Inner().GetList(), + // m_settings.GetThroughHole_Vias_Inner_poly(), + // 1.0f, 0.0f, + // false ); + + const MAP_POLY & innerMapHoles = m_settings.GetPolyMapHoles_Inner(); + const MAP_POLY & outerMapHoles = m_settings.GetPolyMapHoles_Outer(); + + wxASSERT( innerMapHoles.size() == outerMapHoles.size() ); + + const MAP_CONTAINER_2D &map_holes = m_settings.GetMapLayersHoles(); + + if( outerMapHoles.size() > 0 ) { - LAYER_ID layer_id = static_cast(it->first); + float layer_z_bot = 0.0f; + float layer_z_top = 0.0f; + + for( MAP_POLY::const_iterator ii = outerMapHoles.begin(); + ii != outerMapHoles.end(); + ++ii ) + { + LAYER_ID layer_id = static_cast(ii->first); + const SHAPE_POLY_SET *poly = static_cast(ii->second); + const CBVHCONTAINER2D *container = map_holes.at( layer_id ); + + get_layer_z_pos( layer_id, layer_z_top, layer_z_bot ); + + m_ogl_disp_lists_layers_holes_outer[layer_id] = generate_holes_display_list( + container->GetList(), + *poly, + layer_z_top, + layer_z_bot, + false ); + } + + for( MAP_POLY::const_iterator ii = innerMapHoles.begin(); + ii != innerMapHoles.end(); + ++ii ) + { + LAYER_ID layer_id = static_cast(ii->first); + const SHAPE_POLY_SET *poly = static_cast(ii->second); + const CBVHCONTAINER2D *container = map_holes.at( layer_id ); + + get_layer_z_pos( layer_id, layer_z_top, layer_z_bot ); + + m_ogl_disp_lists_layers_holes_inner[layer_id] = generate_holes_display_list( + container->GetList(), + *poly, + layer_z_top, + layer_z_bot, + false ); + } + } + + // Generate vertical cylinders of vias and pads (copper) + generate_3D_Vias_and_Pads(); + + // Add layers maps + // ///////////////////////////////////////////////////////////////////////// + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Load OpenGL: layers" ) ); + + for( MAP_CONTAINER_2D::const_iterator ii = m_settings.GetMapLayers().begin(); + ii != m_settings.GetMapLayers().end(); + ++ii ) + { + LAYER_ID layer_id = static_cast(ii->first); if( !m_settings.Is3DLayerEnabled( layer_id ) ) continue; - const CBVHCONTAINER2D *container2d = static_cast(it->second); - const LIST_OBJECT2D listObject2d = container2d->GetList(); + const CBVHCONTAINER2D *container2d = static_cast(ii->second); + const LIST_OBJECT2D &listObject2d = container2d->GetList(); if( listObject2d.size() == 0 ) continue; - //CMATERIAL *materialLayer = &m_materials.m_SilkS; - SFVEC3F layerColor = SFVEC3F( 0.3f, 0.4f, 0.5f ); + float layer_z_bot = 0.0f; + float layer_z_top = 0.0f; - float layer_z_bot = m_settings.GetLayerBottomZpos3DU( layer_id ); - float layer_z_top = m_settings.GetLayerTopZpos3DU( layer_id ); + get_layer_z_pos( layer_id, layer_z_top, layer_z_bot ); - if( layer_z_top < layer_z_bot ) - { - float tmpFloat = layer_z_bot; - layer_z_bot = layer_z_top; - layer_z_top = tmpFloat; - } - - layer_z_bot -= m_settings.GetNonCopperLayerThickness3DU(); - layer_z_top += m_settings.GetNonCopperLayerThickness3DU(); - - if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) - { - switch( layer_id ) - { - case B_Adhes: - case F_Adhes: - - break; - - case B_Paste: - case F_Paste: - // materialLayer = &m_materials.m_Paste; - break; - - case B_SilkS: - case F_SilkS: - - // materialLayer = &m_materials.m_SilkS; - // layerColor = g_silkscreenColor; - break; - - case Dwgs_User: - case Cmts_User: - case Eco1_User: - case Eco2_User: - case Edge_Cuts: - case Margin: - break; - - case B_CrtYd: - case F_CrtYd: - break; - - case B_Fab: - case F_Fab: - break; - - default: - //materialLayer = &m_materials.m_Copper; - - //layerColor = g_copperColor; - - - break; - } - } - else - { - layerColor = m_settings.GetLayerColor( layer_id ); - - } - - - // Calculate an estiation for then nr of triangles based on the nr of objects + // Calculate an estimation for the nr of triangles based on the nr of objects unsigned int nrTrianglesEstimation = listObject2d.size() * 8; CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( nrTrianglesEstimation ); @@ -276,518 +562,117 @@ void C3D_RENDER_OGL_LEGACY::reload() m_triangles[layer_id] = layerTriangles; for( LIST_OBJECT2D::const_iterator itemOnLayer = listObject2d.begin(); - itemOnLayer != listObject2d.end(); itemOnLayer++ ) + itemOnLayer != listObject2d.end(); + ++itemOnLayer ) { const COBJECT2D *object2d_A = static_cast(*itemOnLayer); switch( object2d_A->GetObjectType() ) { - case OBJ2D_FILLED_CIRCLE: - { - const CFILLEDCIRCLE2D *filledCircle = (const CFILLEDCIRCLE2D *)object2d_A; - const SFVEC2F ¢er = filledCircle->GetCenter(); - float radius = filledCircle->GetRadius() * 2.0f; // Double because the render triangle - float radiusSquared = radius * radius; - - const float f = (sqrtf(2.0f) / 2.0f) * radius * 0.9;// * texture_factor; - - layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, layer_z_top ), - SFVEC3F( center.x - f, center.y, layer_z_top ), - SFVEC3F( center.x, - center.y - f, layer_z_top ) ); - - layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, layer_z_top ), - SFVEC3F( center.x + f, center.y, layer_z_top ), - SFVEC3F( center.x, - center.y + f, layer_z_top ) ); - - layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, layer_z_bot ), - SFVEC3F( center.x + f, center.y, layer_z_bot ), - SFVEC3F( center.x, - center.y - f, layer_z_bot ) ); - - layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, layer_z_bot ), - SFVEC3F( center.x - f, center.y, layer_z_bot ), - SFVEC3F( center.x, - center.y + f, layer_z_bot ) ); - - unsigned int nr_sides_per_circle = (unsigned int)mapf( radiusSquared, - calc_sides_min_factor, calc_sides_max_factor, - 24.0f, 256.0f ); - - wxASSERT( nr_sides_per_circle >= 24 ); - - // Normal radius for the circle - radius = filledCircle->GetRadius(); - - std::vector< SFVEC2F > contournPoints; - - contournPoints.clear(); - contournPoints.reserve( nr_sides_per_circle + 2 ); - int delta = 3600 / nr_sides_per_circle; - for( int ii = 0; ii < 3600; ii += delta ) - { - const SFVEC2F rotatedDir = glm::rotate( SFVEC2F( 0.0f, 1.0f ), (float)ii * 2.0f * 3.14f / 3600.0f ); - contournPoints.push_back( SFVEC2F( center.x - rotatedDir.y * radius, center.y + rotatedDir.x * radius ) ); - } - contournPoints.push_back( contournPoints[0] ); - - if( contournPoints.size() > 1 ) - { - for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i ) - { - const SFVEC2F &v0 = contournPoints[i + 0]; - const SFVEC2F &v1 = contournPoints[i + 1]; - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ), - SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ), - SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ), - SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ) ); - - - const SFVEC2F n0 = glm::normalize( v0 - center ); - const SFVEC2F n1 = glm::normalize( v1 - center ); - const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); - const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); - } - } - contournPoints.clear(); - - } - break; - - case OBJ2D_DUMMYBLOCK: - { - } + add_object_to_triangle_layer( (const CFILLEDCIRCLE2D *)object2d_A, + layerTriangles, + layer_z_top, layer_z_bot ); break; case OBJ2D_POLYGON4PT: - { - const CPOLYGON4PTS2D *poly = (const CPOLYGON4PTS2D *)object2d_A; - const SFVEC2F &v0 = poly->GetV0(); - const SFVEC2F &v1 = poly->GetV1(); - const SFVEC2F &v2 = poly->GetV2(); - const SFVEC2F &v3 = poly->GetV3(); - - add_triangle_top_bot( layerTriangles, v0, v2, v1, layer_z_top, layer_z_bot ); - add_triangle_top_bot( layerTriangles, v2, v0, v3, layer_z_top, layer_z_bot ); - - const SFVEC2F &n0 = poly->GetN0(); - const SFVEC2F &n1 = poly->GetN1(); - const SFVEC2F &n2 = poly->GetN2(); - const SFVEC2F &n3 = poly->GetN3(); - - const SFVEC3F n3d0 = SFVEC3F(-n0.y, n0.x, 0.0f ); - const SFVEC3F n3d1 = SFVEC3F(-n1.y, n1.x, 0.0f ); - const SFVEC3F n3d2 = SFVEC3F(-n2.y, n2.x, 0.0f ); - const SFVEC3F n3d3 = SFVEC3F(-n3.y, n3.x, 0.0f ); - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_bot ), - SFVEC3F( v1.x, v1.y, layer_z_bot ), - SFVEC3F( v1.x, v1.y, layer_z_top ), - SFVEC3F( v0.x, v0.y, layer_z_top ) ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d0, n3d0, n3d0, n3d0 ); - - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v2.x, v2.y, layer_z_top ), - SFVEC3F( v1.x, v1.y, layer_z_top ), - SFVEC3F( v1.x, v1.y, layer_z_bot ), - SFVEC3F( v2.x, v2.y, layer_z_bot ) ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d1, n3d1, n3d1, n3d1 ); - - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v3.x, v3.y, layer_z_top ), - SFVEC3F( v2.x, v2.y, layer_z_top ), - SFVEC3F( v2.x, v2.y, layer_z_bot ), - SFVEC3F( v3.x, v3.y, layer_z_bot ) ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d2, n3d2, n3d2, n3d2 ); - - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_top ), - SFVEC3F( v3.x, v3.y, layer_z_top ), - SFVEC3F( v3.x, v3.y, layer_z_bot ), - SFVEC3F( v0.x, v0.y, layer_z_bot ) ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d3, n3d3, n3d3, n3d3 ); - } + add_object_to_triangle_layer( (const CPOLYGON4PTS2D *)object2d_A, + layerTriangles, + layer_z_top, layer_z_bot ); break; - case OBJ2D_RING: - { - const CRING2D *ring = (const CRING2D *)object2d_A; - const SFVEC2F ¢er = ring->GetCenter(); - float inner = ring->GetInnerRadius(); - float outer = ring->GetOuterRadius(); - - unsigned int nr_sides_per_circle = (unsigned int)mapf( outer, - calc_sides_min_factor, calc_sides_max_factor, - 24.0f, 256.0f ); - - wxASSERT( nr_sides_per_circle >= 24 ); - - - std::vector< SFVEC2F > innerContour; - std::vector< SFVEC2F > outerContour; - innerContour.clear(); - innerContour.reserve( nr_sides_per_circle + 2 ); - - outerContour.clear(); - outerContour.reserve( nr_sides_per_circle + 2 ); - - int delta = 3600 / nr_sides_per_circle; - for( int ii = 0; ii < 3600; ii += delta ) - { - const SFVEC2F rotatedDir = glm::rotate( SFVEC2F( 0.0f, 1.0f), (float) ii * 2.0f * 3.14f / 3600.0f ); - - innerContour.push_back( SFVEC2F( center.x - rotatedDir.y * inner, center.y + rotatedDir.x * inner ) ); - outerContour.push_back( SFVEC2F( center.x - rotatedDir.y * outer, center.y + rotatedDir.x * outer ) ); - } - - innerContour.push_back( innerContour[0] ); - outerContour.push_back( outerContour[0] ); - - wxASSERT( innerContour.size() == outerContour.size() ); - - for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i ) - { - const SFVEC2F &vi0 = innerContour[i + 0]; - const SFVEC2F &vi1 = innerContour[i + 1]; - const SFVEC2F &vo0 = outerContour[i + 0]; - const SFVEC2F &vo1 = outerContour[i + 1]; - - layerTriangles->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, layer_z_top ), - SFVEC3F( vi0.x, vi0.y, layer_z_top ), - SFVEC3F( vo0.x, vo0.y, layer_z_top ), - SFVEC3F( vo1.x, vo1.y, layer_z_top ) ); - - layerTriangles->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, layer_z_bot ), - SFVEC3F( vo1.x, vo1.y, layer_z_bot ), - SFVEC3F( vo0.x, vo0.y, layer_z_bot ), - SFVEC3F( vi0.x, vi0.y, layer_z_bot ) ); - } - - for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i ) - { - const SFVEC2F &v0 = innerContour[i + 0]; - const SFVEC2F &v1 = innerContour[i + 1]; - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ), - SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ), - SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ), - SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ) ); - - - const SFVEC2F n0 = glm::normalize( v0 - center ); - const SFVEC2F n1 = glm::normalize( v1 - center ); - const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); - const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); - } - - - for( unsigned int i = 0; i < ( outerContour.size() - 1 ); ++i ) - { - const SFVEC2F &v0 = outerContour[i + 0]; - const SFVEC2F &v1 = outerContour[i + 1]; - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ), - SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ), - SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ), - SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ) ); - - - const SFVEC2F n0 = glm::normalize( v0 - center ); - const SFVEC2F n1 = glm::normalize( v1 - center ); - const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); - const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); - } - - } + add_object_to_triangle_layer( (const CRING2D *)object2d_A, + layerTriangles, + layer_z_top, layer_z_bot ); break; - case OBJ2D_TRIANGLE: - { - const CTRIANGLE2D *tri = (const CTRIANGLE2D *)object2d_A; - const SFVEC2F &v1 = tri->GetP1(); - const SFVEC2F &v2 = tri->GetP2(); - const SFVEC2F &v3 = tri->GetP3(); - - add_triangle_top_bot( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot ); - } + add_object_to_triangle_layer( (const CTRIANGLE2D *)object2d_A, + layerTriangles, + layer_z_top, layer_z_bot ); break; case OBJ2D_ROUNDSEG: - { - const CROUNDSEGMENT2D &roundSeg = (const CROUNDSEGMENT2D &) *object2d_A; - unsigned int nr_sides_per_circle = (unsigned int)mapf( roundSeg.GetWidth(), - calc_sides_min_factor, calc_sides_max_factor, - 24.0f, 256.0f ); - - wxASSERT( nr_sides_per_circle >= 24 ); - - SFVEC2F leftStart = roundSeg.GetLeftStar(); - SFVEC2F leftEnd = roundSeg.GetLeftEnd(); - SFVEC2F leftDir = roundSeg.GetLeftDir(); - - SFVEC2F rightStart = roundSeg.GetRightStar(); - SFVEC2F rightEnd = roundSeg.GetRightEnd(); - SFVEC2F rightDir = roundSeg.GetRightDir(); - float radius = roundSeg.GetRadius(); - - SFVEC2F start = roundSeg.GetStart(); - SFVEC2F end = roundSeg.GetEnd(); - - float texture_factor = (12.0f/(float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f; - float texture_factorF= ( 4.0f/(float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f; - - const float radius_of_the_square = sqrtf( roundSeg.GetRadiusSquared() * 2.0f ); - const float radius_triangle_factor = (radius_of_the_square - radius) / radius; - - const SFVEC2F factorS = SFVEC2F( -rightDir.y * radius * radius_triangle_factor, rightDir.x * radius * radius_triangle_factor ); - const SFVEC2F factorE = SFVEC2F( -leftDir.y * radius * radius_triangle_factor, leftDir.x * radius * radius_triangle_factor ); - - // Top end segment triangles - layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( rightEnd.x + texture_factor * factorS.x, rightEnd.y + texture_factor * factorS.y, layer_z_top ), - SFVEC3F( leftStart.x + texture_factor * factorE.x, leftStart.y + texture_factor * factorE.y, layer_z_top ), - SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf(2.0f), - start.y - texture_factorF * leftDir.y * radius * sqrtf(2.0f), layer_z_top ) ); - - layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( leftEnd.x + texture_factor * factorE.x, leftEnd.y + texture_factor * factorE.y, layer_z_top ), - SFVEC3F( rightStart.x + texture_factor * factorS.x, rightStart.y + texture_factor * factorS.y, layer_z_top ), - SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf(2.0f), - end.y - texture_factorF * rightDir.y * radius * sqrtf(2.0f), layer_z_top ) ); - - // Bot end segment triangles - layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( leftStart.x + texture_factor * factorE.x, leftStart.y + texture_factor * factorE.y, layer_z_bot ), - SFVEC3F( rightEnd.x + texture_factor * factorS.x, rightEnd.y + texture_factor * factorS.y, layer_z_bot ), - SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf(2.0f), - start.y - texture_factorF * leftDir.y * radius * sqrtf(2.0f), layer_z_bot ) ); - - layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( rightStart.x + texture_factor * factorS.x, rightStart.y + texture_factor * factorS.y, layer_z_bot ), - SFVEC3F( leftEnd.x + texture_factor * factorE.x, leftEnd.y + texture_factor * factorE.y, layer_z_bot ), - SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf(2.0f), - end.y - texture_factorF * rightDir.y * radius * sqrtf(2.0f), layer_z_bot ) ); - - // Segment top and bot planes - layerTriangles->m_layer_top_triangles->AddQuad( SFVEC3F( rightEnd.x, rightEnd.y, layer_z_top ), - SFVEC3F( rightStart.x, rightStart.y, layer_z_top ), - SFVEC3F( leftEnd.x, leftEnd.y, layer_z_top ), - SFVEC3F( leftStart.x, leftStart.y, layer_z_top ) ); - - layerTriangles->m_layer_bot_triangles->AddQuad( SFVEC3F( rightEnd.x, rightEnd.y, layer_z_bot ), - SFVEC3F( leftStart.x, leftStart.y, layer_z_bot ), - SFVEC3F( leftEnd.x, leftEnd.y, layer_z_bot ), - SFVEC3F( rightStart.x, rightStart.y, layer_z_bot ) ); - - // Middle contourns (two sides of the segment) - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( leftStart.x, leftStart.y, layer_z_top ), - SFVEC3F( leftEnd.x, leftEnd.y, layer_z_top ), - SFVEC3F( leftEnd.x, leftEnd.y, layer_z_bot ), - SFVEC3F( leftStart.x, leftStart.y, layer_z_bot ) ); - const SFVEC3F leftNormal = SFVEC3F( -leftDir.y, leftDir.x, 0.0f ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( leftNormal, leftNormal, leftNormal, leftNormal ); - - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( rightStart.x, rightStart.y, layer_z_top ), - SFVEC3F( rightEnd.x, rightEnd.y, layer_z_top ), - SFVEC3F( rightEnd.x, rightEnd.y, layer_z_bot ), - SFVEC3F( rightStart.x, rightStart.y, layer_z_bot ) ); - const SFVEC3F rightNormal = SFVEC3F( -rightDir.y, rightDir.x, 0.0f ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( rightNormal, rightNormal, rightNormal, rightNormal ); - - - // Compute the outlines of the segment, and creates a polygon - // add right rounded end: - - std::vector< SFVEC2F > roundedEndPointsStart; - std::vector< SFVEC2F > roundedEndPointsEnd; - roundedEndPointsStart.clear(); - roundedEndPointsStart.reserve( nr_sides_per_circle + 2 ); - - roundedEndPointsEnd.clear(); - roundedEndPointsEnd.reserve( nr_sides_per_circle + 2 ); - - roundedEndPointsStart.push_back( SFVEC2F( leftStart.x, leftStart.y ) ); - roundedEndPointsEnd.push_back( SFVEC2F( leftEnd.x, leftEnd.y ) ); - - int delta = 3600 / nr_sides_per_circle; - for( int ii = delta; ii < 1800; ii += delta ) - { - const SFVEC2F rotatedDirL = glm::rotate( leftDir, (float) ii * 2.0f * 3.14f / 3600.0f ); - const SFVEC2F rotatedDirR = glm::rotate( rightDir, (float)(1800 - ii) * 2.0f * 3.14f / 3600.0f ); - roundedEndPointsStart.push_back( SFVEC2F( start.x - rotatedDirL.y * radius, start.y + rotatedDirL.x * radius ) ); - roundedEndPointsEnd.push_back( SFVEC2F( end.x - rotatedDirR.y * radius, end.y + rotatedDirR.x * radius ) ); - } - roundedEndPointsStart.push_back( SFVEC2F( rightEnd.x, rightEnd.y ) ); - roundedEndPointsEnd.push_back( SFVEC2F( rightStart.x, rightStart.y ) ); - - if( roundedEndPointsStart.size() > 1 ) - { - for( unsigned int i = 0; i < ( roundedEndPointsStart.size() - 1 ); ++i ) - { - const SFVEC2F &v0 = roundedEndPointsStart[i + 0]; - const SFVEC2F &v1 = roundedEndPointsStart[i + 1]; - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_bot ), - SFVEC3F( v1.x, v1.y, layer_z_bot ), - SFVEC3F( v1.x, v1.y, layer_z_top ), - SFVEC3F( v0.x, v0.y, layer_z_top ) ); - - - const SFVEC2F n0 = glm::normalize( v0 - start ); - const SFVEC2F n1 = glm::normalize( v1 - start ); - const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); - const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); - } - } - roundedEndPointsStart.clear(); - - if( roundedEndPointsEnd.size() > 1 ) - { - for( unsigned int i = 0; i < ( roundedEndPointsEnd.size() - 1 ); ++i ) - { - const SFVEC2F &v0 = roundedEndPointsEnd[i + 0]; - const SFVEC2F &v1 = roundedEndPointsEnd[i + 1]; - - layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_top ), - SFVEC3F( v1.x, v1.y, layer_z_top ), - SFVEC3F( v1.x, v1.y, layer_z_bot ), - SFVEC3F( v0.x, v0.y, layer_z_bot ) ); - - - const SFVEC2F n0 = glm::normalize( v0 - end ); - const SFVEC2F n1 = glm::normalize( v1 - end ); - const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); - const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); - layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); - } - } - roundedEndPointsEnd.clear(); - } + add_object_to_triangle_layer( (const CROUNDSEGMENT2D *) object2d_A, + layerTriangles, + layer_z_top, layer_z_bot ); break; default: - { - } + wxFAIL_MSG("C3D_RENDER_OGL_LEGACY: Object type is not implemented"); break; } -#if 0 - // not yet used / implemented (can be used in future to clip the objects in the board borders - COBJECT2D *object2d_C = CSGITEM_FULL; + } - std::vector *object2d_B = CSGITEM_EMPTY; + const MAP_POLY &map_poly = m_settings.GetPolyMap(); - if( m_settings.GetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES ) ) - { - object2d_B = new std::vector(); + if( map_poly.find( layer_id ) != map_poly.end() ) + { + const SHAPE_POLY_SET *polyList = map_poly.at( layer_id ); - // Check if there are any layerhole that intersects this object - // Eg: a segment is cutted by a via hole or THT hole. - // ///////////////////////////////////////////////////////////// - const MAP_CONTAINER_2D &layerHolesMap = m_settings.GetMapLayersHoles(); - if( layerHolesMap.find(layer_id) != layerHolesMap.end() ) - { - MAP_CONTAINER_2D::const_iterator ii_hole = layerHolesMap.find(layer_id); - const CBVHCONTAINER2D *containerLayerHoles2d = static_cast(ii_hole->second); - - - CONST_LIST_OBJECT2D intersectionList; - containerLayerHoles2d->GetListObjectsIntersects( object2d_A->GetBBox(), intersectionList ); - - if( !intersectionList.empty() ) - { - for( CONST_LIST_OBJECT2D::const_iterator holeOnLayer = intersectionList.begin(); - holeOnLayer != intersectionList.end(); - holeOnLayer++ ) - { - const COBJECT2D *hole2d = static_cast(*holeOnLayer); - - //if( object2d_A->Intersects( hole2d->GetBBox() ) ) - //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) - object2d_B->push_back( hole2d ); - } - } - } - - // Check if there are any THT that intersects this object - // ///////////////////////////////////////////////////////////// - if( !m_settings.GetThroughHole_Inflated().GetList().empty() ) - { - CONST_LIST_OBJECT2D intersectionList; - m_settings.GetThroughHole_Inflated().GetListObjectsIntersects( object2d_A->GetBBox(), intersectionList ); - - if( !intersectionList.empty() ) - { - for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin(); - hole != intersectionList.end(); - hole++ ) - { - const COBJECT2D *hole2d = static_cast(*hole); - - //if( object2d_A->Intersects( hole2d->GetBBox() ) ) - //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) - object2d_B->push_back( hole2d ); - } - } - } - - if( object2d_B->empty() ) - { - delete object2d_B; - object2d_B = CSGITEM_EMPTY; - } - } - - if( (object2d_B == CSGITEM_EMPTY) && - (object2d_C == CSGITEM_FULL) ) - { -#if 0 - create_3d_object_from( m_object_container, object2d_A, m_settings.GetLayerBottomZpos3DU( layer_id ), - m_settings.GetLayerTopZpos3DU( layer_id ), - materialLayer, - layerColor ); -#else - CLAYERITEM *objPtr = new CLAYERITEM( object2d_A, m_settings.GetLayerBottomZpos3DU( layer_id ), - m_settings.GetLayerTopZpos3DU( layer_id ) ); - objPtr->SetMaterial( materialLayer ); - objPtr->SetColor( layerColor ); - m_object_container.Add( objPtr ); -#endif - } - else - { -#if 1 - CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A, object2d_B, object2d_C, - object2d_A->GetBoardItem() ); - m_containerWithObjectsToDelete.Add( itemCSG2d ); - - CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d, m_settings.GetLayerBottomZpos3DU( layer_id ), - m_settings.GetLayerTopZpos3DU( layer_id ) ); - - objPtr->SetMaterial( materialLayer ); - objPtr->SetColor( layerColor ); - m_object_container.Add( objPtr ); -#endif - } -#endif + layerTriangles->AddToMiddleContourns( *polyList, + layer_z_bot, + layer_z_top, + m_settings.BiuTo3Dunits(), + false ); } // Create display list // ///////////////////////////////////////////////////////////////////// m_ogl_disp_lists_layers[layer_id] = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, m_ogl_circle_texture, - layerColor ); + layer_z_bot, + layer_z_top ); }// for each layer on map +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_end_OpenGL_Load_Time = GetRunningMicroSecs(); +#endif + + // Load 3D models + // ///////////////////////////////////////////////////////////////////////// +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_start_models_Load_Time = GetRunningMicroSecs(); +#endif + + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Loading 3D models" ) ); + + load_3D_models(); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_end_models_Load_Time = GetRunningMicroSecs(); + + + printf( "C3D_RENDER_OGL_LEGACY::reload times:\n" ); + printf( " Reload board: %.3f ms\n", + (float)( stats_endReloadTime - stats_startReloadTime ) / 1000.0f ); + printf( " Loading to openGL: %.3f ms\n", + (float)( stats_end_OpenGL_Load_Time - stats_start_OpenGL_Load_Time ) / 1000.0f ); + printf( " Loading 3D models: %.3f ms\n", + (float)( stats_end_models_Load_Time - stats_start_models_Load_Time ) / 1000.0f ); + COBJECT2D_STATS::Instance().PrintStats(); +#endif + + if( aStatusTextReporter ) + { + // Calculation time in seconds + const double calculation_time = (double)( GetRunningMicroSecs() - + stats_startReloadTime) / 1e6; + + aStatusTextReporter->Report( wxString::Format( _( "Reload time %.3f s" ), + calculation_time ) ); + } } -void C3D_RENDER_OGL_LEGACY::add_triangle_top_bot( CLAYER_TRIANGLES *aDst, const SFVEC2F &v0, const SFVEC2F &v1, const SFVEC2F &v2, float top, float bot ) +void C3D_RENDER_OGL_LEGACY::add_triangle_top_bot( CLAYER_TRIANGLES *aDst, + const SFVEC2F &v0, + const SFVEC2F &v1, + const SFVEC2F &v2, + float top, + float bot ) { aDst->m_layer_bot_triangles->AddTriangle( SFVEC3F( v0.x, v0.y, bot ), SFVEC3F( v1.x, v1.y, bot ), @@ -797,3 +682,286 @@ void C3D_RENDER_OGL_LEGACY::add_triangle_top_bot( CLAYER_TRIANGLES *aDst, const SFVEC3F( v1.x, v1.y, top ), SFVEC3F( v0.x, v0.y, top ) ); } + + +void C3D_RENDER_OGL_LEGACY::get_layer_z_pos ( LAYER_ID aLayerID, + float &aOutZtop, + float &aOutZbot ) const +{ + aOutZbot = m_settings.GetLayerBottomZpos3DU( aLayerID ); + aOutZtop = m_settings.GetLayerTopZpos3DU( aLayerID ); + + if( aOutZtop < aOutZbot ) + { + float tmpFloat = aOutZbot; + aOutZbot = aOutZtop; + aOutZtop = tmpFloat; + } +} + + +void C3D_RENDER_OGL_LEGACY::generate_cylinder( const SFVEC2F &aCenter, + float aInnerRadius, + float aOuterRadius, + float aZtop, + float aZbot, + unsigned int aNr_sides_per_circle, + CLAYER_TRIANGLES *aDstLayer ) +{ + std::vector< SFVEC2F > innerContour; + std::vector< SFVEC2F > outerContour; + + generate_ring_contour( aCenter, + aInnerRadius, + aOuterRadius, + aNr_sides_per_circle, + innerContour, + outerContour, + false ); + + for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i ) + { + const SFVEC2F &vi0 = innerContour[i + 0]; + const SFVEC2F &vi1 = innerContour[i + 1]; + const SFVEC2F &vo0 = outerContour[i + 0]; + const SFVEC2F &vo1 = outerContour[i + 1]; + + aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ), + SFVEC3F( vi0.x, vi0.y, aZtop ), + SFVEC3F( vo0.x, vo0.y, aZtop ), + SFVEC3F( vo1.x, vo1.y, aZtop ) ); + + aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ), + SFVEC3F( vo1.x, vo1.y, aZbot ), + SFVEC3F( vo0.x, vo0.y, aZbot ), + SFVEC3F( vi0.x, vi0.y, aZbot ) ); + } + + aDstLayer->AddToMiddleContourns( outerContour, aZbot, aZtop, true ); + aDstLayer->AddToMiddleContourns( innerContour, aZbot, aZtop, false ); +} + + +void C3D_RENDER_OGL_LEGACY::generate_3D_Vias_and_Pads() +{ + if( m_settings.GetStats_Nr_Vias() ) + { + const unsigned int reserve_nr_triangles_estimation = + m_settings.GetNrSegmentsCircle( m_settings.GetStats_Med_Via_Hole_Diameter3DU() ) * + 8 * + m_settings.GetStats_Nr_Vias(); + + CLAYER_TRIANGLES *layerTriangleVIA = new CLAYER_TRIANGLES( reserve_nr_triangles_estimation ); + + // Insert plated vertical holes inside the board + // ///////////////////////////////////////////////////////////////////////// + + // Insert vias holes (vertical cylinders) + for( const TRACK* track = m_settings.GetBoard()->m_Track; + track; + track = track->Next() ) + { + if( track->Type() == PCB_VIA_T ) + { + const VIA *via = static_cast(track); + + const float holediameter = via->GetDrillValue() * m_settings.BiuTo3Dunits(); + const float thickness = m_settings.GetCopperThickness3DU(); + const float hole_inner_radius = ( holediameter / 2.0f ); + + const SFVEC2F via_center( via->GetStart().x * m_settings.BiuTo3Dunits(), + -via->GetStart().y * m_settings.BiuTo3Dunits() ); + + LAYER_ID top_layer, bottom_layer; + via->LayerPair( &top_layer, &bottom_layer ); + + float ztop, zbot, dummy; + + get_layer_z_pos( top_layer, ztop, dummy ); + get_layer_z_pos( bottom_layer, dummy, zbot ); + + wxASSERT( zbot < ztop ); + + generate_cylinder( via_center, + hole_inner_radius, + hole_inner_radius + thickness, + ztop, + zbot, + m_settings.GetNrSegmentsCircle( via->GetDrillValue() ), + layerTriangleVIA ); + } + } + + m_ogl_disp_list_via = new CLAYERS_OGL_DISP_LISTS( *layerTriangleVIA, + 0, + 0.0f, + 0.0f ); + + delete layerTriangleVIA; + } + + + if( m_settings.GetStats_Nr_Holes() > 0 ) + { + SHAPE_POLY_SET tht_outer_holes_poly; // Stores the outer poly of the copper holes (the pad) + SHAPE_POLY_SET tht_inner_holes_poly; // Stores the inner poly of the copper holes (the hole) + + tht_outer_holes_poly.RemoveAllContours(); + tht_inner_holes_poly.RemoveAllContours(); + + // Insert pads holes (vertical cylinders) + for( const MODULE* module = m_settings.GetBoard()->m_Modules; + module; + module = module->Next() ) + { + for( const D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) + { + if( pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED ) + { + const wxSize drillsize = pad->GetDrillSize(); + const bool hasHole = drillsize.x && drillsize.y; + + if( !hasHole ) + continue; + + // we use the hole diameter to calculate the seg count. + // for round holes, drillsize.x == drillsize.y + // for oblong holes, the diameter is the smaller of + // (drillsize.x, drillsize.y) + const int diam = std::min( drillsize.x, drillsize.y ) + + m_settings.GetCopperThicknessBIU() * 2; + + const int segmentsPerCircle = m_settings.GetNrSegmentsCircle( diam ); + + pad->BuildPadDrillShapePolygon( tht_outer_holes_poly, + m_settings.GetCopperThicknessBIU(), + segmentsPerCircle ); + + pad->BuildPadDrillShapePolygon( tht_inner_holes_poly, + 0, + segmentsPerCircle ); + } + } + } + + // Subtract the holes + tht_outer_holes_poly.BooleanSubtract( tht_inner_holes_poly, SHAPE_POLY_SET::PM_FAST ); + + + CCONTAINER2D holesContainer; + + Convert_shape_line_polygon_to_triangles( tht_outer_holes_poly, + holesContainer, + m_settings.BiuTo3Dunits(), + (const BOARD_ITEM &)*m_settings.GetBoard() ); + + const LIST_OBJECT2D &listHolesObject2d = holesContainer.GetList(); + + if( listHolesObject2d.size() > 0 ) + { + float layer_z_top, layer_z_bot, dummy; + + get_layer_z_pos( F_Cu, layer_z_top, dummy ); + get_layer_z_pos( B_Cu, dummy, layer_z_bot ); + + CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( listHolesObject2d.size() ); + + // Convert the list of objects(triangles) to triangle layer structure + for( LIST_OBJECT2D::const_iterator itemOnLayer = listHolesObject2d.begin(); + itemOnLayer != listHolesObject2d.end(); + ++itemOnLayer ) + { + const COBJECT2D *object2d_A = static_cast(*itemOnLayer); + + wxASSERT( object2d_A->GetObjectType() == OBJ2D_TRIANGLE ); + + const CTRIANGLE2D *tri = (const CTRIANGLE2D *)object2d_A; + + const SFVEC2F &v1 = tri->GetP1(); + const SFVEC2F &v2 = tri->GetP2(); + const SFVEC2F &v3 = tri->GetP3(); + + add_triangle_top_bot( layerTriangles, + v1, + v2, + v3, + layer_z_top, + layer_z_bot ); + } + + wxASSERT( tht_outer_holes_poly.OutlineCount() > 0 ); + + if( tht_outer_holes_poly.OutlineCount() > 0 ) + { + layerTriangles->AddToMiddleContourns( tht_outer_holes_poly, + layer_z_bot, + layer_z_top, + m_settings.BiuTo3Dunits(), + false ); + + m_ogl_disp_list_pads_holes = new CLAYERS_OGL_DISP_LISTS( + *layerTriangles, + m_ogl_circle_texture, // not need + layer_z_top, + layer_z_top ); + } + + delete layerTriangles; + } + } +} + + +/* + * This function will get models from the cache and load it to openGL lists + * in the form of C_OGL_3DMODEL. So this map of models will work as a local + * cache for this render. (cache based on C_OGL_3DMODEL with associated + * openGL lists in GPU memory) + */ +void C3D_RENDER_OGL_LEGACY::load_3D_models() +{ + if( (!m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL )) && + (!m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT )) && + (!m_settings.GetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL )) ) + return; + + // Go for all modules + for( const MODULE* module = m_settings.GetBoard()->m_Modules; + module; + module = module->Next() ) + { + if( !module->Models().empty() ) + { + // Get the list of model files for this model + std::list::const_iterator sM = module->Models().begin(); + std::list::const_iterator eM = module->Models().end(); + + while( sM != eM ) + { + if( !sM->m_Filename.empty() ) + { + // Check if the model is not present in our cache map + if( m_3dmodel_map.find( sM->m_Filename ) == m_3dmodel_map.end() ) + { + // It is not present, try get it from cache + const S3DMODEL *modelPtr = + m_settings.Get3DCacheManager()->GetModel( sM->m_Filename ); + + // only add it if the return is not NULL + if( modelPtr ) + { + C_OGL_3DMODEL* ogl_model = + new C_OGL_3DMODEL( *modelPtr, + m_settings.MaterialModeGet() ); + + if( ogl_model ) + m_3dmodel_map[ sM->m_Filename ] = ogl_model; + } + } + } + + ++sM; + } + } + } +} diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.cpp b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.cpp index 90a6544e0b..6f79fc3300 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.cpp +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro + * Copyright (C) 2015-2016 Mario Luzeiro * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or @@ -32,17 +32,42 @@ #include "ogl_legacy_utils.h" #include "common_ogl/ogl_utils.h" #include "../cimage.h" +#include +#include +#include <3d_math.h> -C3D_RENDER_OGL_LEGACY::C3D_RENDER_OGL_LEGACY( CINFO3D_VISU &aSettings, - S3D_CACHE *a3DModelManager ) : - C3D_RENDER_BASE( aSettings, a3DModelManager ) +#include + +/** + * Scale convertion from 3d model units to pcb units + */ +#define UNITS3D_TO_UNITSPCB (IU_PER_MM) + +C3D_RENDER_OGL_LEGACY::C3D_RENDER_OGL_LEGACY( CINFO3D_VISU &aSettings ) : + C3D_RENDER_BASE( aSettings ) { wxLogTrace( m_logTrace, wxT( "C3D_RENDER_OGL_LEGACY::C3D_RENDER_OGL_LEGACY" ) ); m_ogl_disp_lists_layers.clear(); + m_ogl_disp_lists_layers_holes_outer.clear(); + m_ogl_disp_lists_layers_holes_inner.clear(); m_triangles.clear(); - m_ogl_disp_list_board = 0; + m_ogl_disp_list_board = NULL; + + m_ogl_disp_list_through_holes_outer_with_npth = NULL; + m_ogl_disp_list_through_holes_outer = NULL; + m_ogl_disp_list_through_holes_inner = NULL; + m_ogl_disp_list_through_holes_vias_outer = NULL; + //m_ogl_disp_list_through_holes_vias_inner = NULL; + m_ogl_disp_list_via = NULL; + m_ogl_disp_list_pads_holes = NULL; + m_ogl_disp_list_vias_and_pad_holes_outer_contourn_and_caps = NULL; + m_ogl_circle_texture = 0; + m_ogl_disp_list_grid = 0; + m_last_grid_type = GRID3D_NONE; + + m_3dmodel_map.clear(); } @@ -50,6 +75,15 @@ C3D_RENDER_OGL_LEGACY::~C3D_RENDER_OGL_LEGACY() { wxLogTrace( m_logTrace, wxT( "C3D_RENDER_OGL_LEGACY::~C3D_RENDER_OGL_LEGACY" ) ); + ogl_free_all_display_lists(); + + glDeleteTextures( 1, &m_ogl_circle_texture ); +} + + +int C3D_RENDER_OGL_LEGACY::GetWaitForEditingTimeOut() +{ + return 50; // ms } @@ -60,617 +94,720 @@ void C3D_RENDER_OGL_LEGACY::SetCurWindowSize( const wxSize &aSize ) m_windowSize = aSize; glViewport( 0, 0, m_windowSize.x, m_windowSize.y ); - // Initialize here any screen dependent data + // Initialize here any screen dependent data here } } -/* defines */ -#define SPHERE 1 -#define CONE 2 -#define CUBE 3 - -/* csgOperation - * types of CSG operations - */ -typedef enum { - CSG_A, - CSG_B, - CSG_A_OR_B, - CSG_A_AND_B, - CSG_A_SUB_B, - CSG_B_SUB_A -} csgOperation; - -GLfloat cone_x = 0.0; -GLfloat cone_y = 0.0; -GLfloat cone_z = 0.0; - -GLfloat cube_x = 0.0; -GLfloat cube_y = 0.0; -GLfloat cube_z = 0.0; - -GLfloat sphere_x = 0.0; -GLfloat sphere_y = 0.0; -GLfloat sphere_z = 0.0; - -GLint mouse_state = -1; -GLint mouse_button = -1; - -csgOperation Op = CSG_A_OR_B; - -void (*A)(void); -void (*B)(void); - - -/* functions */ - -/* one() - * draw a single object - */ -void op_one(void(*a)(void)) +void C3D_RENDER_OGL_LEGACY::setLight_Front( bool enabled ) { - glEnable(GL_DEPTH_TEST); - a(); - glDisable(GL_DEPTH_TEST); + if( enabled ) + glEnable( GL_LIGHT0 ); + else + glDisable( GL_LIGHT0 ); } -/* or() - * boolean A or B (draw wherever A or B) - * algorithm: simple, just draw both with depth test enabled - */ -void op_or(void(*a)(void), void(*b)()) + +void C3D_RENDER_OGL_LEGACY::setLight_Top( bool enabled ) { - glPushAttrib(GL_ALL_ATTRIB_BITS); /* TODO - should just push depth */ - glEnable(GL_DEPTH_TEST); - //glDisable(GL_CULL_FACE); - a(); b(); - glPopAttrib(); + if( enabled ) + glEnable( GL_LIGHT1 ); + else + glDisable( GL_LIGHT1 ); } -/* inside() - * sets stencil buffer to show the part of A - * (front or back face according to 'face') - * that is inside of B. - */ -void op_inside(void(*a)(void), void(*b)(void), GLenum face, GLenum test) + +void C3D_RENDER_OGL_LEGACY::setLight_Bottom( bool enabled ) { - /* draw A into depth buffer, but not into color buffer */ - glEnable(GL_DEPTH_TEST); - glDisable(GL_STENCIL_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glCullFace(face); - a(); - - /* use stencil buffer to find the parts of A that are inside of B - * by first incrementing the stencil buffer wherever B's front faces - * are... - */ - - glDepthMask(GL_FALSE); - glEnable(GL_STENCIL_TEST); - //glStencilFunc(GL_LESS, 1, 0); - glStencilFunc(GL_ALWAYS, 0, 0); - glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT ); //GL_INCR); - glCullFace(GL_BACK); - b(); - - /* ...then decrement the stencil buffer wherever B's back faces are */ - //glStencilFunc(GL_EQUAL, 0, 0); - //glStencilFunc(GL_ALWAYS, 0, 0); - glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO ); - glCullFace(GL_FRONT); - b(); - - - /* now draw the part of A that is inside of B */ - glDepthMask(GL_TRUE); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glStencilFunc(test, 0, 1); - glDisable(GL_DEPTH_TEST); - glCullFace(face); - a(); - - /* reset stencil test */ - glDisable(GL_STENCIL_TEST); + if( enabled ) + glEnable( GL_LIGHT2 ); + else + glDisable( GL_LIGHT2 ); } -/* fixup() - * fixes up the depth buffer with A's depth values - */ -void fixup(void(*a)(void)) -{ - /* fix up the depth buffer */ - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glEnable(GL_DEPTH_TEST); - glDisable(GL_STENCIL_TEST); - glDepthFunc(GL_ALWAYS); - a(); - /* reset depth func */ - glDepthFunc(GL_LESS); +void C3D_RENDER_OGL_LEGACY::render_3D_arrows() +{ + const float arrow_size = RANGE_SCALE_3D * 0.30f; + + glDisable( GL_CULL_FACE ); + + // YxY squared view port, this is on propose + glViewport( 4, 4, m_windowSize.y / 8 , m_windowSize.y / 8 ); + glClear( GL_DEPTH_BUFFER_BIT ); + + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + gluPerspective( 45.0f, 1.0f, 0.001f, RANGE_SCALE_3D ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + const glm::mat4 TranslationMatrix = glm::translate( + glm::mat4(1.0f), + SFVEC3F( 0.0f, 0.0f, -(arrow_size * 2.75f) ) ); + + const glm::mat4 ViewMatrix = TranslationMatrix * + m_settings.CameraGet().GetRotationMatrix(); + + glLoadMatrixf( glm::value_ptr( ViewMatrix ) ); + + ogl_set_arrow_material(); + + glColor3f( 0.9f, 0.0f, 0.0f ); + OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), + SFVEC3F( arrow_size, 0.0f, 0.0f ), + 0.275f ); + + glColor3f( 0.0f, 0.9f, 0.0f ); + OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), + SFVEC3F( 0.0f, arrow_size, 0.0f ), + 0.275f ); + + glColor3f( 0.0f, 0.0f, 0.9f ); + OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), + SFVEC3F( 0.0f, 0.0f, arrow_size ), + 0.275f ); + + glEnable( GL_CULL_FACE ); } -/* and() - * boolean A and B (draw wherever A intersects B) - * algorithm: find where A is inside B, then find where - * B is inside A - */ -void op_and(void(*a)(void), void(*b)(void)) + +void C3D_RENDER_OGL_LEGACY::setupMaterials() { - op_inside(a, b, GL_BACK, GL_NOTEQUAL); -#if 1 /* set to 0 for faster, but incorrect results */ - fixup(b); -#endif - op_inside(b, a, GL_BACK, GL_NOTEQUAL); + + memset( &m_materials, 0, sizeof( m_materials ) ); + + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + { + // http://devernay.free.fr/cours/opengl/materials.html + + // Copper material mixed with the copper color + m_materials.m_Copper.m_Ambient = SFVEC3F( m_settings.m_CopperColor.r * 0.1f, + m_settings.m_CopperColor.g * 0.1f, + m_settings.m_CopperColor.b * 0.1f); + + m_materials.m_Copper.m_Specular = SFVEC3F( m_settings.m_CopperColor.r * 0.75f + 0.25f, + m_settings.m_CopperColor.g * 0.75f + 0.25f, + m_settings.m_CopperColor.b * 0.75f + 0.25f ); + + // This guess the material type(ex: copper vs gold) to determine the + // shininess factor between 0.1 and 0.4 + float shininessfactor = 0.40f - mapf( fabs( m_settings.m_CopperColor.r - + m_settings.m_CopperColor.g ), + 0.15f, 1.00f, + 0.00f, 0.30f ); + + m_materials.m_Copper.m_Shininess = shininessfactor * 128.0f; + + + // Paste material mixed with paste color + m_materials.m_Paste.m_Ambient = SFVEC3F( m_settings.m_SolderPasteColor.r, + m_settings.m_SolderPasteColor.g, + m_settings.m_SolderPasteColor.b ); + + m_materials.m_Paste.m_Specular = SFVEC3F( m_settings.m_SolderPasteColor.r * + m_settings.m_SolderPasteColor.r, + m_settings.m_SolderPasteColor.g * + m_settings.m_SolderPasteColor.g, + m_settings.m_SolderPasteColor.b * + m_settings.m_SolderPasteColor.b ); + + m_materials.m_Paste.m_Shininess = 0.1f * 128.0f; + + + // Silk screen material mixed with silk screen color + m_materials.m_SilkS.m_Ambient = SFVEC3F( m_settings.m_SilkScreenColor.r, + m_settings.m_SilkScreenColor.g, + m_settings.m_SilkScreenColor.b ); + + m_materials.m_SilkS.m_Specular = SFVEC3F( m_settings.m_SilkScreenColor.r * + m_settings.m_SilkScreenColor.r + 0.10f, + m_settings.m_SilkScreenColor.g * + m_settings.m_SilkScreenColor.g + 0.10f, + m_settings.m_SilkScreenColor.b * + m_settings.m_SilkScreenColor.b + 0.10f ); + + m_materials.m_SilkS.m_Shininess = 0.078125f * 128.0f; + + + // Solder mask material mixed with solder mask color + m_materials.m_SolderMask.m_Ambient = SFVEC3F( m_settings.m_SolderMaskColor.r * 0.3f, + m_settings.m_SolderMaskColor.g * 0.3f, + m_settings.m_SolderMaskColor.b * 0.3f ); + + m_materials.m_SolderMask.m_Specular = SFVEC3F( m_settings.m_SolderMaskColor.r * + m_settings.m_SolderMaskColor.r, + m_settings.m_SolderMaskColor.g * + m_settings.m_SolderMaskColor.g, + m_settings.m_SolderMaskColor.b * + m_settings.m_SolderMaskColor.b ); + + m_materials.m_SolderMask.m_Shininess = 0.8f * 128.0f; + m_materials.m_SolderMask.m_Transparency = 0.17f; + + + // Epoxy material + m_materials.m_EpoxyBoard.m_Ambient = SFVEC3F( 117.0f / 255.0f, + 97.0f / 255.0f, + 47.0f / 255.0f ); + + m_materials.m_EpoxyBoard.m_Diffuse = m_settings.m_BoardBodyColor; + + m_materials.m_EpoxyBoard.m_Specular = SFVEC3F( 18.0f / 255.0f, + 3.0f / 255.0f, + 20.0f / 255.0f ); + + m_materials.m_EpoxyBoard.m_Shininess = 0.1f * 128.0f; + } + else // Technical Mode + { + const SFVEC3F matAmbientColor = SFVEC3F( 0.10f ); + const SFVEC3F matSpecularColor = SFVEC3F( 0.10f ); + const float matShininess = 0.1f * 128.0f; + + // Copper material + m_materials.m_Copper.m_Ambient = matAmbientColor; + m_materials.m_Copper.m_Specular = matSpecularColor; + m_materials.m_Copper.m_Shininess = matShininess; + + // Paste material + m_materials.m_Paste.m_Ambient = matAmbientColor; + m_materials.m_Paste.m_Specular = matSpecularColor; + m_materials.m_Paste.m_Shininess = matShininess; + + // Silk screen material + m_materials.m_SilkS.m_Ambient = matAmbientColor; + m_materials.m_SilkS.m_Specular = matSpecularColor; + m_materials.m_SilkS.m_Shininess = matShininess; + + // Solder mask material + m_materials.m_SolderMask.m_Ambient = matAmbientColor; + m_materials.m_SolderMask.m_Specular = matSpecularColor; + m_materials.m_SolderMask.m_Shininess = matShininess; + m_materials.m_SolderMask.m_Transparency = 0.17f; + + // Epoxy material + m_materials.m_EpoxyBoard.m_Ambient = matAmbientColor; + m_materials.m_EpoxyBoard.m_Diffuse = m_settings.m_BoardBodyColor; + m_materials.m_EpoxyBoard.m_Specular = matSpecularColor; + m_materials.m_EpoxyBoard.m_Shininess = matShininess; + + // Gray material (used for example in technical vias and pad holes) + m_materials.m_GrayMaterial.m_Ambient = SFVEC3F( 0.8f, 0.8f, 0.8f ); + m_materials.m_GrayMaterial.m_Diffuse = SFVEC3F( 0.3f, 0.3f, 0.3f ); + m_materials.m_GrayMaterial.m_Specular = SFVEC3F( 0.4f, 0.4f, 0.4f ); + m_materials.m_GrayMaterial.m_Shininess = 0.01f * 128.0f; + } } -/* - * sub() - * boolean A subtract B (draw wherever A is and B is NOT) - * algorithm: find where a is inside B, then find where - * the BACK faces of B are NOT in A - */ -void op_sub(void(*a)(void), void(*b)(void)) + +void C3D_RENDER_OGL_LEGACY::set_layer_material( LAYER_ID aLayerID ) { - op_inside(a, b, GL_FRONT, GL_NOTEQUAL); + switch( aLayerID ) + { + case B_Mask: + case F_Mask: + m_materials.m_SolderMask.m_Diffuse = get_layer_color( aLayerID ); + OGL_SetMaterial( m_materials.m_SolderMask ); + break; -#if 1 /* set to 0 for faster, but incorrect results */ - fixup(b); -#endif - op_inside(b, a, GL_BACK, GL_EQUAL); - //op_inside(a, b, GL_FRONT, GL_NOTEQUAL); + case B_Paste: + case F_Paste: + m_materials.m_Paste.m_Diffuse = get_layer_color( aLayerID ); + OGL_SetMaterial( m_materials.m_Paste ); + break; + case B_SilkS: + case F_SilkS: + m_materials.m_SilkS.m_Diffuse = get_layer_color( aLayerID ); + OGL_SetMaterial( m_materials.m_SilkS ); + break; + + case B_Adhes: + case F_Adhes: + case Dwgs_User: + case Cmts_User: + case Eco1_User: + case Eco2_User: + case Edge_Cuts: + case Margin: + case B_CrtYd: + case F_CrtYd: + case B_Fab: + case F_Fab: + m_materials.m_Plastic.m_Diffuse = get_layer_color( aLayerID ); + m_materials.m_Plastic.m_Ambient = SFVEC3F( + m_materials.m_Plastic.m_Diffuse.r * 0.05f, + m_materials.m_Plastic.m_Diffuse.g * 0.05f, + m_materials.m_Plastic.m_Diffuse.b * 0.05f ); + + m_materials.m_Plastic.m_Specular = SFVEC3F( + m_materials.m_Plastic.m_Diffuse.r * 0.7f, + m_materials.m_Plastic.m_Diffuse.g * 0.7f, + m_materials.m_Plastic.m_Diffuse.b * 0.7f ); + + m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f; + OGL_SetMaterial( m_materials.m_Plastic ); + break; + + default: + m_materials.m_Copper.m_Diffuse = get_layer_color( aLayerID ); + OGL_SetMaterial( m_materials.m_Copper ); + + break; + } } -/* sphere() - * draw a yellow sphere - */ -void sphere(void) -{/* - glLoadName(2); - glPushMatrix(); - glTranslatef(sphere_x, sphere_y, sphere_z); - glColor3f(1.0, 1.0, 0.0); - glutSolidSphere(5.0, 16, 16); - glPopMatrix();*/ - glBegin(GL_QUADS); // Begin drawing the color cube with 6 quads - // Top face (y = 1.0f) - // Define vertices in counter-clockwise (CCW) order with normal pointing out - glColor3f(0.0f, 1.0f, 0.0f); // Green - glVertex3f( 1.0f, 1.0f, -1.0f); - glVertex3f(-1.0f, 1.0f, -1.0f); - glVertex3f(-1.0f, 1.0f, 1.0f); - glVertex3f( 1.0f, 1.0f, 1.0f); - // Bottom face (y = -1.0f) - glColor3f(1.0f, 0.5f, 0.0f); // Orange - glVertex3f( 1.0f, -1.0f, 1.0f); - glVertex3f(-1.0f, -1.0f, 1.0f); - glVertex3f(-1.0f, -1.0f, -1.0f); - glVertex3f( 1.0f, -1.0f, -1.0f); +SFVEC3F C3D_RENDER_OGL_LEGACY::get_layer_color( LAYER_ID aLayerID ) +{ + SFVEC3F layerColor = m_settings.GetLayerColor( aLayerID ); - // Front face (z = 1.0f) - glColor3f(1.0f, 0.0f, 0.0f); // Red - glVertex3f( 1.0f, 1.0f, 1.0f); - glVertex3f(-1.0f, 1.0f, 1.0f); - glVertex3f(-1.0f, -1.0f, 1.0f); - glVertex3f( 1.0f, -1.0f, 1.0f); + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + { + switch( aLayerID ) + { + case B_Adhes: + case F_Adhes: + break; - // Back face (z = -1.0f) - glColor3f(1.0f, 1.0f, 0.0f); // Yellow - glVertex3f( 1.0f, -1.0f, -1.0f); - glVertex3f(-1.0f, -1.0f, -1.0f); - glVertex3f(-1.0f, 1.0f, -1.0f); - glVertex3f( 1.0f, 1.0f, -1.0f); + case B_Mask: + case F_Mask: + layerColor = m_settings.m_SolderMaskColor; + break; - // Left face (x = -1.0f) - glColor3f(0.0f, 0.0f, 1.0f); // Blue - glVertex3f(-1.0f, 1.0f, 1.0f); - glVertex3f(-1.0f, 1.0f, -1.0f); - glVertex3f(-1.0f, -1.0f, -1.0f); - glVertex3f(-1.0f, -1.0f, 1.0f); + case B_Paste: + case F_Paste: + layerColor = m_settings.m_SolderPasteColor; + break; - // Right face (x = 1.0f) - glColor3f(1.0f, 0.0f, 1.0f); // Magenta - glVertex3f(1.0f, 1.0f, -1.0f); - glVertex3f(1.0f, 1.0f, 1.0f); - glVertex3f(1.0f, -1.0f, 1.0f); - glVertex3f(1.0f, -1.0f, -1.0f); - glEnd(); // End of drawing color-cube + case B_SilkS: + case F_SilkS: + layerColor = m_settings.m_SilkScreenColor; + break; + + case Dwgs_User: + case Cmts_User: + case Eco1_User: + case Eco2_User: + case Edge_Cuts: + case Margin: + break; + + case B_CrtYd: + case F_CrtYd: + break; + + case B_Fab: + case F_Fab: + break; + + default: + layerColor = m_settings.m_CopperColor; + break; + } + } + + return layerColor; } -/* cube() - * draw a red cube - */ -void cube(void) +void init_lights(void) { - glPushMatrix(); - glColor3f(0.3, 0.8, 0.1); - OGL_draw_arrow( SFVEC3F( 1.0f, 0.0f, 0.0f ), - SFVEC3F( 0.0f, 0.0f, 0.0f ), - 0.2f ); - glPopMatrix(); -} - -/* cone() - * draw a green cone - */ -void cone(void) -{ - /* glLoadName(3); - glPushMatrix(); - glTranslatef(cone_x, cone_y, cone_z); - glColor3f(0.0, 1.0, 0.0); - glTranslatef(0.0, 0.0, -6.5); - glutSolidCone(4.0, 15.0, 16, 16); - glRotatef(180.0, 1.0, 0.0, 0.0); - glutSolidCone(4.0, 0.0, 16, 1); - glPopMatrix();*/ -} - -void init(void) -{ - //tbInit(GLUT_MIDDLE_BUTTON); - - //glSelectBuffer(SELECT_BUFFER, select_buffer); - - // glEnable( GL_DITHER ); - - glDepthFunc(GL_LESS); - glEnable(GL_DEPTH_TEST); - - //glEnable(GL_LIGHT0); - //glEnable(GL_LIGHT1); - //glEnable(GL_LIGHT2); - //glEnable(GL_LIGHTING); - // Setup light // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml // ///////////////////////////////////////////////////////////////////////// - { - const GLfloat ambient[] = { 0.1f, 0.0f, 0.1f, 1.0f }; - const GLfloat diffuse[] = { 0.3f, 0.3f, 0.3f, 1.0f }; - const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; - //const GLfloat lmodel_ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; + const GLfloat ambient[] = { 0.084f, 0.084f, 0.084f, 1.0f }; + const GLfloat diffuse0[] = { 0.3f, 0.3f, 0.3f, 1.0f }; + const GLfloat specular0[] = { 0.5f, 0.5f, 0.5f, 1.0f }; glLightfv( GL_LIGHT0, GL_AMBIENT, ambient ); - glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse ); - glLightfv( GL_LIGHT0, GL_SPECULAR, specular ); + glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse0 ); + glLightfv( GL_LIGHT0, GL_SPECULAR, specular0 ); + + const GLfloat diffuse12[] = { 0.7f, 0.7f, 0.7f, 1.0f }; + const GLfloat specular12[] = { 0.7f, 0.7f, 0.7f, 1.0f }; + + // defines a directional light that points along the negative z-axis + GLfloat position[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; + + // This makes a vector slight not perpendicular with XZ plane + const SFVEC3F vectorLight = SphericalToCartesian( glm::pi() * 0.03f, + glm::pi() * 0.25f ); + + position[0] = vectorLight.x; + position[1] = vectorLight.y; + position[2] = vectorLight.z; + + glLightfv( GL_LIGHT1, GL_AMBIENT, ambient ); + glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse12 ); + glLightfv( GL_LIGHT1, GL_SPECULAR, specular12 ); + glLightfv( GL_LIGHT1, GL_POSITION, position ); + + + // defines a directional light that points along the positive z-axis + position[2] = -position[2]; - const GLfloat position1[]= { 0.0f, 0.0f, 1.0f, 0.0f }; // defines a directional light that points along the negative z-axis glLightfv( GL_LIGHT2, GL_AMBIENT, ambient ); - glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse ); - glLightfv( GL_LIGHT2, GL_SPECULAR, specular ); - glLightfv( GL_LIGHT2, GL_POSITION, position1 ); + glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse12 ); + glLightfv( GL_LIGHT2, GL_SPECULAR, specular12 ); + glLightfv( GL_LIGHT2, GL_POSITION, position ); - const GLfloat position2[]= { 0.0f, 0.0f, -1.0f, 0.0f }; // defines a directional light that points along the positive z-axis - glLightfv( GL_LIGHT2, GL_AMBIENT, ambient ); - glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse ); - glLightfv( GL_LIGHT2, GL_SPECULAR, specular ); - glLightfv( GL_LIGHT2, GL_POSITION, position2 ); - //glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); -} + const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE ); + glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient ); - - glEnable(GL_CULL_FACE); - - glClearColor(0.0, 0.0, 1.0, 0.0); -{ - const SFVEC4F ambient = SFVEC4F( 0.2,0.2,0.2, 1.0f ); - const SFVEC4F diffuse = SFVEC4F( 0.5,0.1,0.1, 1.0f ); - const SFVEC4F specular = SFVEC4F( 0.1,0.1,0.8, 1.0f ); - //const SFVEC4F emissive = SFVEC4F( material.m_Emissive, 1.0f ); - - glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r ); - glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r ); - glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r ); - //glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r ); - } - glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 128 ); - glDisable( GL_COLOR_MATERIAL ); - - glFrontFace( GL_CCW ); // This is the openGL default - glEnable( GL_NORMALIZE ); // This allow openGL to normalize the normals after transformations + glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE ); } -void display(void) +bool C3D_RENDER_OGL_LEGACY::Redraw( bool aIsMoving, + REPORTER *aStatusTextReporter ) { - glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); - //glClearDepth( 1.0f ); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - glPushMatrix(); - //tbMatrix(); - - switch(Op) { - case CSG_A: - op_one(A); - break; - case CSG_B: - op_one(B); - break; - case CSG_A_OR_B: - op_or(A, B); - break; - case CSG_A_AND_B: - op_and(A, B); - break; - case CSG_A_SUB_B: - op_sub(A, B); - break; - case CSG_B_SUB_A: - op_sub(B, A); - break; - } - - glPopMatrix(); -} - -static const C3D_RENDER_OGL_LEGACY *renderclass = NULL; - -void draw_board(void) -{ - if( renderclass ) - { - renderclass->GetBoardDispList()->DrawTop(); - renderclass->GetBoardDispList()->DrawBot(); - } -} - -void draw_b_mask(void) -{ - if( renderclass ) - { - const CLAYERS_OGL_DISP_LISTS *layer = renderclass->GetLayerDispList( B_Mask ); - layer->DrawTop(); - layer->DrawBot(); - } -} - - - -static SFVEC3F light = SFVEC3F(); - -void C3D_RENDER_OGL_LEGACY::Redraw( bool aIsMoving ) -{ - (void) aIsMoving; - // Initialize openGL if( !m_is_opengl_initialized ) { if( !initializeOpenGL() ) - return; - - A = sphere; - B = cube; - - //Op = CSG_A; - //Op = CSG_B; - //Op = CSG_A_OR_B; - //Op = CSG_A_AND_B; - //Op = CSG_A_SUB_B; - Op = CSG_B_SUB_A; + return false; } -/* - const GLfloat position0[]= { 0.0f, 0.0f, 1.0f, 0.0f }; // defines a directional light that points along the negative z-axis - glLightfv( GL_LIGHT0, GL_POSITION, position0 ); -*/ + if( m_reloadRequested ) - reload(); + { + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Loading..." ) ); - init(); + reload( aStatusTextReporter ); + setupMaterials(); - glEnable( GL_LIGHT0 ); - glEnable(GL_LIGHTING); + // generate a new 3D grid as the size of the board may had changed + m_last_grid_type = m_settings.GridGet(); + generate_new_3DGrid( m_last_grid_type ); + } + else + { + // Check if grid was changed + if( m_settings.GridGet() != m_last_grid_type ) + { + // and generate a new one + m_last_grid_type = m_settings.GridGet(); + generate_new_3DGrid( m_last_grid_type ); + } + } + + // Initial setup + // ///////////////////////////////////////////////////////////////////////// + glDepthFunc( GL_LESS ); + glEnable( GL_CULL_FACE ); + glFrontFace( GL_CCW ); // This is the openGL default + glEnable( GL_NORMALIZE ); // This allow openGL to normalize the normals after transformations + + glViewport( 0, 0, m_windowSize.x, m_windowSize.y ); // clear color and depth buffers // ///////////////////////////////////////////////////////////////////////// glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); glClearDepth( 1.0f ); + glClearStencil( 0x00 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + + // Draw the background ( rectangle with color gradient) + // ///////////////////////////////////////////////////////////////////////// + OGL_DrawBackground( SFVEC3F( m_settings.m_BgColorTop ), + SFVEC3F( m_settings.m_BgColorBot ) ); + + glEnable( GL_DEPTH_TEST ); + + // Set projection and modelview matrixes // ///////////////////////////////////////////////////////////////////////// glMatrixMode( GL_PROJECTION ); glLoadMatrixf( glm::value_ptr( m_settings.CameraGet().GetProjectionMatrix() ) ); glMatrixMode( GL_MODELVIEW ); - - // Position the headlight glLoadIdentity(); - - //glTranslatef( -m_settings.GetBBox3DU().GetCenter().x, -m_settings.GetBBox3DU().GetCenter().y, -m_settings.GetBBox3DU().GetCenter().z ); -// glTranslatef( m_settings.GetBBox3DU().GetCenter().x, m_settings.GetBBox3DU().GetCenter().y, m_settings.GetBBox3DU().GetCenter().z ); - glLoadMatrixf( glm::value_ptr( m_settings.CameraGet().GetViewMatrix() ) ); + + // Position the headlight + // ///////////////////////////////////////////////////////////////////////// + + setLight_Front( true ); + setLight_Top( true ); + setLight_Bottom( true ); + + glEnable( GL_LIGHTING ); + { - const SFVEC3F &cameraPos = m_settings.CameraGet().GetPos();//m_settings.CameraGet().GetPos(); - const GLfloat headlight_pos[] = { cameraPos.x, cameraPos.y, cameraPos.z, 1.0f }; + const SFVEC3F &cameraPos = m_settings.CameraGet().GetPos(); + + // Place the light at a minimun Z so the diffuse factor will not drop + // and the board will still look with good light. + float zpos; + + if( cameraPos.z > 0.0f ) + { + zpos = glm::max( cameraPos.z, 0.5f ) + cameraPos.z * cameraPos.z; + } + else + { + zpos = glm::min( cameraPos.z,-0.5f ) - cameraPos.z * cameraPos.z; + } + + const GLfloat headlight_pos[] = { cameraPos.x, + cameraPos.y, + zpos, + 1.0f }; // This is a point light + glLightfv( GL_LIGHT0, GL_POSITION, headlight_pos ); } - const SFVEC4F specular = SFVEC4F( 0.5f, 1.0f, 0.5f, 1.0f ); + // Display board body + // ///////////////////////////////////////////////////////////////////////// + if( m_settings.GetFlag( FL_SHOW_BOARD_BODY ) ) + { + if( m_ogl_disp_list_board ) + { + m_ogl_disp_list_board->ApplyScalePosition( -m_settings.GetEpoxyThickness3DU() / 2.0f, + m_settings.GetEpoxyThickness3DU() ); - glMaterialfv( GL_FRONT, GL_SPECULAR, &specular.r ); - glMaterialf( GL_FRONT, GL_SHININESS, 10.0f ); + OGL_SetMaterial( m_materials.m_EpoxyBoard ); - SFVEC4F layerColor4 = SFVEC4F( specular.x, specular.y, specular.z, 1.0f ); - glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &layerColor4.x ); - glDisable( GL_COLOR_MATERIAL ); + m_ogl_disp_list_board->SetItIsTransparent( false ); -/* - glPushMatrix(); + if( m_ogl_disp_list_through_holes_outer_with_npth ) + { + m_ogl_disp_list_through_holes_outer_with_npth->ApplyScalePosition( + -m_settings.GetEpoxyThickness3DU() / 2.0f, + m_settings.GetEpoxyThickness3DU() ); - glTranslatef( m_settings.GetBBox3DU().GetCenter().x, m_settings.GetBBox3DU().GetCenter().y, m_settings.GetBBox3DU().GetCenter().z ); + m_ogl_disp_list_board->DrawAllCameraCulledSubtractLayer( + m_ogl_disp_list_through_holes_outer_with_npth, + NULL ); + } + else + { + m_ogl_disp_list_board->DrawAll(); + } + } + } - glEnable( GL_COLOR_MATERIAL ); - glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + { + // Draw vias and pad holes with copper material + set_layer_material( B_Cu ); + } + else + { + OGL_SetMaterial( m_materials.m_GrayMaterial ); + } - //const SFVEC4F specular = SFVEC4F( 0.5f, 0.5f, 0.5f, 1.0f ); + if( m_ogl_disp_list_via ) + { + m_ogl_disp_list_via->DrawAll(); + } - glMaterialfv( GL_FRONT, GL_SPECULAR, &specular.r ); - glMaterialf( GL_FRONT, GL_SHININESS, 10.0f ); + if( m_ogl_disp_list_pads_holes ) + { + m_ogl_disp_list_pads_holes->DrawAll(); + } - glColor3f( 0.9f, 0.0f, 0.0f ); - OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), - SFVEC3F( RANGE_SCALE_3D / 1.0f, 0.0f, 0.0f ), - 0.2f ); - glColor3f( 0.0f, 0.9f, 0.0f ); - OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), - SFVEC3F( 0.0f, RANGE_SCALE_3D / 1.0f, 0.0f ), - 0.2f ); - - glColor3f( 0.0f, 0.0f, 0.9f ); - OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), - SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 1.0f ), - 0.2f ); - glPopMatrix(); -*/ - -/* + // Display copper and tech layers + // ///////////////////////////////////////////////////////////////////////// for( MAP_OGL_DISP_LISTS::const_iterator ii = m_ogl_disp_lists_layers.begin(); ii != m_ogl_disp_lists_layers.end(); - ii++ ) + ++ii ) { - LAYER_ID layer_id = (LAYER_ID)(ii->first); + const LAYER_ID layer_id = (LAYER_ID)(ii->first); // Mask kayers are not processed here because they are a special case if( (layer_id == B_Mask) || (layer_id == F_Mask) ) continue; + // Do not show inner layers when it is displaying the board + if( m_settings.GetFlag( FL_SHOW_BOARD_BODY ) ) + { + if( (layer_id > F_Cu) && (layer_id < B_Cu) ) + continue; + } + + glPushMatrix(); + + // !TODO: if we want to increase the separation between layers + //glScalef( 1.0f, 1.0f, 3.0f ); + + CLAYERS_OGL_DISP_LISTS *pLayerDispList = static_cast(ii->second); - pLayerDispList->DrawAll(); + set_layer_material( layer_id ); + + if( m_ogl_disp_list_through_holes_outer ) + m_ogl_disp_list_through_holes_outer->ApplyScalePosition( + pLayerDispList->GetZBot(), + pLayerDispList->GetZTop() - pLayerDispList->GetZBot() ); + + if( (layer_id >= F_Cu) && (layer_id <= B_Cu) ) + { + if( m_ogl_disp_lists_layers_holes_outer.find( layer_id ) != + m_ogl_disp_lists_layers_holes_outer.end() ) + { + const CLAYERS_OGL_DISP_LISTS* viasHolesLayer = + m_ogl_disp_lists_layers_holes_outer.at( layer_id ); + + wxASSERT( viasHolesLayer != NULL ); + + if( viasHolesLayer != NULL ) + { + pLayerDispList->DrawAllCameraCulledSubtractLayer( + m_ogl_disp_list_through_holes_outer, + viasHolesLayer, + (aIsMoving == false) ); + } + } + else + { + pLayerDispList->DrawAllCameraCulledSubtractLayer( + m_ogl_disp_list_through_holes_outer, + NULL, + (aIsMoving == false) ); + } + } + else + { + pLayerDispList->DrawAllCameraCulled( m_settings.CameraGet().GetPos().z, + (aIsMoving == false) ); + } + + glPopMatrix(); } -*/ - renderclass = this; - //op_sub( draw_b_mask, draw_board ); - //op_sub( draw_board, draw_b_mask ); - const CLAYERS_OGL_DISP_LISTS *layer = renderclass->GetLayerDispList( B_Mask ); + // Render 3D Models (Non-transparent) + // ///////////////////////////////////////////////////////////////////////// - //glEnable(GL_LIGHTING); - //glEnable(GL_LIGHT0); -/* - glEnable(GL_DEPTH_TEST); - glDisable(GL_STENCIL_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glDisable(GL_CULL_FACE); - m_ogl_disp_list_board->DrawBot(); -*/ + //setLight_Top( false ); + //setLight_Bottom( true ); + render_3D_models( false, false ); - glCullFace(GL_BACK); - - glDisable(GL_DEPTH_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glDepthMask(GL_FALSE); - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 1, 0); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE ); - layer->DrawBot(); - - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glStencilFunc(GL_EQUAL, 0, 1); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP ); - //glDisable(GL_DEPTH_TEST); - //glCullFace(face); - m_ogl_disp_list_board->DrawBot(); - - //glClear( GL_STENCIL_BUFFER_BIT ); - - glDisable(GL_DEPTH_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glDepthMask(GL_FALSE); - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 2, 0); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE ); - layer->DrawTop(); - - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glStencilFunc(GL_NOTEQUAL, 2, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR ); - //glDisable(GL_DEPTH_TEST); - //glCullFace(face); - m_ogl_disp_list_board->DrawTop(); - - //glClear( GL_STENCIL_BUFFER_BIT ); - - //glCullFace(GL_FRONT); -/* - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glDepthMask(GL_FALSE); - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_GEQUAL, 3, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR ); - layer->DrawBot(); - layer->DrawTop(); - -*/ - glEnable(GL_CULL_FACE); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glCullFace(GL_FRONT); - glStencilFunc(GL_GEQUAL, 3, 0x03); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP ); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - layer->DrawMiddle(); + //setLight_Top( true ); + //setLight_Bottom( false ); + render_3D_models( true, false ); + // Display transparent mask layers + // ///////////////////////////////////////////////////////////////////////// + if( m_settings.GetFlag( FL_SOLDERMASK ) ) + { + //setLight_Top( true ); + //setLight_Bottom( true ); + + if( m_settings.CameraGet().GetPos().z > 0 ) + { + render_solder_mask_layer( B_Mask, m_settings.GetLayerTopZpos3DU( B_Mask ), + aIsMoving ); + + render_solder_mask_layer( F_Mask, m_settings.GetLayerBottomZpos3DU( F_Mask ), + aIsMoving ); + } + else + { + render_solder_mask_layer( F_Mask, m_settings.GetLayerBottomZpos3DU( F_Mask ), + aIsMoving ); + + render_solder_mask_layer( B_Mask, m_settings.GetLayerTopZpos3DU( B_Mask ), + aIsMoving ); + } + } + + + // Render 3D Models (Transparent) + // ///////////////////////////////////////////////////////////////////////// + + //setLight_Top( false ); + //setLight_Bottom( true ); + render_3D_models( false, true ); + + //setLight_Top( true ); + //setLight_Bottom( false ); + render_3D_models( true, true ); + + + // Render Grid + // ///////////////////////////////////////////////////////////////////////// + + if( m_settings.GridGet() != GRID3D_NONE ) + { + glDisable( GL_LIGHTING ); + + if( glIsList( m_ogl_disp_list_grid ) ) + glCallList( m_ogl_disp_list_grid ); + + glEnable( GL_LIGHTING ); + } + + + // Render 3D arrows + // ///////////////////////////////////////////////////////////////////////// + if( m_settings.GetFlag( FL_AXIS ) ) + render_3D_arrows(); + + // Return back to the original viewport (this is important if we want + // to take a screenshot after the render) + // ///////////////////////////////////////////////////////////////////////// + glViewport( 0, 0, m_windowSize.x, m_windowSize.y ); + + return false; } bool C3D_RENDER_OGL_LEGACY::initializeOpenGL() { - GLenum err = glewInit(); - - if( GLEW_OK != err ) - { - std::string msgError = (const char*) glewGetErrorString( err ); - - return false; - } - else - { - wxLogTrace( m_logTrace, - wxString( wxT( "C3D_RENDER_OGL_LEGACY::initializeOpenGL Using GLEW " ) ) + - FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) ); - } + glEnable( GL_LINE_SMOOTH ); + glShadeModel( GL_SMOOTH ); // 4-byte pixel alignment glPixelStorei( GL_UNPACK_ALIGNMENT, 4 ); // Initialize the open GL texture to draw the filled semi-circle of the segments - CIMAGE circleImage( SIZE_OF_CIRCLE_TEXTURE, SIZE_OF_CIRCLE_TEXTURE ); + CIMAGE *circleImage = new CIMAGE( SIZE_OF_CIRCLE_TEXTURE, SIZE_OF_CIRCLE_TEXTURE ); - circleImage.CircleFilled( (SIZE_OF_CIRCLE_TEXTURE / 2) - 0, (SIZE_OF_CIRCLE_TEXTURE / 2) - 0, (SIZE_OF_CIRCLE_TEXTURE / 2) - 4, 0xFF ); - //circleImage.CircleFilled( (SIZE_OF_CIRCLE_TEXTURE / 4)*1.5f - 1, (SIZE_OF_CIRCLE_TEXTURE / 4)*1.5f - 1, (SIZE_OF_CIRCLE_TEXTURE / 4)*1.5f - 2, 0xFF ); + if( !circleImage ) + return false; - CIMAGE bluredCircle( circleImage ); - circleImage.EfxFilter( &bluredCircle, FILTER_GAUSSIAN_BLUR2 ); + circleImage->CircleFilled( (SIZE_OF_CIRCLE_TEXTURE / 2) - 0, + (SIZE_OF_CIRCLE_TEXTURE / 2) - 0, + (SIZE_OF_CIRCLE_TEXTURE / 2) - 4, + 0xFF ); - m_ogl_circle_texture = OGL_LoadTexture( circleImage ); + //circleImage->CircleFilled( (SIZE_OF_CIRCLE_TEXTURE / 4)*1.5f - 1, + // (SIZE_OF_CIRCLE_TEXTURE / 4)*1.5f - 1, + // (SIZE_OF_CIRCLE_TEXTURE / 4)*1.5f - 2, 0xFF ); - circleImage.SaveAsPNG("circleImage.png"); + CIMAGE *circleImage_Copy = new CIMAGE( *circleImage ); - bluredCircle.SaveAsPNG("circleImage_blured.png"); + circleImage->EfxFilter( circleImage_Copy, FILTER_BLUR_3X3 ); + + m_ogl_circle_texture = OGL_LoadTexture( *circleImage ); + + //circleImage_Copy->SaveAsPNG("circleImage.png"); + delete circleImage_Copy; + circleImage_Copy = 0; + + //circleImage->SaveAsPNG("circleImage_blured.png"); + delete circleImage; + circleImage = 0; + + init_lights(); + + // Use this mode if you want see the triangle lines (debug proposes) + //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); m_is_opengl_initialized = true; + return true; } @@ -680,18 +817,30 @@ void C3D_RENDER_OGL_LEGACY::ogl_set_arrow_material() glEnable( GL_COLOR_MATERIAL ); glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); + const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f ); + const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f ); + const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f ); const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r ); - glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f ); + glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f ); + + glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r ); + glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r ); + glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r ); } void C3D_RENDER_OGL_LEGACY::ogl_free_all_display_lists() { + if( glIsList( m_ogl_disp_list_grid ) ) + glDeleteLists( m_ogl_disp_list_grid, 1 ); + + m_ogl_disp_list_grid = 0; + for( MAP_OGL_DISP_LISTS::const_iterator ii = m_ogl_disp_lists_layers.begin(); ii != m_ogl_disp_lists_layers.end(); - ii++ ) + ++ii ) { CLAYERS_OGL_DISP_LISTS *pLayerDispList = static_cast(ii->second); delete pLayerDispList; @@ -699,9 +848,31 @@ void C3D_RENDER_OGL_LEGACY::ogl_free_all_display_lists() m_ogl_disp_lists_layers.clear(); + + for( MAP_OGL_DISP_LISTS::const_iterator ii = m_ogl_disp_lists_layers_holes_outer.begin(); + ii != m_ogl_disp_lists_layers_holes_outer.end(); + ++ii ) + { + CLAYERS_OGL_DISP_LISTS *pLayerDispList = static_cast(ii->second); + delete pLayerDispList; + } + + m_ogl_disp_lists_layers_holes_outer.clear(); + + + for( MAP_OGL_DISP_LISTS::const_iterator ii = m_ogl_disp_lists_layers_holes_inner.begin(); + ii != m_ogl_disp_lists_layers_holes_inner.end(); + ++ii ) + { + CLAYERS_OGL_DISP_LISTS *pLayerDispList = static_cast(ii->second); + delete pLayerDispList; + } + + m_ogl_disp_lists_layers_holes_inner.clear(); + for( MAP_TRIANGLES::const_iterator ii = m_triangles.begin(); ii != m_triangles.end(); - ii++ ) + ++ii ) { CLAYER_TRIANGLES *pointer = static_cast(ii->second); delete pointer; @@ -709,6 +880,413 @@ void C3D_RENDER_OGL_LEGACY::ogl_free_all_display_lists() m_triangles.clear(); + + for( MAP_3DMODEL::const_iterator ii = m_3dmodel_map.begin(); + ii != m_3dmodel_map.end(); + ++ii ) + { + C_OGL_3DMODEL *pointer = static_cast(ii->second); + delete pointer; + } + + m_3dmodel_map.clear(); + + delete m_ogl_disp_list_board; m_ogl_disp_list_board = 0; + + delete m_ogl_disp_list_through_holes_outer_with_npth; + m_ogl_disp_list_through_holes_outer_with_npth = 0; + + delete m_ogl_disp_list_through_holes_outer; + m_ogl_disp_list_through_holes_outer = 0; + + delete m_ogl_disp_list_through_holes_inner; + m_ogl_disp_list_through_holes_inner = 0; + + delete m_ogl_disp_list_through_holes_vias_outer; + m_ogl_disp_list_through_holes_vias_outer = 0; + + delete m_ogl_disp_list_via; + m_ogl_disp_list_via = 0; + + delete m_ogl_disp_list_pads_holes; + m_ogl_disp_list_pads_holes = 0; + + delete m_ogl_disp_list_vias_and_pad_holes_outer_contourn_and_caps; + m_ogl_disp_list_vias_and_pad_holes_outer_contourn_and_caps = 0; +} + + +void C3D_RENDER_OGL_LEGACY::render_solder_mask_layer( LAYER_ID aLayerID, + float aZPosition, + bool aIsRenderingOnPreviewMode ) +{ + wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) ); + + if( m_ogl_disp_list_board ) + { + if( m_ogl_disp_lists_layers.find( aLayerID ) != + m_ogl_disp_lists_layers.end() ) + { + CLAYERS_OGL_DISP_LISTS *pLayerDispListMask = m_ogl_disp_lists_layers.at( aLayerID ); + + if( m_ogl_disp_list_through_holes_vias_outer ) + m_ogl_disp_list_through_holes_vias_outer->ApplyScalePosition( + aZPosition, + m_settings.GetNonCopperLayerThickness3DU() ); + + m_ogl_disp_list_board->ApplyScalePosition( + aZPosition, + m_settings.GetNonCopperLayerThickness3DU() ); + + set_layer_material( aLayerID ); + + m_ogl_disp_list_board->SetItIsTransparent( true ); + + m_ogl_disp_list_board->DrawAllCameraCulledSubtractLayer( + pLayerDispListMask, + m_ogl_disp_list_through_holes_vias_outer, + !aIsRenderingOnPreviewMode ); + } + else + { + // This case there is no layer with mask, so we will render the full board as mask + + if( m_ogl_disp_list_through_holes_vias_outer ) + m_ogl_disp_list_through_holes_vias_outer->ApplyScalePosition( + aZPosition, + m_settings.GetNonCopperLayerThickness3DU() ); + + m_ogl_disp_list_board->ApplyScalePosition( + aZPosition, + m_settings.GetNonCopperLayerThickness3DU() ); + + set_layer_material( aLayerID ); + + m_ogl_disp_list_board->SetItIsTransparent( true ); + + m_ogl_disp_list_board->DrawAllCameraCulledSubtractLayer( + NULL, + m_ogl_disp_list_through_holes_vias_outer, + !aIsRenderingOnPreviewMode ); + } + } +} + + +void C3D_RENDER_OGL_LEGACY::render_3D_models( bool aRenderTopOrBot, + bool aRenderTransparentOnly ) +{ + // Go for all modules + if( m_settings.GetBoard()->m_Modules.GetCount() ) + { + for( const MODULE* module = m_settings.GetBoard()->m_Modules; + module; + module = module->Next() ) + { + if( !module->Models().empty() ) + if( m_settings.ShouldModuleBeDisplayed( (MODULE_ATTR_T)module->GetAttributes() ) ) + if( ( aRenderTopOrBot && !module->IsFlipped()) || + (!aRenderTopOrBot && module->IsFlipped()) ) + render_3D_module( module, aRenderTransparentOnly ); + } + } +} + + +void C3D_RENDER_OGL_LEGACY::render_3D_module( const MODULE* module, + bool aRenderTransparentOnly ) +{ + if( !module->Models().empty() ) + { + const double zpos = m_settings.GetModulesZcoord3DIU( module->IsFlipped() ); + + glPushMatrix(); + + wxPoint pos = module->GetPosition(); + + glTranslatef( pos.x * m_settings.BiuTo3Dunits(), + -pos.y * m_settings.BiuTo3Dunits(), + zpos ); + + if( module->GetOrientation() ) + glRotated( (double) module->GetOrientation() / 10.0, 0.0, 0.0, 1.0 ); + + if( module->IsFlipped() ) + { + glRotatef( 180.0f, 0.0f, 1.0f, 0.0f ); + glRotatef( 180.0f, 0.0f, 0.0f, 1.0f ); + } + + double modelunit_to_3d_units_factor = m_settings.BiuTo3Dunits() * UNITS3D_TO_UNITSPCB; + + glScaled( modelunit_to_3d_units_factor, + modelunit_to_3d_units_factor, + modelunit_to_3d_units_factor ); + + // Get the list of model files for this model + std::list::const_iterator sM = module->Models().begin(); + std::list::const_iterator eM = module->Models().end(); + + while( sM != eM ) + { + if( !sM->m_Filename.empty() ) + { + // Check if the model is present in our cache map + if( m_3dmodel_map.find( sM->m_Filename ) != m_3dmodel_map.end() ) + { + // It is not present, try get it from cache + const C_OGL_3DMODEL *modelPtr = m_3dmodel_map[ sM->m_Filename ]; + + if( modelPtr ) + { + if( ( (!aRenderTransparentOnly) && modelPtr->Have_opaque() ) || + ( aRenderTransparentOnly && modelPtr->Have_transparent() ) ) + { + glPushMatrix(); + + glTranslatef( sM->m_Offset.x * 25.4f, + sM->m_Offset.y * 25.4f, + sM->m_Offset.z * 25.4f ); + + glRotatef( -sM->m_Rotation.z, 0.0f, 0.0f, 1.0f ); + glRotatef( -sM->m_Rotation.y, 0.0f, 1.0f, 0.0f ); + glRotatef( -sM->m_Rotation.x, 1.0f, 0.0f, 0.0f ); + + glScalef( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ); + + if( aRenderTransparentOnly ) + modelPtr->Draw_transparent(); + else + modelPtr->Draw_opaque(); + + if( m_settings.GetFlag( FL_RENDER_OPENGL_SHOW_MODEL_BBOX ) ) + { + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + glLineWidth( 1 ); + modelPtr->Draw_bboxes(); + + glDisable( GL_LIGHTING ); + + glColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); + + glLineWidth( 4 ); + modelPtr->Draw_bbox(); + + glEnable( GL_LIGHTING ); + } + + glPopMatrix(); + } + } + } + } + + ++sM; + } + + glPopMatrix(); + } +} + + +// create a 3D grid to an openGL display list: an horizontal grid (XY plane and Z = 0, +// and a vertical grid (XZ plane and Y = 0) +void C3D_RENDER_OGL_LEGACY::generate_new_3DGrid( GRID3D_TYPE aGridType ) +{ + if( glIsList( m_ogl_disp_list_grid ) ) + glDeleteLists( m_ogl_disp_list_grid, 1 ); + + m_ogl_disp_list_grid = 0; + + if( aGridType == GRID3D_NONE ) + return; + + m_ogl_disp_list_grid = glGenLists( 1 ); + + if( !glIsList( m_ogl_disp_list_grid ) ) + return; + + glNewList( m_ogl_disp_list_grid, GL_COMPILE ); + + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + const double zpos = 0.0; + + // Color of grid lines + const SFVEC3F gridColor = m_settings.GetColor( DARKGRAY ); + + // Color of grid lines every 5 lines + const SFVEC3F gridColor_marker = m_settings.GetColor( LIGHTGRAY ); + const double scale = m_settings.BiuTo3Dunits(); + const double transparency = 0.35; + + double griSizeMM = 0.0; + + switch( aGridType ) + { + default: + case GRID3D_NONE: + return; + case GRID3D_1MM: + griSizeMM = 1.0; + break; + case GRID3D_2P5MM: + griSizeMM = 2.5; + break; + case GRID3D_5MM: + griSizeMM = 5.0; + break; + case GRID3D_10MM: + griSizeMM = 10.0; + break; + } + + glNormal3f( 0.0, 0.0, 1.0 ); + + const wxSize brd_size = m_settings.GetBoardSizeBIU(); + wxPoint brd_center_pos = m_settings.GetBoardPosBIU(); + + brd_center_pos.y = -brd_center_pos.y; + + const int xsize = std::max( brd_size.x, Millimeter2iu( 100 ) ) * 1.2; + const int ysize = std::max( brd_size.y, Millimeter2iu( 100 ) ) * 1.2; + + // Grid limits, in 3D units + double xmin = (brd_center_pos.x - xsize / 2) * scale; + double xmax = (brd_center_pos.x + xsize / 2) * scale; + double ymin = (brd_center_pos.y - ysize / 2) * scale; + double ymax = (brd_center_pos.y + ysize / 2) * scale; + double zmin = Millimeter2iu( -50 ) * scale; + double zmax = Millimeter2iu( 100 ) * scale; + + // Draw horizontal grid centered on 3D origin (center of the board) + for( int ii = 0; ; ii++ ) + { + if( (ii % 5) ) + glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency ); + else + glColor4f( gridColor_marker.r, + gridColor_marker.g, + gridColor_marker.b, + transparency ); + + const int delta = KiROUND( ii * griSizeMM * IU_PER_MM ); + + if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis + { + glBegin( GL_LINES ); + glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos ); + glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos ); + glEnd(); + + if( ii != 0 ) + { + glBegin( GL_LINES ); + glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos ); + glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos ); + glEnd(); + } + } + + if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis + { + glBegin( GL_LINES ); + glVertex3f( xmin, -(brd_center_pos.y + delta) * scale, zpos ); + glVertex3f( xmax, -(brd_center_pos.y + delta) * scale, zpos ); + glEnd(); + + if( ii != 0 ) + { + glBegin( GL_LINES ); + glVertex3f( xmin, -(brd_center_pos.y - delta) * scale, zpos ); + glVertex3f( xmax, -(brd_center_pos.y - delta) * scale, zpos ); + glEnd(); + } + } + + if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) ) + break; + } + + // Draw vertical grid on Z axis + glNormal3f( 0.0, -1.0, 0.0 ); + + // Draw vertical grid lines (parallel to Z axis) + double posy = -brd_center_pos.y * scale; + + for( int ii = 0; ; ii++ ) + { + if( (ii % 5) ) + glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency ); + else + glColor4f( gridColor_marker.r, + gridColor_marker.g, + gridColor_marker.b, + transparency ); + + const double delta = ii * griSizeMM * IU_PER_MM; + + glBegin( GL_LINES ); + xmax = (brd_center_pos.x + delta) * scale; + + glVertex3f( xmax, posy, zmin ); + glVertex3f( xmax, posy, zmax ); + glEnd(); + + if( ii != 0 ) + { + glBegin( GL_LINES ); + xmin = (brd_center_pos.x - delta) * scale; + glVertex3f( xmin, posy, zmin ); + glVertex3f( xmin, posy, zmax ); + glEnd(); + } + + if( delta > xsize / 2.0f ) + break; + } + + // Draw horizontal grid lines on Z axis (parallel to X axis) + for( int ii = 0; ; ii++ ) + { + if( (ii % 5) ) + glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency ); + else + glColor4f( gridColor_marker.r, + gridColor_marker.g, + gridColor_marker.b, + transparency ); + + const double delta = ii * griSizeMM * IU_PER_MM * scale; + + if( delta <= zmax ) + { + // Draw grid lines on Z axis (positive Z axis coordinates) + glBegin( GL_LINES ); + glVertex3f( xmin, posy, delta ); + glVertex3f( xmax, posy, delta ); + glEnd(); + } + + if( delta <= -zmin && ( ii != 0 ) ) + { + // Draw grid lines on Z axis (negative Z axis coordinates) + glBegin( GL_LINES ); + glVertex3f( xmin, posy, -delta ); + glVertex3f( xmax, posy, -delta ); + glEnd(); + } + + if( ( delta > zmax ) && ( delta > -zmin ) ) + break; + } + + glDisable( GL_BLEND ); + + glEndList(); } diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.h b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.h index 2bdd331548..1697efa316 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.h +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,13 +32,26 @@ #include "../c3d_render_base.h" #include "clayer_triangles.h" + +#include "../3d_render_raytracing/shapes2D/cpolygon2d.h" +#include "../3d_render_raytracing/shapes2D/ctriangle2d.h" +#include "../3d_render_raytracing/shapes2D/cpolygon4pts2d.h" +#include "../3d_render_raytracing/shapes2D/cfilledcircle2d.h" +#include "../3d_render_raytracing/shapes2D/cring2d.h" +#include "../3d_render_raytracing/shapes2D/croundsegment2d.h" + +#include "c_ogl_3dmodel.h" + +#include "3d_cache/3d_info.h" + #include typedef std::map< LAYER_ID, CLAYERS_OGL_DISP_LISTS* > MAP_OGL_DISP_LISTS; typedef std::map< LAYER_ID, CLAYER_TRIANGLES * > MAP_TRIANGLES; +typedef std::map< wxString, C_OGL_3DMODEL * > MAP_3DMODEL; -#define SIZE_OF_CIRCLE_TEXTURE 512 +#define SIZE_OF_CIRCLE_TEXTURE 1024 /** * @brief The C3D_RENDER_OGL_LEGACY class render the board using openGL legacy mode @@ -46,30 +59,157 @@ typedef std::map< LAYER_ID, CLAYER_TRIANGLES * > MAP_TRIANGLES; class C3D_RENDER_OGL_LEGACY : public C3D_RENDER_BASE { public: - C3D_RENDER_OGL_LEGACY( CINFO3D_VISU &aSettings, - S3D_CACHE *a3DModelManager ); + explicit C3D_RENDER_OGL_LEGACY( CINFO3D_VISU &aSettings ); ~C3D_RENDER_OGL_LEGACY(); // Imported from C3D_RENDER_BASE void SetCurWindowSize( const wxSize &aSize ); - void Redraw( bool aIsMoving ); + bool Redraw( bool aIsMoving, REPORTER *aStatusTextReporter ); + + int GetWaitForEditingTimeOut(); private: bool initializeOpenGL(); - void reload(); + void reload( REPORTER *aStatusTextReporter ); void ogl_set_arrow_material(); void ogl_free_all_display_lists(); MAP_OGL_DISP_LISTS m_ogl_disp_lists_layers; + MAP_OGL_DISP_LISTS m_ogl_disp_lists_layers_holes_outer; + MAP_OGL_DISP_LISTS m_ogl_disp_lists_layers_holes_inner; CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_board; + CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_through_holes_outer; + CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_through_holes_inner; + + // User for body render + CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_through_holes_outer_with_npth; + + CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_through_holes_vias_outer; + //CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_through_holes_vias_inner; // Not in use + + // This is for pads holes of the modules + //CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_vias_and_pad_holes_inner_contourn_and_caps; + CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_vias_and_pad_holes_outer_contourn_and_caps; + MAP_TRIANGLES m_triangles; GLuint m_ogl_circle_texture; + GLuint m_ogl_disp_list_grid; ///< oGL list that stores current grid + + GRID3D_TYPE m_last_grid_type; ///< Stores the last grid computed + + CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_via; + CLAYERS_OGL_DISP_LISTS* m_ogl_disp_list_pads_holes; + + MAP_3DMODEL m_3dmodel_map; + private: - void add_triangle_top_bot( CLAYER_TRIANGLES *aDst, const SFVEC2F &v0, const SFVEC2F &v1, const SFVEC2F &v2, float top, float bot ); + void generate_through_outer_holes(); + void generate_through_inner_holes(); + + CLAYERS_OGL_DISP_LISTS *generate_holes_display_list( const LIST_OBJECT2D &aListHolesObject2d, + const SHAPE_POLY_SET &aPoly, + float aZtop, + float aZbot, + bool aInvertFaces ); + + void add_triangle_top_bot( CLAYER_TRIANGLES *aDst, + const SFVEC2F &v0, + const SFVEC2F &v1, + const SFVEC2F &v2, + float top, + float bot ); + + void add_object_to_triangle_layer( const CRING2D *aRing, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ); + + void add_object_to_triangle_layer( const CPOLYGON4PTS2D *aPoly, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ); + + void add_object_to_triangle_layer( const CFILLEDCIRCLE2D *aFilledCircle, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ); + + void add_object_to_triangle_layer( const CTRIANGLE2D *aTri, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ); + + void add_object_to_triangle_layer( const CROUNDSEGMENT2D *aSeg, + CLAYER_TRIANGLES *aDstLayer, + float aZtop, + float aZbot ); + + void render_solder_mask_layer( LAYER_ID aLayerID, + float aZPosition, + bool aIsRenderingOnPreviewMode ); + + void get_layer_z_pos( LAYER_ID aLayerID, + float &aOutZtop, + float &aOutZbot ) const; + + void generate_ring_contour( const SFVEC2F &aCenter, + float aInnerRadius, + float aOuterRadius, + unsigned int aNr_sides_per_circle, + std::vector< SFVEC2F > &aInnerContourResult, + std::vector< SFVEC2F > &aOuterContourResult, + bool aInvertOrder ); + + void generate_cylinder( const SFVEC2F &aCenter, + float aInnerRadius, + float aOuterRadius, + float aZtop, + float aZbot, + unsigned int aNr_sides_per_circle, + CLAYER_TRIANGLES *aDstLayer ); + + void generate_3D_Vias_and_Pads(); + + void load_3D_models(); + + /** + * @brief render_3D_models + * @param aRenderTopOrBot - true will render Top, false will render bottom + * @param aRenderTransparentOnly - true will render only the transparent + * objects, false will render opaque + */ + void render_3D_models( bool aRenderTopOrBot, bool aRenderTransparentOnly ); + + void render_3D_module( const MODULE* module, bool aRenderTransparentOnly ); + + void setLight_Front( bool enabled ); + void setLight_Top( bool enabled ); + void setLight_Bottom( bool enabled ); + + void render_3D_arrows(); + + void generate_new_3DGrid( GRID3D_TYPE aGridType ); + + // Materials + void setupMaterials(); + + struct + { + SMATERIAL m_Paste; + SMATERIAL m_SilkS; + SMATERIAL m_SolderMask; + SMATERIAL m_EpoxyBoard; + SMATERIAL m_Copper; + SMATERIAL m_Plastic; + SMATERIAL m_GrayMaterial; + }m_materials; + + void set_layer_material( LAYER_ID aLayerID ); + SFVEC3F get_layer_color( LAYER_ID aLayerID ); public: const MAP_OGL_DISP_LISTS &GetLayerDispListMap() const { return m_ogl_disp_lists_layers; } @@ -78,3 +218,4 @@ public: }; #endif // C3D_RENDER_OGL_LEGACY_H_ + diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.cpp b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.cpp index 8f857a0d91..9215bfe5d6 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.cpp +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,14 +30,15 @@ #include "c_ogl_3dmodel.h" #include "ogl_legacy_utils.h" #include "../common_ogl/ogl_utils.h" +#include "../3d_math.h" #include -C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) +C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel, + MATERIAL_MODE aMaterialMode ) { m_ogl_idx_list_opaque = 0; m_ogl_idx_list_transparent = 0; - m_ogl_idx_list_meshes = 0; m_nr_meshes = 0; m_meshs_bbox = NULL; @@ -55,7 +56,7 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) m_meshs_bbox = new CBBOX[a3DModel.m_MeshesSize]; // Generate m_MeshesSize auxiliar lists to render the meshes - GLuint m_ogl_idx_list_meshes = glGenLists( a3DModel.m_MeshesSize ); + m_ogl_idx_list_meshes = glGenLists( a3DModel.m_MeshesSize ); // Render each mesh of the model // ///////////////////////////////////////////////////////////////////// @@ -75,14 +76,18 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) (mesh.m_FaceIdx != NULL) && (mesh.m_FaceIdxSize > 0) && (mesh.m_VertexSize > 0) ) { - SFVEC4F *pColorRGBA = NULL; + SFVEC4F *pColorRGBA = NULL; // Create the bbox for this mesh // ///////////////////////////////////////////////////////// m_meshs_bbox[mesh_i].Reset(); - for( unsigned int vertex_i = 0; vertex_i < mesh.m_VertexSize; ++vertex_i ) + for( unsigned int vertex_i = 0; + vertex_i < mesh.m_VertexSize; + ++vertex_i ) + { m_meshs_bbox[mesh_i].Union( mesh.m_Positions[vertex_i] ); + } // Make sure we start with client state disabled // ///////////////////////////////////////////////////////// @@ -107,22 +112,47 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) if( mesh.m_MaterialIdx < a3DModel.m_MaterialsSize ) transparency = a3DModel.m_Materials[mesh.m_MaterialIdx].m_Transparency; - if( transparency > FLT_EPSILON ) + if( (transparency > FLT_EPSILON) && + (aMaterialMode == MATERIAL_MODE_NORMAL) ) { // Create a new array of RGBA colors pColorRGBA = new SFVEC4F[mesh.m_VertexSize]; // Copy RGB array and add the Alpha value for( unsigned int i = 0; i < mesh.m_VertexSize; ++i ) - pColorRGBA[i] = SFVEC4F( mesh.m_Color[i], 1.0f - transparency ); + pColorRGBA[i] = SFVEC4F( mesh.m_Color[i], + 1.0f - transparency ); // Load an RGBA array glColorPointer( 4, GL_FLOAT, 0, pColorRGBA ); } else { - // Else load the original RGB color array - glColorPointer( 3, GL_FLOAT, 0, mesh.m_Color ); + switch( aMaterialMode ) + { + case MATERIAL_MODE_NORMAL: + case MATERIAL_MODE_DIFFUSE_ONLY: + // load the original RGB color array + glColorPointer( 3, GL_FLOAT, 0, mesh.m_Color ); + break; + case MATERIAL_MODE_CAD_MODE: + // Create a new array of RGBA colors + pColorRGBA = new SFVEC4F[mesh.m_VertexSize]; + + // Copy RGB array and add the Alpha value + for( unsigned int i = 0; i < mesh.m_VertexSize; ++i ) + { + pColorRGBA[i] = + SFVEC4F( MaterialDiffuseToColorCAD( mesh.m_Color[i] ), + 1.0f ); + } + + // Load an RGBA array + glColorPointer( 4, GL_FLOAT, 0, pColorRGBA ); + break; + default: + break; + } } } @@ -152,12 +182,29 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) if( mesh.m_MaterialIdx < a3DModel.m_MaterialsSize ) { - OGL_SetMaterial( a3DModel.m_Materials[mesh.m_MaterialIdx] ); + switch( aMaterialMode ) + { + case MATERIAL_MODE_NORMAL: + OGL_SetMaterial( a3DModel.m_Materials[mesh.m_MaterialIdx] ); + break; + case MATERIAL_MODE_DIFFUSE_ONLY: + OGL_SetDiffuseOnlyMaterial( + a3DModel.m_Materials[mesh.m_MaterialIdx].m_Diffuse ); + break; + case MATERIAL_MODE_CAD_MODE: + OGL_SetDiffuseOnlyMaterial( + MaterialDiffuseToColorCAD( + a3DModel.m_Materials[mesh.m_MaterialIdx].m_Diffuse ) ); + break; + default: + break; + } } // Draw mesh // ///////////////////////////////////////////////////////// - glDrawElements( GL_TRIANGLES, mesh.m_FaceIdxSize, GL_UNSIGNED_INT, mesh.m_FaceIdx ); + glDrawElements( GL_TRIANGLES, mesh.m_FaceIdxSize, + GL_UNSIGNED_INT, mesh.m_FaceIdx ); glDisable( GL_COLOR_MATERIAL ); @@ -170,19 +217,22 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) glDisableClientState( GL_NORMAL_ARRAY ); glDisableClientState( GL_VERTEX_ARRAY ); + glFinish(); + delete [] pColorRGBA; } } }// for each mesh - bool have_opaque_meshes = false; - bool have_transparent_meshes = false; m_ogl_idx_list_opaque = glGenLists( 1 ); // Check if the generated list is valid if( glIsList( m_ogl_idx_list_opaque ) ) { + bool have_opaque_meshes = false; + bool have_transparent_meshes = false; + // Compile the model display list glNewList( m_ogl_idx_list_opaque, GL_COMPILE ); @@ -198,12 +248,12 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) if( material.m_Transparency == 0.0f ) { - have_opaque_meshes = true; // Flag that we have at least one opaque mesh + have_opaque_meshes = true; // Flag that we have at least one opaque mesh glCallList( m_ogl_idx_list_meshes + mesh_i ); } else { - have_transparent_meshes = true; // Flag that we found a transparent mesh + have_transparent_meshes = true; // Flag that we found a transparent mesh } } } @@ -212,7 +262,8 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) if( !have_opaque_meshes ) { - glDeleteLists( m_ogl_idx_list_opaque, 1 ); // If we dont have opaque meshes, we can free the list + // If we dont have opaque meshes, we can free the list + glDeleteLists( m_ogl_idx_list_opaque, 1 ); m_ogl_idx_list_opaque = 0; } @@ -239,8 +290,9 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) { const SMATERIAL &material = a3DModel.m_Materials[mesh.m_MaterialIdx]; + // Render the transparent mesh if it have a transparency value if( material.m_Transparency != 0.0f ) - glCallList( m_ogl_idx_list_meshes + mesh_i ); // Render the transparent mesh + glCallList( m_ogl_idx_list_meshes + mesh_i ); } } @@ -265,6 +317,8 @@ C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel ) for( unsigned int mesh_i = 0; mesh_i < a3DModel.m_MeshesSize; ++mesh_i ) m_model_bbox.Union( m_meshs_bbox[mesh_i] ); + + glFinish(); } } diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.h b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.h index d042f63689..c5a2309d5d 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.h +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,19 +30,21 @@ #ifndef _C_OGL_3DMODEL_H_ #define _C_OGL_3DMODEL_H_ -#include "plugins/3dapi/c3dmodel.h" -#include "common_ogl/openGL_includes.h" -#include "3d_rendering/3d_render_raytracing/shapes3D/cbbox.h" +#include +#include "../../common_ogl/openGL_includes.h" +#include "../3d_render_raytracing/shapes3D/cbbox.h" +#include "../../3d_enums.h" -/// -class GLM_ALIGN(CLASS_ALIGNMENT) C_OGL_3DMODEL +/// +class C_OGL_3DMODEL { public: /** * @brief C_OGL_3DMODEL - Load a 3d model. This must be called inside a gl context * @param a3DModel: a 3d model data to load. + * @param aMaterialMode: a mode to render the materials of the model */ - C_OGL_3DMODEL( const S3DMODEL &a3DModel ); + C_OGL_3DMODEL( const S3DMODEL &a3DModel, MATERIAL_MODE aMaterialMode ); ~C_OGL_3DMODEL(); @@ -56,16 +58,6 @@ public: */ void Draw_transparent() const; - /** - * @brief Draw_bbox - draw main bounding box of the model - */ - void Draw_bbox() const; - - /** - * @brief Draw_bboxes - draw individual bounding boxes of each mesh - */ - void Draw_bboxes() const; - /** * @brief Have_opaque - return true if have opaque meshs to render */ @@ -76,6 +68,16 @@ public: */ bool Have_transparent() const; + /** + * @brief Draw_bbox - draw main bounding box of the model + */ + void Draw_bbox() const; + + /** + * @brief Draw_bboxes - draw individual bounding boxes of each mesh + */ + void Draw_bboxes() const; + /** * @brief GetBBox - Get main bbox * @return the main model bbox @@ -83,13 +85,13 @@ public: const CBBOX &GetBBox() const { return m_model_bbox; } private: - GLuint m_ogl_idx_list_opaque; ///< display list for rendering opaque meshes - GLuint m_ogl_idx_list_transparent; ///< display list for rendering transparent meshes - GLuint m_ogl_idx_list_meshes; ///< display lists for all meshes. - unsigned int m_nr_meshes; ///< number of meshes of this model + GLuint m_ogl_idx_list_opaque; ///< display list for rendering opaque meshes + GLuint m_ogl_idx_list_transparent; ///< display list for rendering transparent meshes + GLuint m_ogl_idx_list_meshes; ///< display lists for all meshes. + unsigned int m_nr_meshes; ///< number of meshes of this model - CBBOX m_model_bbox; ///< global bounding box for this model - CBBOX *m_meshs_bbox; ///< individual bbox for each mesh + CBBOX m_model_bbox; ///< global bounding box for this model + CBBOX *m_meshs_bbox; ///< individual bbox for each mesh }; #endif // _C_OGL_3DMODEL_H_ diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/clayer_triangles.cpp b/3d-viewer/3d_rendering/3d_render_ogl_legacy/clayer_triangles.cpp index 2b2e86d9c6..ddb28a2d6f 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/clayer_triangles.cpp +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/clayer_triangles.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,10 +29,11 @@ #include "clayer_triangles.h" -#include // For the wxASSERT +#include // For the wxASSERT -CLAYER_TRIANGLE_CONTAINER::CLAYER_TRIANGLE_CONTAINER(unsigned int aNrReservedTriangles, bool aReserveNormals) +CLAYER_TRIANGLE_CONTAINER::CLAYER_TRIANGLE_CONTAINER( unsigned int aNrReservedTriangles, + bool aReserveNormals ) { wxASSERT( aNrReservedTriangles > 0 ); @@ -46,7 +47,20 @@ CLAYER_TRIANGLE_CONTAINER::CLAYER_TRIANGLE_CONTAINER(unsigned int aNrReservedTri } -void CLAYER_TRIANGLE_CONTAINER::AddQuad( const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3, const SFVEC3F &aV4 ) +void CLAYER_TRIANGLE_CONTAINER::Reserve_More( unsigned int aNrReservedTriangles, + bool aReserveNormals ) +{ + m_vertexs.reserve( m_vertexs.size() + aNrReservedTriangles * 3 ); + + if( aReserveNormals ) + m_normals.reserve( m_normals.size() + aNrReservedTriangles * 3 ); +} + + +void CLAYER_TRIANGLE_CONTAINER::AddQuad( const SFVEC3F &aV1, + const SFVEC3F &aV2, + const SFVEC3F &aV3, + const SFVEC3F &aV4 ) { m_vertexs.push_back( aV1 ); m_vertexs.push_back( aV2 ); @@ -58,7 +72,9 @@ void CLAYER_TRIANGLE_CONTAINER::AddQuad( const SFVEC3F &aV1, const SFVEC3F &aV2, } -void CLAYER_TRIANGLE_CONTAINER::AddTriangle( const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3 ) +void CLAYER_TRIANGLE_CONTAINER::AddTriangle( const SFVEC3F &aV1, + const SFVEC3F &aV2, + const SFVEC3F &aV3 ) { m_vertexs.push_back( aV1 ); m_vertexs.push_back( aV2 ); @@ -66,14 +82,19 @@ void CLAYER_TRIANGLE_CONTAINER::AddTriangle( const SFVEC3F &aV1, const SFVEC3F & } -void CLAYER_TRIANGLE_CONTAINER::AddNormal( const SFVEC3F &aN1, const SFVEC3F &aN2, const SFVEC3F &aN3 ) +void CLAYER_TRIANGLE_CONTAINER::AddNormal( const SFVEC3F &aN1, + const SFVEC3F &aN2, + const SFVEC3F &aN3 ) { m_normals.push_back( aN1 ); m_normals.push_back( aN2 ); m_normals.push_back( aN3 ); } -void CLAYER_TRIANGLE_CONTAINER::AddNormal( const SFVEC3F &aN1, const SFVEC3F &aN2, const SFVEC3F &aN3, const SFVEC3F &aN4 ) +void CLAYER_TRIANGLE_CONTAINER::AddNormal( const SFVEC3F &aN1, + const SFVEC3F &aN2, + const SFVEC3F &aN3, + const SFVEC3F &aN4 ) { m_normals.push_back( aN1 ); m_normals.push_back( aN2 ); @@ -89,11 +110,16 @@ CLAYER_TRIANGLES::CLAYER_TRIANGLES( unsigned int aNrReservedTriangles ) { wxASSERT( aNrReservedTriangles > 0 ); - m_layer_top_segment_ends = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, false ); - m_layer_top_triangles = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, false ); - m_layer_middle_contourns_quads = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, true ); - m_layer_bot_triangles = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, false ); - m_layer_bot_segment_ends = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, false ); + m_layer_top_segment_ends = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, + false ); + m_layer_top_triangles = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, + false ); + m_layer_middle_contourns_quads = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, + true ); + m_layer_bot_triangles = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, + false ); + m_layer_bot_segment_ends = new CLAYER_TRIANGLE_CONTAINER( aNrReservedTriangles, + false ); } @@ -116,11 +142,194 @@ CLAYER_TRIANGLES::~CLAYER_TRIANGLES() } -CLAYERS_OGL_DISP_LISTS::CLAYERS_OGL_DISP_LISTS(const CLAYER_TRIANGLES &aLayerTriangles, - GLuint aTextureIndexForSegEnds, - const SFVEC3F& aLayerColor ) +void CLAYER_TRIANGLES::AddToMiddleContourns( const std::vector< SFVEC2F > &aContournPoints, + float zBot, + float zTop, + bool aInvertFaceDirection ) { - wxASSERT( glIsTexture( aTextureIndexForSegEnds ) ); + if( aContournPoints.size() > 4 ) + { + // Calculate normals of each segment of the contourn + std::vector< SFVEC2F > contournNormals; + + contournNormals.clear(); + contournNormals.resize( aContournPoints.size() - 1 ); + + if( aInvertFaceDirection ) + { + for( unsigned int i = 0; i < ( aContournPoints.size() - 1 ); ++i ) + { + const SFVEC2F &v0 = aContournPoints[i + 0]; + const SFVEC2F &v1 = aContournPoints[i + 1]; + + const SFVEC2F n = glm::normalize( v1 - v0 ); + + contournNormals[i] = SFVEC2F( n.y,-n.x ); + } + } + else + { + for( unsigned int i = 0; i < ( aContournPoints.size() - 1 ); ++i ) + { + const SFVEC2F &v0 = aContournPoints[i + 0]; + const SFVEC2F &v1 = aContournPoints[i + 1]; + + const SFVEC2F n = glm::normalize( v1 - v0 ); + + contournNormals[i] = SFVEC2F( -n.y, n.x ); + } + } + + + if( aInvertFaceDirection ) + std::swap( zBot, zTop ); + + const unsigned int nContournsToProcess = ( aContournPoints.size() - 1 ); + + for( unsigned int i = 0; i < nContournsToProcess; ++i ) + { + SFVEC2F lastNormal; + + if( i > 0 ) + lastNormal = contournNormals[i - 1]; + else + lastNormal = contournNormals[nContournsToProcess - 1]; + + SFVEC2F n0 = contournNormals[i]; + + // Only interpolate the normal if the angle is closer + if( glm::dot( n0, lastNormal ) > 0.5f ) + n0 = glm::normalize( n0 + lastNormal ); + + SFVEC2F nextNormal; + + if( i < (nContournsToProcess - 1) ) + nextNormal = contournNormals[i + 1]; + else + nextNormal = contournNormals[0]; + + SFVEC2F n1 = contournNormals[i]; + + if( glm::dot( n1, nextNormal ) > 0.5f ) + n1 = glm::normalize( n1 + nextNormal ); + + const SFVEC3F n3d0 = SFVEC3F( n0.x, n0.y, 0.0f ); + const SFVEC3F n3d1 = SFVEC3F( n1.x, n1.y, 0.0f ); + + const SFVEC2F &v0 = aContournPoints[i + 0]; + const SFVEC2F &v1 = aContournPoints[i + 1]; + + #pragma omp critical + { + m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, zTop ), + SFVEC3F( v1.x, v1.y, zTop ), + SFVEC3F( v1.x, v1.y, zBot ), + SFVEC3F( v0.x, v0.y, zBot ) ); + + m_layer_middle_contourns_quads->AddNormal( n3d0, n3d1, n3d1, n3d0 ); + } + } + } +} + + +void CLAYER_TRIANGLES::AddToMiddleContourns( const SHAPE_LINE_CHAIN &outlinePath, + float zBot, + float zTop, + double aBiuTo3Du, + bool aInvertFaceDirection ) +{ + std::vector< SFVEC2F >contournPoints; + + contournPoints.clear(); + contournPoints.reserve( outlinePath.PointCount() + 2 ); + + const VECTOR2I &firstV = outlinePath.CPoint( 0 ); + + SFVEC2F lastV = SFVEC2F( firstV.x * aBiuTo3Du, + -firstV.y * aBiuTo3Du ); + + contournPoints.push_back( lastV ); + + for( unsigned int i = 1; i < (unsigned int)outlinePath.PointCount(); ++i ) + { + const VECTOR2I & v = outlinePath.CPoint( i ); + + const SFVEC2F vf = SFVEC2F( v.x * aBiuTo3Du, + -v.y * aBiuTo3Du ); + + if( vf != lastV ) // Do not add repeated points + { + lastV = vf; + contournPoints.push_back( vf ); + } + } + + // Add first position fo the list to close the path + if( lastV != contournPoints[0] ) + contournPoints.push_back( contournPoints[0] ); + + AddToMiddleContourns( contournPoints, zBot, zTop, aInvertFaceDirection ); +} + + +void CLAYER_TRIANGLES::AddToMiddleContourns( const SHAPE_POLY_SET &aPolySet, + float zBot, + float zTop, + double aBiuTo3Du, + bool aInvertFaceDirection ) +{ + wxASSERT( aPolySet.OutlineCount() > 0 ); + + if( aPolySet.OutlineCount() == 0 ) + return; + + // Calculate an estimation of points to reserve + unsigned int nrContournPointsToReserve = 0; + + for( int i = 0; i < aPolySet.OutlineCount(); ++i ) + { + const SHAPE_LINE_CHAIN& pathOutline = aPolySet.COutline( i ); + + nrContournPointsToReserve += pathOutline.PointCount(); + + for( int h = 0; h < aPolySet.HoleCount( i ); ++h ) + { + const SHAPE_LINE_CHAIN &hole = aPolySet.CHole( i, h ); + + nrContournPointsToReserve += hole.PointCount(); + } + } + + // Request to reserve more space + m_layer_middle_contourns_quads->Reserve_More( nrContournPointsToReserve * 2, + true ); + + #pragma omp parallel for + for( signed int i = 0; i < aPolySet.OutlineCount(); ++i ) + { + // Add outline + const SHAPE_LINE_CHAIN& pathOutline = aPolySet.COutline( i ); + + AddToMiddleContourns( pathOutline, zBot, zTop, aBiuTo3Du, aInvertFaceDirection ); + + // Add holes for this outline + for( int h = 0; h < aPolySet.HoleCount( i ); ++h ) + { + const SHAPE_LINE_CHAIN &hole = aPolySet.CHole( i, h ); + AddToMiddleContourns( hole, zBot, zTop, aBiuTo3Du, aInvertFaceDirection ); + } + } +} + + +CLAYERS_OGL_DISP_LISTS::CLAYERS_OGL_DISP_LISTS( const CLAYER_TRIANGLES &aLayerTriangles, + GLuint aTextureIndexForSegEnds, + float aZBot, + float aZTop ) +{ + m_zBot = aZBot; + m_zTop = aZTop; m_layer_top_segment_ends = 0; m_layer_top_triangles = 0; @@ -128,12 +337,41 @@ CLAYERS_OGL_DISP_LISTS::CLAYERS_OGL_DISP_LISTS(const CLAYER_TRIANGLES &aLayerTri m_layer_bot_triangles = 0; m_layer_bot_segment_ends = 0; - m_layer_top_segment_ends = generate_top_or_bot_seg_ends( aLayerTriangles.m_layer_top_segment_ends, aLayerColor, true, aTextureIndexForSegEnds ); - m_layer_top_triangles = generate_top_or_bot_triangles( aLayerTriangles.m_layer_top_triangles, aLayerColor, true ); - m_layer_bot_triangles = generate_top_or_bot_triangles( aLayerTriangles.m_layer_bot_triangles, aLayerColor, false ); - m_layer_bot_segment_ends = generate_top_or_bot_seg_ends( aLayerTriangles.m_layer_bot_segment_ends, aLayerColor, false, aTextureIndexForSegEnds ); + if( aTextureIndexForSegEnds ) + { + wxASSERT( glIsTexture( aTextureIndexForSegEnds ) ); - m_layer_middle_contourns_quads = generate_middle_triangles( aLayerTriangles.m_layer_middle_contourns_quads, aLayerColor ); + if( glIsTexture( aTextureIndexForSegEnds ) ) + { + m_layer_top_segment_ends = + generate_top_or_bot_seg_ends( aLayerTriangles.m_layer_top_segment_ends, + true, + aTextureIndexForSegEnds ); + + m_layer_bot_segment_ends = + generate_top_or_bot_seg_ends( aLayerTriangles.m_layer_bot_segment_ends, + false, + aTextureIndexForSegEnds ); + } + } + + m_layer_top_triangles = generate_top_or_bot_triangles( aLayerTriangles.m_layer_top_triangles, + true ); + + m_layer_bot_triangles = generate_top_or_bot_triangles( aLayerTriangles.m_layer_bot_triangles, + false ); + + + if( aLayerTriangles.m_layer_middle_contourns_quads->GetVertexSize() > 0 ) + { + m_layer_middle_contourns_quads = + generate_middle_triangles( aLayerTriangles.m_layer_middle_contourns_quads ); + } + + m_draw_it_transparent = false; + m_haveTransformation = false; + m_zPositionTransformation = 0.0f; + m_zScaleTransformation = 0.0f; } @@ -153,73 +391,99 @@ CLAYERS_OGL_DISP_LISTS::~CLAYERS_OGL_DISP_LISTS() if( glIsList( m_layer_bot_segment_ends ) ) glDeleteLists( m_layer_bot_segment_ends, 1 ); + + m_layer_top_segment_ends = 0; + m_layer_top_triangles = 0; + m_layer_middle_contourns_quads = 0; + m_layer_bot_triangles = 0; + m_layer_bot_segment_ends = 0; } void CLAYERS_OGL_DISP_LISTS::DrawTopAndMiddle() const { - if( glIsList( m_layer_top_triangles ) ) - glCallList( m_layer_top_triangles ); + beginTransformation(); if( glIsList( m_layer_middle_contourns_quads ) ) glCallList( m_layer_middle_contourns_quads ); + if( glIsList( m_layer_top_triangles ) ) + glCallList( m_layer_top_triangles ); + if( glIsList( m_layer_top_segment_ends ) ) glCallList( m_layer_top_segment_ends ); + endTransformation(); } void CLAYERS_OGL_DISP_LISTS::DrawBotAndMiddle() const { - if( glIsList( m_layer_bot_triangles ) ) - glCallList( m_layer_bot_triangles ); + beginTransformation(); if( glIsList( m_layer_middle_contourns_quads ) ) glCallList( m_layer_middle_contourns_quads ); + if( glIsList( m_layer_bot_triangles ) ) + glCallList( m_layer_bot_triangles ); + if( glIsList( m_layer_bot_segment_ends ) ) glCallList( m_layer_bot_segment_ends ); + endTransformation(); } void CLAYERS_OGL_DISP_LISTS::DrawTop() const { + beginTransformation(); + if( glIsList( m_layer_top_triangles ) ) glCallList( m_layer_top_triangles ); if( glIsList( m_layer_top_segment_ends ) ) glCallList( m_layer_top_segment_ends ); + + endTransformation(); } void CLAYERS_OGL_DISP_LISTS::DrawBot() const { + beginTransformation(); + if( glIsList( m_layer_bot_triangles ) ) glCallList( m_layer_bot_triangles ); if( glIsList( m_layer_bot_segment_ends ) ) glCallList( m_layer_bot_segment_ends ); + + endTransformation(); } void CLAYERS_OGL_DISP_LISTS::DrawMiddle() const { + beginTransformation(); + if( glIsList( m_layer_middle_contourns_quads ) ) glCallList( m_layer_middle_contourns_quads ); + + endTransformation(); } -void CLAYERS_OGL_DISP_LISTS::DrawAll() const +void CLAYERS_OGL_DISP_LISTS::DrawAll( bool aDrawMiddle ) const { + beginTransformation(); + + if( aDrawMiddle ) + if( glIsList( m_layer_middle_contourns_quads ) ) + glCallList( m_layer_middle_contourns_quads ); if( glIsList( m_layer_top_triangles ) ) glCallList( m_layer_top_triangles ); - if( glIsList( m_layer_middle_contourns_quads ) ) - glCallList( m_layer_middle_contourns_quads ); - if( glIsList( m_layer_bot_triangles ) ) glCallList( m_layer_bot_triangles ); @@ -229,15 +493,162 @@ void CLAYERS_OGL_DISP_LISTS::DrawAll() const if( glIsList( m_layer_bot_segment_ends ) ) glCallList( m_layer_bot_segment_ends ); + endTransformation(); } -GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_seg_ends(const CLAYER_TRIANGLE_CONTAINER *aTriangleContainer, const SFVEC3F &aLayerColor, bool aIsNormalUp, GLuint aTextureId ) const +void CLAYERS_OGL_DISP_LISTS::DrawAllCameraCulled(float zCameraPos, bool aDrawMiddle ) const +{ + zCameraPos = m_haveTransformation?( (zCameraPos - m_zPositionTransformation ) / + m_zScaleTransformation ):zCameraPos; + + if( aDrawMiddle ) + DrawMiddle(); + + if( zCameraPos > m_zTop ) + { + DrawTop(); + } + else + { + if( zCameraPos < m_zBot ) + { + DrawBot(); + } + else + { + // If camera is in the middle dont draw it + } + } +} + + +void CLAYERS_OGL_DISP_LISTS::DrawAllCameraCulledSubtractLayer( + const CLAYERS_OGL_DISP_LISTS *aLayerToSubtractA, + const CLAYERS_OGL_DISP_LISTS *aLayerToSubtractB, + bool aDrawMiddle ) const +{ + if( aDrawMiddle ) + DrawMiddle(); + + glClearStencil( 0x00 ); + glClear( GL_STENCIL_BUFFER_BIT ); + + glEnable( GL_CULL_FACE ); + glCullFace( GL_BACK ); + + glDisable( GL_DEPTH_TEST ); + glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); + glDepthMask( GL_FALSE ); + glEnable( GL_STENCIL_TEST ); + glStencilFunc( GL_ALWAYS, 1, 0 ); + glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE ); + + if( aLayerToSubtractA ) + aLayerToSubtractA->DrawBot(); + + if( aLayerToSubtractB ) + aLayerToSubtractB->DrawBot(); + + + //if( !m_draw_it_transparent ) + { + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + } + + glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + glStencilFunc( GL_EQUAL, 0, 1 ); + glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); + DrawBot(); + + glDisable( GL_DEPTH_TEST ); + glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); + glDepthMask( GL_FALSE ); + glEnable( GL_STENCIL_TEST ); + glStencilFunc( GL_ALWAYS, 2, 0 ); + glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE ); + + if( aLayerToSubtractA ) + aLayerToSubtractA->DrawTop(); + + if( aLayerToSubtractB ) + aLayerToSubtractB->DrawTop(); + + //if( !m_draw_it_transparent ) + { + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + } + + glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + glStencilFunc( GL_NOTEQUAL, 2, 0x03 ); + glStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); + DrawTop(); + + + glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE ); + + glCullFace( GL_FRONT ); + glStencilFunc( GL_GEQUAL, 3, 0x03 ); + glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); + glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + + if( aDrawMiddle ) + { + if( aLayerToSubtractA ) + aLayerToSubtractA->DrawMiddle(); + + // It will not render the middle contours of the layer. + // It is used with vias and holes (copper vias and to subtract solder + // mask holes). But since in the vias, it will draw a cylinder + // and in soldermask it doesn't need to draw the contour. + // so it is not used the middle part of B +// if( aLayerToSubtractB ) +// aLayerToSubtractB->DrawMiddle(); + } + + glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE ); + + glCullFace( GL_BACK ); + glDisable( GL_STENCIL_TEST ); + +/* + if( m_draw_it_transparent ) + { + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + }*/ +} + + +void CLAYERS_OGL_DISP_LISTS::ApplyScalePosition( float aZposition, + float aZscale ) +{ + wxASSERT( aZscale > FLT_EPSILON ); + + m_zPositionTransformation = aZposition; + m_zScaleTransformation = aZscale; + m_haveTransformation = true; +} + + +void CLAYERS_OGL_DISP_LISTS::SetItIsTransparent( bool aSetTransparent ) +{ + m_draw_it_transparent = aSetTransparent; +} + + +GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_seg_ends( + const CLAYER_TRIANGLE_CONTAINER *aTriangleContainer, + bool aIsNormalUp, + GLuint aTextureId ) const { wxASSERT( aTriangleContainer != NULL ); wxASSERT( (aTriangleContainer->GetVertexSize() % 3) == 0 ); + // Top and Bot dont have normals array stored in container wxASSERT( aTriangleContainer->GetNormalsSize() == 0 ); if( (aTriangleContainer->GetVertexSize() > 0) && @@ -250,7 +661,9 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_seg_ends(const CLAYER_TRIANGL // Prepare an array of UV text coordinates SFVEC2F *uvArray = new SFVEC2F[aTriangleContainer->GetVertexSize()]; - for( unsigned int i = 0; i < aTriangleContainer->GetVertexSize(); i += 3 ) + for( unsigned int i = 0; + i < aTriangleContainer->GetVertexSize(); + i += 3 ) { uvArray[i + 0] = SFVEC2F( 1.0f, 0.0f ); uvArray[i + 1] = SFVEC2F( 0.0f, 1.0f ); @@ -266,22 +679,16 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_seg_ends(const CLAYER_TRIANGL glNewList( listIdx, GL_COMPILE ); + glDisable( GL_COLOR_MATERIAL ); + glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, aTextureId ); - glAlphaFunc( GL_GREATER, 0.60f ); + setBlendfunction(); + + glAlphaFunc( GL_GREATER, 0.2f ); glEnable( GL_ALPHA_TEST ); - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - - //SFVEC4F layerColor4 = SFVEC4F( aLayerColor.x, aLayerColor.y, aLayerColor.z, 1.0f ); - //glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, &layerColor4.x ); - //glDisable( GL_COLOR_MATERIAL ); - glEnable( GL_COLOR_MATERIAL ); - glColor4f( aLayerColor.x, aLayerColor.y, aLayerColor.z, 1.0f ); - - // OGL_SetMaterial() glNormal3f( 0.0f, 0.0f, aIsNormalUp?1.0f:-1.0f ); glDrawArrays( GL_TRIANGLES, 0, aTriangleContainer->GetVertexSize() ); @@ -289,7 +696,6 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_seg_ends(const CLAYER_TRIANGL glDisable( GL_TEXTURE_2D ); glDisable( GL_ALPHA_TEST ); glDisable( GL_BLEND ); - glDisable( GL_COLOR_MATERIAL ); glEndList(); @@ -305,18 +711,21 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_seg_ends(const CLAYER_TRIANGL } -GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_triangles(const CLAYER_TRIANGLE_CONTAINER *aTriangleContainer, const SFVEC3F &aLayerColor , bool aIsNormalUp) const +GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_triangles( + const CLAYER_TRIANGLE_CONTAINER *aTriangleContainer, + bool aIsNormalUp ) const { wxASSERT( aTriangleContainer != NULL ); wxASSERT( (aTriangleContainer->GetVertexSize() % 3) == 0 ); + // Top and Bot dont have normals array stored in container wxASSERT( aTriangleContainer->GetNormalsSize() == 0 ); if( (aTriangleContainer->GetVertexSize() > 0) && - ((aTriangleContainer->GetVertexSize() % 3) == 0) ) + ( (aTriangleContainer->GetVertexSize() % 3) == 0) ) { - GLuint listIdx = glGenLists( 1 ); + const GLuint listIdx = glGenLists( 1 ); if( glIsList( listIdx ) ) { @@ -328,35 +737,13 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_triangles(const CLAYER_TRIANG glNewList( listIdx, GL_COMPILE ); - //SFVEC4F layerColor4 = SFVEC4F( aLayerColor.x, aLayerColor.y, aLayerColor.z, 1.0f ); - //glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, &layerColor4.x ); - glEnable( GL_COLOR_MATERIAL ); - glColor4f( aLayerColor.x, aLayerColor.y, aLayerColor.z, 1.0f ); + setBlendfunction(); - // OGL_SetMaterial() glNormal3f( 0.0f, 0.0f, aIsNormalUp?1.0f:-1.0f ); glDrawArrays( GL_TRIANGLES, 0, aTriangleContainer->GetVertexSize() ); -/* - glEnable( GL_COLOR_MATERIAL ); - glColor3f( 0.0,1.0,1.0); - for( unsigned int i=0; i < aTriangleContainer->GetVertexSize(); ++i ) - { - const SFVEC3F &v1 = aTriangleContainer->GetVertexPointer()[ i * 3]; - glBegin(GL_LINES); - glVertex3f( v1.x, v1.y, v1.z ); - if( aIsNormalUp ) - { - glVertex3f( v1.x, v1.y, v1.z+1.0f*.0051f ); - } - else - { - glVertex3f( v1.x, v1.y, v1.z-1.0f*.0051f ); - } - glEnd(); - } -*/ + glDisable( GL_BLEND ); glEndList(); glDisableClientState( GL_VERTEX_ARRAY ); @@ -368,7 +755,9 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_top_or_bot_triangles(const CLAYER_TRIANG return 0; } -GLuint CLAYERS_OGL_DISP_LISTS::generate_middle_triangles( const CLAYER_TRIANGLE_CONTAINER *aTriangleContainer, const SFVEC3F &aLayerColor ) const + +GLuint CLAYERS_OGL_DISP_LISTS::generate_middle_triangles( + const CLAYER_TRIANGLE_CONTAINER *aTriangleContainer ) const { wxASSERT( aTriangleContainer != NULL ); @@ -387,7 +776,7 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_middle_triangles( const CLAYER_TRIANGLE_ ( (aTriangleContainer->GetVertexSize() % 6) == 0 ) && ( aTriangleContainer->GetNormalsSize() == aTriangleContainer->GetVertexSize() ) ) { - GLuint listIdx = glGenLists( 1 ); + const GLuint listIdx = glGenLists( 1 ); if( glIsList( listIdx ) ) { @@ -399,34 +788,12 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_middle_triangles( const CLAYER_TRIANGLE_ glNormalPointer( GL_FLOAT, 0, aTriangleContainer->GetNormalsPointer() ); glNewList( listIdx, GL_COMPILE ); -/* - const SFVEC4F specular = SFVEC4F( 0.5f, 0.5f, 0.5f, 1.0f ); - glMaterialfv( GL_FRONT, GL_SPECULAR, &specular.r ); - glMaterialf( GL_FRONT, GL_SHININESS, 10.0f );*/ - - //SFVEC4F layerColor4 = SFVEC4F( aLayerColor.x, aLayerColor.y, aLayerColor.z, 1.0f ); - //glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, &layerColor4.x ); - //glDisable( GL_COLOR_MATERIAL ); - glEnable( GL_COLOR_MATERIAL ); - glColor4f( aLayerColor.x, aLayerColor.y, aLayerColor.z, 1.0f ); - - // OGL_SetMaterial() + setBlendfunction(); glDrawArrays( GL_TRIANGLES, 0, aTriangleContainer->GetVertexSize() ); -/* - glEnable( GL_COLOR_MATERIAL ); - glColor3f( 1.0,0.0,1.0); - for( unsigned int i=0; i < aTriangleContainer->GetVertexSize() / 3 ; ++i ) - { - const SFVEC3F &v1 = ((const SFVEC3F*)aTriangleContainer->GetVertexPointer())[i * 3]; - const SFVEC3F &n1 = ((const SFVEC3F*)aTriangleContainer->GetNormalsPointer())[i * 3]; - glBegin(GL_LINES); - glVertex3f( v1.x, v1.y, v1.z ); - glVertex3f( v1.x+n1.x*.01f, v1.y+n1.y*.01f, v1.z+n1.z*.01f ); - glEnd(); - } -*/ + + glDisable( GL_BLEND ); glEndList(); glDisableClientState( GL_VERTEX_ARRAY ); @@ -439,3 +806,29 @@ GLuint CLAYERS_OGL_DISP_LISTS::generate_middle_triangles( const CLAYER_TRIANGLE_ return 0; } + +void CLAYERS_OGL_DISP_LISTS::endTransformation() const +{ + if( m_haveTransformation ) + { + glPopMatrix(); + } +} + + +void CLAYERS_OGL_DISP_LISTS::setBlendfunction() const +{ + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); +} + + +void CLAYERS_OGL_DISP_LISTS::beginTransformation() const +{ + if( m_haveTransformation ) + { + glPushMatrix(); + glTranslatef( 0.0f, 0.0f, m_zPositionTransformation ); + glScalef( 1.0f, 1.0f, m_zScaleTransformation ); + } +} diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/clayer_triangles.h b/3d-viewer/3d_rendering/3d_render_ogl_legacy/clayer_triangles.h index c230bc044e..4a4f08c126 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/clayer_triangles.h +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/clayer_triangles.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,8 +30,10 @@ #ifndef CLAYER_TRIANGLES_H_ #define CLAYER_TRIANGLES_H_ -#include "common_ogl/openGL_includes.h" -#include "plugins/3dapi/xv3d_types.h" +#include "../../common_ogl/openGL_includes.h" +#include +#include +#include #include @@ -49,10 +51,17 @@ public: /** * @brief CLAYER_TRIANGLE_CONTAINER * @param aNrReservedTriangles: number of triangles expected to be used - * @param aReserveNormals: if you will use normals, set it to bool to pre reserve space + * @param aReserveNormals: if you will use normals, set it to bool to pre + * reserve space */ CLAYER_TRIANGLE_CONTAINER( unsigned int aNrReservedTriangles, bool aReserveNormals ); + /** + * @brief Reserve_More - reserve more triangles + * + */ + void Reserve_More( unsigned int aNrReservedTriangles, bool aReserveNormals ); + /** * @brief AddTriangle * @param aV1 @@ -68,7 +77,10 @@ public: * @param aV3 * @param aV4 */ - void AddQuad( const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3, const SFVEC3F &aV4 ); + void AddQuad( const SFVEC3F &aV1, + const SFVEC3F &aV2, + const SFVEC3F &aV3, + const SFVEC3F &aV4 ); /** * @brief AddNormal @@ -84,7 +96,10 @@ public: * @param aN2 * @param aN3 */ - void AddNormal( const SFVEC3F &aN1, const SFVEC3F &aN2, const SFVEC3F &aN3, const SFVEC3F &aN4 ); + void AddNormal( const SFVEC3F &aN1, + const SFVEC3F &aN2, + const SFVEC3F &aN3, + const SFVEC3F &aN4 ); /** * @brief GetVertexPointer - Get the array of vertexes @@ -102,17 +117,17 @@ public: * @brief GetVertexSize * @return */ - unsigned int GetVertexSize() const { return m_vertexs.size(); } + unsigned int GetVertexSize() const { return (unsigned int)m_vertexs.size(); } /** * @brief GetNormalsSize * @return */ - unsigned int GetNormalsSize() const { return m_normals.size(); } + unsigned int GetNormalsSize() const { return (unsigned int)m_normals.size(); } private: - SFVEC3F_VECTOR m_vertexs; ///< vertex array - SFVEC3F_VECTOR m_normals; ///< normals array + SFVEC3F_VECTOR m_vertexs; ///< vertex array + SFVEC3F_VECTOR m_normals; ///< normals array }; @@ -127,7 +142,7 @@ public: * @brief CLAYER_TRIANGLES - initialize arrays with reserved triangles * @param aNrReservedTriangles: number of pre alloc triangles to reserve */ - CLAYER_TRIANGLES( unsigned int aNrReservedTriangles ); + explicit CLAYER_TRIANGLES( unsigned int aNrReservedTriangles ); /** * @brief ~CLAYER_TRIANGLES - Free containers @@ -135,11 +150,30 @@ public: ~CLAYER_TRIANGLES(); /** - * @brief IsLayersSizeValid - check if the vertex arrays of the layers are as expected + * @brief IsLayersSizeValid - check if the vertex arrays of the layers are + * as expected * @return TRUE if layers are correctly setup */ bool IsLayersSizeValid(); + + void AddToMiddleContourns( const SHAPE_LINE_CHAIN &outlinePath, + float zBot, + float zTop, + double aBiuTo3Du, + bool aInvertFaceDirection ); + + void AddToMiddleContourns( const SHAPE_POLY_SET &aPolySet, + float zBot, + float zTop, + double aBiuTo3Du, + bool aInvertFaceDirection ); + + void AddToMiddleContourns( const std::vector< SFVEC2F > &aContournPoints, + float zBot, + float zTop, + bool aInvertFaceDirection ); + CLAYER_TRIANGLE_CONTAINER *m_layer_top_segment_ends; CLAYER_TRIANGLE_CONTAINER *m_layer_top_triangles; CLAYER_TRIANGLE_CONTAINER *m_layer_middle_contourns_quads; @@ -157,25 +191,33 @@ class CLAYERS_OGL_DISP_LISTS public: /** * @brief CLAYERS_OGL_DISP_LISTS - Creates the display lists for a layer - * @param aLayerTriangles: contains the layers array of vertex to render to display lists - * @param aTextureIndexForSegEnds: texture index to be used by segment ends. It is a black and white squared texture with a center circle diameter of the size of the texture. + * @param aLayerTriangles: contains the layers array of vertex to render to + * display lists + * @param aTextureIndexForSegEnds: texture index to be used by segment ends. + * It is a black and white squared texture + * with a center circle diameter of the size + * of the texture. */ CLAYERS_OGL_DISP_LISTS( const CLAYER_TRIANGLES &aLayerTriangles, GLuint aTextureIndexForSegEnds, - const SFVEC3F& aLayerColor ); + float aZBot, + float aZTop ); /** - * @brief ~CLAYERS_OGL_DISP_LISTS - Destroy this class while free the display lists from GPU mem + * @brief ~CLAYERS_OGL_DISP_LISTS - Destroy this class while free the display + * lists from GPU mem */ ~CLAYERS_OGL_DISP_LISTS(); /** - * @brief DrawTopAndMiddle - This function calls the display lists for the top elements and middle contourns + * @brief DrawTopAndMiddle - This function calls the display lists for the + * top elements and middle contourns */ void DrawTopAndMiddle() const; /** - * @brief DrawBotAndMiddle - This function calls the display lists for the botton elements and middle contourns + * @brief DrawBotAndMiddle - This function calls the display lists for the + * botton elements and middle contourns */ void DrawBotAndMiddle() const; @@ -190,26 +232,66 @@ public: void DrawBot() const; /** - * @brief DrawMiddle - This function calls the display lists for the middle elements + * @brief DrawMiddle - This function calls the display lists for the middle + * elements */ void DrawMiddle() const; /** * @brief DrawAll - This function calls all the display lists */ - void DrawAll() const; + void DrawAll( bool aDrawMiddle = true ) const; + + /** + * @brief DrawAllCameraCulled - Draw all layers if they are visible by the camera. + * i.e.: if camera position is above the layer. This only works because the + * board is centered and the planes are always perpendicular to the Z axis. + * @param zCameraPos: camera z position + */ + void DrawAllCameraCulled( float zCameraPos, bool aDrawMiddle = true ) const; + + void DrawAllCameraCulledSubtractLayer( const CLAYERS_OGL_DISP_LISTS *aLayerToSubtractA, + const CLAYERS_OGL_DISP_LISTS *aLayerToSubtractB, + bool aDrawMiddle = true ) const; + + void ApplyScalePosition( float aZposition, float aZscale ); + + void ClearScalePosition() { m_haveTransformation = false; } + + void SetItIsTransparent( bool aSetTransparent ); + + float GetZBot() const { return m_zBot; } + float GetZTop() const { return m_zTop; } private: - GLuint generate_top_or_bot_seg_ends(const CLAYER_TRIANGLE_CONTAINER * aTriangleContainer, const SFVEC3F& aLayerColor, bool aIsNormalUp , GLuint aTextureId ) const; - GLuint generate_top_or_bot_triangles( const CLAYER_TRIANGLE_CONTAINER * aTriangleContainer, const SFVEC3F& aLayerColor, bool aIsNormalUp ) const; - GLuint generate_middle_triangles( const CLAYER_TRIANGLE_CONTAINER * aTriangleContainer, const SFVEC3F& aLayerColor ) const; + GLuint generate_top_or_bot_seg_ends( const CLAYER_TRIANGLE_CONTAINER * aTriangleContainer, + bool aIsNormalUp, + GLuint aTextureId ) const; + + GLuint generate_top_or_bot_triangles( const CLAYER_TRIANGLE_CONTAINER * aTriangleContainer, + bool aIsNormalUp ) const; + + GLuint generate_middle_triangles( const CLAYER_TRIANGLE_CONTAINER * aTriangleContainer ) const; + + void beginTransformation() const; + void endTransformation() const; + + void setBlendfunction() const; private: - GLuint m_layer_top_segment_ends; - GLuint m_layer_top_triangles; - GLuint m_layer_middle_contourns_quads; - GLuint m_layer_bot_triangles; - GLuint m_layer_bot_segment_ends; + float m_zBot; + float m_zTop; + GLuint m_layer_top_segment_ends; + GLuint m_layer_top_triangles; + GLuint m_layer_middle_contourns_quads; + GLuint m_layer_bot_triangles; + GLuint m_layer_bot_segment_ends; + + bool m_haveTransformation; + float m_zPositionTransformation; + float m_zScaleTransformation; + + bool m_draw_it_transparent; }; #endif // CLAYER_TRIANGLES_H_ diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.cpp b/3d-viewer/3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.cpp index 6c53f3765b..ce221c3083 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.cpp +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,10 +28,10 @@ */ -#include "common_ogl/openGL_includes.h" +#include "../../common_ogl/openGL_includes.h" #include "ogl_legacy_utils.h" #include -#include // For the wxASSERT +#include // For the wxASSERT #define RADPERDEG 0.0174533 @@ -50,8 +50,12 @@ void OGL_draw_arrow( SFVEC3F aPosition, SFVEC3F aTargetPos, float aSize ) if( ( vec.x != 0.0f ) || ( vec.y != 0.0f ) ) { - glRotatef( atan2(vec.y, vec.x) / RADPERDEG, 0.0f, 0.0f, 1.0f ); - glRotatef( atan2(sqrt(vec.x * vec.x + vec.y * vec.y), vec.z) / RADPERDEG, 0.0f, 1.0f, 0.0f ); + glRotatef( atan2( vec.y, vec.x ) / RADPERDEG, 0.0f, 0.0f, 1.0f ); + glRotatef( atan2( sqrt( vec.x * vec.x + vec.y * vec.y ), vec.z ) / RADPERDEG, + 0.0f, + 1.0f, + 0.0f ); + } else if( vec.z < 0.0f ) { glRotatef( 180.0f, 1.0f, 0.0f, 0.0f ); @@ -87,8 +91,8 @@ void OGL_draw_arrow( SFVEC3F aPosition, SFVEC3F aTargetPos, float aSize ) glTranslatef( 0.0f , 0.0f ,-length + 4.0f * aSize ); quadObj = gluNewQuadric(); - gluQuadricDrawStyle(quadObj, GLU_FILL ); - gluQuadricNormals(quadObj, GLU_SMOOTH ); + gluQuadricDrawStyle( quadObj, GLU_FILL ); + gluQuadricNormals( quadObj, GLU_SMOOTH ); gluCylinder( quadObj, aSize, aSize, length - 4.0 * aSize, 12, 1 ); gluDeleteQuadric( quadObj ); /* @@ -154,45 +158,53 @@ void OGL_draw_half_open_cylinder( unsigned int aNrSidesPerCircle ) { if( aNrSidesPerCircle > 1 ) { - float radius = 0.5f; - int delta = 3600 / aNrSidesPerCircle; + const float radius = 0.5f; + const int delta = 3600 / aNrSidesPerCircle; // Generate bottom glNormal3f( 0.0f, 0.0f,-1.0f ); glBegin( GL_TRIANGLE_FAN ); - glVertex3f( 0.0, 0.0, 0.0 ); // This is the V0 of the FAN + glVertex3f( 0.0, 0.0, 0.0 ); // This is the V0 of the FAN + for( int ii = 0; ii < 1800; ii += delta ) { SFVEC2D corner = SFVEC2D( 0.0, radius ); RotatePoint( &corner.x, &corner.y, ii ); glVertex3f( corner.x, corner.y, 0.0 ); } + glVertex3d( 0.0, -radius, 0.0 ); glEnd(); // Generate top glNormal3f( 0.0f, 0.0f, 1.0f ); glBegin( GL_TRIANGLE_FAN ); - glVertex3f( 0.0, 0.0, 1.0 ); // This is the V0 of the FAN + glVertex3f( 0.0, 0.0, 1.0 ); // This is the V0 of the FAN + for( int ii = 1800; ii > 0; ii -= delta ) { SFVEC2D corner = SFVEC2D( 0.0, radius ); + RotatePoint( &corner.x, &corner.y, ii ); glVertex3f( corner.x, corner.y, 1.0 ); } + glVertex3f( 0.0, radius, 1.0 ); glEnd(); // Generate contours glBegin( GL_QUAD_STRIP ); + for( int ii = 1800; ii > 0; ii -= delta ) { SFVEC2D corner = SFVEC2D( 0.0, radius ); + RotatePoint( &corner.x, &corner.y, ii ); glNormal3f( corner.x * 2.0f, corner.y * 2.0f, 0.0f ); glVertex3f( corner.x, corner.y, 1.0 ); glVertex3f( corner.x, corner.y, 0.0 ); } + glNormal3f( 0.0, 1.0f, 0.0f ); glVertex3d( 0.0, radius, 1.0 ); glVertex3d( 0.0, radius, 0.0 ); @@ -201,7 +213,8 @@ void OGL_draw_half_open_cylinder( unsigned int aNrSidesPerCircle ) } -void OGL_Draw_segment( const CROUNDSEGMENT2D &aSegment, unsigned int aNrSidesPerCircle ) +void OGL_Draw_segment( const CROUNDSEGMENT2D &aSegment, + unsigned int aNrSidesPerCircle ) { glPushMatrix(); @@ -215,14 +228,17 @@ void OGL_Draw_segment( const CROUNDSEGMENT2D &aSegment, unsigned int aNrSidesPer if( ( end_minus_start.x != 0.0f ) || ( end_minus_start.y != 0.0f ) ) { - glRotatef( atan2(end_minus_start.y, end_minus_start.x) / RADPERDEG, 0.0f, 0.0f, 1.0f ); + glRotatef( atan2( end_minus_start.y, end_minus_start.x ) / RADPERDEG, + 0.0f, + 0.0f, + 1.0f ); } glPushMatrix(); glTranslatef( length, 0.0, 0.0f ); glScalef( width, width, 1.0f ); OGL_draw_half_open_cylinder( aNrSidesPerCircle ); - glPopMatrix (); + glPopMatrix(); glBegin( GL_QUADS ); glNormal3f( 0.0,-1.0, 0.0 ); diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h b/3d-viewer/3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h index 8cb0a1232e..7d1a4c8c06 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,9 +30,8 @@ #ifndef OGL_LEGACY_UTILS_H_ #define OGL_LEGACY_UTILS_H_ -#include "plugins/3dapi/xv3d_types.h" -#include "3d_render_raytracing/shapes3D/cbbox.h" -#include "3d_render_raytracing/shapes2D/croundsegment2d.h" +#include "../3d_render_raytracing/shapes3D/cbbox.h" +#include "../3d_render_raytracing/shapes2D/croundsegment2d.h" /** * @brief OGL_draw_arrow - draw a round arrow @@ -62,6 +61,7 @@ void OGL_draw_half_open_cylinder( unsigned int aNrSidesPerCircle ); * @brief OGL_Draw_segment * @param aSegment */ -void OGL_Draw_segment( const CROUNDSEGMENT2D &aSegment, unsigned int aNrSidesPerCircle ); +void OGL_Draw_segment( const CROUNDSEGMENT2D &aSegment, + unsigned int aNrSidesPerCircle ); #endif // OGL_LEGACY_UTILS_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/caccelerator.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/caccelerator.cpp new file mode 100644 index 0000000000..9bf7ab1095 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/caccelerator.cpp @@ -0,0 +1,40 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file caccelerator.cpp + * @brief + */ + +#include "caccelerator.h" + + +CGENERICACCELERATOR::CGENERICACCELERATOR( ) +{ + m_bbox.Reset(); +} + +CGENERICACCELERATOR::~CGENERICACCELERATOR() +{ +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/caccelerator.h b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/caccelerator.h new file mode 100644 index 0000000000..a5e631032a --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/caccelerator.h @@ -0,0 +1,58 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file caccelerator.h + * @brief + */ + +#ifndef _CACCELERATOR_H_ +#define _CACCELERATOR_H_ + +#include "ccontainer.h" +#include "../raypacket.h" + + +class CGENERICACCELERATOR +{ +protected: + CBBOX m_bbox; + +public: + CGENERICACCELERATOR( ); + virtual ~CGENERICACCELERATOR(); + + virtual bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const = 0; + + virtual bool Intersect( const RAY &aRay, + HITINFO &aHitInfo, + unsigned int aAccNodeInfo ) const = 0; + + virtual bool Intersect( const RAYPACKET &aRayPacket, + HITINFO_PACKET *aHitInfoPacket ) const = 0; + + virtual bool IntersectP( const RAY &aRay, float aMaxDistance ) const = 0; +}; + +#endif // _CACCELERATOR_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_packet_traversal.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_packet_traversal.cpp new file mode 100644 index 0000000000..65e7b82c55 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_packet_traversal.cpp @@ -0,0 +1,295 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cbvh_packet_traversal.cpp + * @brief This file implementes packet traversal over the BVH PBRT implementation + */ + +#include "cbvh_pbrt.h" +#include + + +#define BVH_RANGED_TRAVERSAL +//#define BVH_PARTITION_TRAVERSAL + + +#define MAX_TODOS 64 + + +struct StackNode +{ + int cell; + unsigned int ia; // Index to the first alive ray +}; + + +static inline unsigned int getFirstHit( const RAYPACKET &aRayPacket, + const CBBOX &aBBox, + unsigned int ia, + HITINFO_PACKET *aHitInfoPacket ) +{ + float hitT; + + if( aBBox.Intersect( aRayPacket.m_ray[ia], &hitT ) ) + if( hitT < aHitInfoPacket[ia].m_HitInfo.m_tHit ) + return ia; + + if( !aRayPacket.m_Frustum.Intersect( aBBox ) ) + return RAYPACKET_RAYS_PER_PACKET; + + for( unsigned int i = ia + 1; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + { + if( aBBox.Intersect( aRayPacket.m_ray[i], &hitT ) ) + if( hitT < aHitInfoPacket[i].m_HitInfo.m_tHit ) + return i; + } + + return RAYPACKET_RAYS_PER_PACKET; +} + + +#ifdef BVH_RANGED_TRAVERSAL + +static inline unsigned int getLastHit( const RAYPACKET &aRayPacket, + const CBBOX &aBBox, + unsigned int ia, + HITINFO_PACKET *aHitInfoPacket ) +{ + for( unsigned int ie = (RAYPACKET_RAYS_PER_PACKET - 1); ie > ia; --ie ) + { + float hitT; + + if( aBBox.Intersect( aRayPacket.m_ray[ie], &hitT ) ) + if( hitT < aHitInfoPacket[ie].m_HitInfo.m_tHit ) + return ie + 1; + } + + return ia + 1; +} + + +// "Large Ray Packets for Real-time Whitted Ray Tracing" +// http://cseweb.ucsd.edu/~ravir/whitted.pdf + +// Ranged Traversal +bool CBVH_PBRT::Intersect( const RAYPACKET &aRayPacket, + HITINFO_PACKET *aHitInfoPacket ) const +{ + if( m_nodes == NULL ) + return false; + + if( (&m_nodes[0]) == NULL ) + return false; + + bool anyHitted = false; + int todoOffset = 0, nodeNum = 0; + StackNode todo[MAX_TODOS]; + + unsigned int ia = 0; + + while( true ) + { + const LinearBVHNode *curCell = &m_nodes[nodeNum]; + + ia = getFirstHit( aRayPacket, curCell->bounds, ia, aHitInfoPacket ); + + if( ia < RAYPACKET_RAYS_PER_PACKET ) + { + if( curCell->nPrimitives == 0 ) + { + StackNode &node = todo[todoOffset++]; + node.cell = curCell->secondChildOffset; + node.ia = ia; + nodeNum = nodeNum + 1; + continue; + } + else + { + const unsigned int ie = getLastHit( aRayPacket, + curCell->bounds, + ia, + aHitInfoPacket ); + + for( int j = 0; j < curCell->nPrimitives; ++j ) + { + const COBJECT *obj = m_primitives[curCell->primitivesOffset + j]; + + if( aRayPacket.m_Frustum.Intersect( obj->GetBBox() ) ) + { + for( unsigned int i = ia; i < ie; ++i ) + { + const bool hitted = obj->Intersect( aRayPacket.m_ray[i], + aHitInfoPacket[i].m_HitInfo ); + + if( hitted ) + { + anyHitted |= hitted; + aHitInfoPacket[i].m_hitresult |= hitted; + aHitInfoPacket[i].m_HitInfo.m_acc_node_info = nodeNum; + } + } + } + } + } + } + + if( todoOffset == 0 ) + break; + + const StackNode &node = todo[--todoOffset]; + + nodeNum = node.cell; + ia = node.ia; + } + + return anyHitted; + +}// Ranged Traversal +#endif + + +// "Ray Tracing Deformable Scenes Using Dynamic Bounding Volume Hierarchies" +// http://www.cs.cmu.edu/afs/cs/academic/class/15869-f11/www/readings/wald07_packetbvh.pdf + +#ifdef BVH_PARTITION_TRAVERSAL + +static inline unsigned int getLastHit( const RAYPACKET &aRayPacket, + const CBBOX &aBBox, + unsigned int ia, + const unsigned int *aRayIndex, + HITINFO_PACKET *aHitInfoPacket ) +{ + for( unsigned int ie = (RAYPACKET_RAYS_PER_PACKET - 1); ie > ia; --ie ) + { + float hitT; + + if( aBBox.Intersect( aRayPacket.m_ray[ aRayIndex[ie] ], &hitT ) ) + if( hitT < aHitInfoPacket[ aRayIndex[ie] ].m_HitInfo.m_tHit ) + return ie + 1; + } + + return ia + 1; +} + + +static inline unsigned int partRays( const RAYPACKET &aRayPacket, + const CBBOX &aBBox, + unsigned int ia, + unsigned int *aRayIndex, + HITINFO_PACKET *aHitInfoPacket ) +{ + + if( !aRayPacket.m_Frustum.Intersect( aBBox ) ) + return RAYPACKET_RAYS_PER_PACKET; + + unsigned int ie = 0; + + for( unsigned int i = 0; i < ia; ++i ) + { + float hitT; + if( aBBox.Intersect( aRayPacket.m_ray[ aRayIndex[i] ], &hitT ) ) + if( hitT < aHitInfoPacket[ aRayIndex[i] ].m_HitInfo.m_tHit ) + std::swap( aRayIndex[ie++], aRayIndex[i] ); + } + + return ie; +} + + +bool CBVH_PBRT::Intersect( const RAYPACKET &aRayPacket, + HITINFO_PACKET *aHitInfoPacket ) const +{ + bool anyHitted = false; + int todoOffset = 0, nodeNum = 0; + StackNode todo[MAX_TODOS]; + + unsigned int I[RAYPACKET_RAYS_PER_PACKET]; + + memcpy( I, m_I, RAYPACKET_RAYS_PER_PACKET * sizeof( unsigned int ) ); + + unsigned int ia = 0; + + while( true ) + { + const LinearBVHNode *curCell = &m_nodes[nodeNum]; + + ia = partRays( aRayPacket, curCell->bounds, ia, I, aHitInfoPacket ); + + if( ia < RAYPACKET_RAYS_PER_PACKET ) + { + if( curCell->nPrimitives == 0 ) + { + StackNode &node = todo[todoOffset++]; + node.cell = curCell->secondChildOffset; + node.ia = ia; + nodeNum = nodeNum + 1; + continue; + } + else + { + unsigned int ie = getLastHit( aRayPacket, + curCell->bounds, + ia, + I, + aHitInfoPacket ); + + for( int j = 0; j < curCell->nPrimitives; ++j ) + { + const COBJECT *obj = m_primitives[curCell->primitivesOffset + j]; + + if( aRayPacket.m_Frustum.Intersect( obj->GetBBox() ) ) + { + for( unsigned int i = 0; i < ie; ++i ) + { + unsigned int idx = I[i]; + + bool hitted = obj->Intersect( + aRayPacket.m_ray[idx], + aHitInfoPacket[idx].m_HitInfo ); + + if( hitted ) + { + anyHitted |= hitted; + aHitInfoPacket[idx].m_hitresult |= hitted; + aHitInfoPacket[idx].m_HitInfo.m_acc_node_info = nodeNum; + } + } + } + } + } + } + + if( todoOffset == 0 ) + break; + + const StackNode &node = todo[--todoOffset]; + + nodeNum = node.cell; + ia = node.ia; + } + + return anyHitted; +}// Partition Traversal +#endif diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_pbrt.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_pbrt.cpp new file mode 100644 index 0000000000..257e843e66 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_pbrt.cpp @@ -0,0 +1,1325 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cbvh_pbrt.cpp + * @brief This BVH implementation is based on the source code implementation + * from the book "Physically Based Rendering" (v2 and v3) + * + * Adaptions performed for kicad: + * - Types and class types adapted to KiCad project + * - Convert some source to build in the C++ specification of KiCad + * - Code style to match KiCad + * - Asserts converted + * - Use compare functions/structures for std::partition and std::nth_element + * + * The original source code has the following licence: + * + * "pbrt source code is Copyright(c) 1998-2015 + * Matt Pharr, Greg Humphreys, and Wenzel Jakob. + * + * This file is part of pbrt. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * 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." + * + */ + +#include "cbvh_pbrt.h" +#include "../../../3d_fastmath.h" +#include +#include +#include +//#include +#include + +#include +#include + +#ifdef PRINT_STATISTICS_3D_VIEWER +#include +#endif + +// BVHAccel Local Declarations +struct BVHPrimitiveInfo +{ + BVHPrimitiveInfo() + { + primitiveNumber = 0; + bounds.Reset(); + centroid = SFVEC3F( 0.0f ); + } + + BVHPrimitiveInfo( int aPrimitiveNumber, const CBBOX &aBounds ) : + primitiveNumber( aPrimitiveNumber ), + bounds( aBounds ), + centroid( .5f * aBounds.Min() + .5f * aBounds.Max() ) {} + + int primitiveNumber; + CBBOX bounds; + SFVEC3F centroid; +}; + + +struct BVHBuildNode +{ + // BVHBuildNode Public Methods + void InitLeaf( int first, int n, const CBBOX &b) + { + firstPrimOffset = first; + nPrimitives = n; + bounds = b; + children[0] = children[1] = NULL; + } + + void InitInterior( int axis, BVHBuildNode *c0, BVHBuildNode *c1 ) + { + children[0] = c0; + children[1] = c1; + bounds.Set( c0->bounds ); + bounds.Union( c1->bounds ); + splitAxis = axis; + nPrimitives = 0; + } + + CBBOX bounds; + BVHBuildNode *children[2]; + int splitAxis, firstPrimOffset, nPrimitives; +}; + + +struct MortonPrimitive +{ + int primitiveIndex; + uint32_t mortonCode; +}; + + +struct LBVHTreelet +{ + int startIndex, numPrimitives; + BVHBuildNode *buildNodes; +}; + + +// BVHAccel Utility Functions +inline uint32_t LeftShift3( uint32_t x ) +{ + wxASSERT( x <= (1 << 10) ); + + if( x == (1 << 10) ) + --x; + + x = (x | (x << 16)) & 0b00000011000000000000000011111111; + // x = ---- --98 ---- ---- ---- ---- 7654 3210 + x = (x | (x << 8)) & 0b00000011000000001111000000001111; + // x = ---- --98 ---- ---- 7654 ---- ---- 3210 + x = (x | (x << 4)) & 0b00000011000011000011000011000011; + // x = ---- --98 ---- 76-- --54 ---- 32-- --10 + x = (x | (x << 2)) & 0b00001001001001001001001001001001; + // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 + + return x; +} + + +inline uint32_t EncodeMorton3( const SFVEC3F &v ) +{ + wxASSERT( v.x >= 0 && v.x <= (1 << 10) ); + wxASSERT( v.y >= 0 && v.y <= (1 << 10) ); + wxASSERT( v.z >= 0 && v.z <= (1 << 10) ); + + return (LeftShift3(v.z) << 2) | (LeftShift3(v.y) << 1) | LeftShift3(v.x); +} + + +static void RadixSort( std::vector *v ) +{ + std::vector tempVector( v->size() ); + + const int bitsPerPass = 6; + const int nBits = 30; + + wxASSERT( (nBits % bitsPerPass) == 0 ); + + const int nPasses = nBits / bitsPerPass; + + for( int pass = 0; pass < nPasses; ++pass ) + { + // Perform one pass of radix sort, sorting _bitsPerPass_ bits + const int lowBit = pass * bitsPerPass; + + // Set in and out vector pointers for radix sort pass + std::vector &in = (pass & 1) ? tempVector : *v; + std::vector &out = (pass & 1) ? *v : tempVector; + + // Count number of zero bits in array for current radix sort bit + const int nBuckets = 1 << bitsPerPass; + int bucketCount[nBuckets] = {0}; + const int bitMask = (1 << bitsPerPass) - 1; + + for( uint32_t i = 0; i < in.size(); ++i ) + { + const MortonPrimitive &mp = in[i]; + int bucket = (mp.mortonCode >> lowBit) & bitMask; + + wxASSERT( (bucket >= 0) && (bucket < nBuckets) ); + + ++bucketCount[bucket]; + } + + // Compute starting index in output array for each bucket + int startIndex[nBuckets]; + startIndex[0] = 0; + + for( int i = 1; i < nBuckets; ++i ) + startIndex[i] = startIndex[i - 1] + bucketCount[i - 1]; + + // Store sorted values in output array + for( uint32_t i = 0; i < in.size(); ++i ) + { + const MortonPrimitive &mp = in[i]; + int bucket = (mp.mortonCode >> lowBit) & bitMask; + out[startIndex[bucket]++] = mp; + } + } + + // Copy final result from _tempVector_, if needed + if( nPasses & 1 ) + std::swap( *v, tempVector ); +} + + +CBVH_PBRT::CBVH_PBRT( const CGENERICCONTAINER &aObjectContainer, + int aMaxPrimsInNode, + SPLITMETHOD aSplitMethod ) : + m_maxPrimsInNode( std::min( 255, aMaxPrimsInNode ) ), + m_splitMethod( aSplitMethod ) +{ + if( aObjectContainer.GetList().empty() ) + { + m_nodes = NULL; + + return; + } + + // Initialize the indexes of ray packet for partition traversal + for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + { + m_I[i] = i; + } + + // Convert the objects list to vector of objects + // ///////////////////////////////////////////////////////////////////////// + aObjectContainer.ConvertTo( m_primitives ); + + wxASSERT( aObjectContainer.GetList().size() == m_primitives.size() ); + + // Initialize _primitiveInfo_ array for primitives + // ///////////////////////////////////////////////////////////////////////// + std::vector primitiveInfo( m_primitives.size() ); + + for( size_t i = 0; i < m_primitives.size(); ++i ) + { + wxASSERT( m_primitives[i]->GetBBox().IsInitialized() ); + + primitiveInfo[i] = BVHPrimitiveInfo( i, m_primitives[i]->GetBBox() ); + } + + // Build BVH tree for primitives using _primitiveInfo_ + int totalNodes = 0; + + CONST_VECTOR_OBJECT orderedPrims; + orderedPrims.clear(); + orderedPrims.reserve( m_primitives.size() ); + + BVHBuildNode *root; + + if( m_splitMethod == SPLIT_HLBVH ) + root = HLBVHBuild( primitiveInfo, &totalNodes, orderedPrims); + else + root = recursiveBuild( primitiveInfo, 0, m_primitives.size(), + &totalNodes, orderedPrims); + + wxASSERT( m_primitives.size() == orderedPrims.size() ); + + m_primitives.swap( orderedPrims ); + + // Compute representation of depth-first traversal of BVH tree + m_nodes = static_cast( _mm_malloc( sizeof( LinearBVHNode ) * + totalNodes, + L1_CACHE_LINE_SIZE ) ); + m_addresses_pointer_to_mm_free.push_back( m_nodes ); + + for( int i = 0; i < totalNodes; ++i ) + { + m_nodes[i].bounds.Reset(); + m_nodes[i].primitivesOffset = 0; + m_nodes[i].nPrimitives = 0; + m_nodes[i].axis = 0; + } + + uint32_t offset = 0; + + flattenBVHTree( root, &offset ); + + wxASSERT( offset == (unsigned int)totalNodes ); + +#ifdef PRINT_STATISTICS_3D_VIEWER + uint32_t treeBytes = totalNodes * sizeof( LinearBVHNode ) + sizeof( *this ) + + m_primitives.size() * sizeof( m_primitives[0] ) + + m_addresses_pointer_to_mm_free.size() * sizeof( void * ); + + printf( "////////////////////////////////////////////////////////////////////////////////\n" ); + printf( "Creating a CBVH_PBRT from %u objects ", (unsigned int)m_primitives.size() ); + + switch( m_splitMethod ) + { + case SPLIT_MIDDLE: printf( "using SPLIT_MIDDLE\n" ); break; + case SPLIT_EQUALCOUNTS: printf( "using SPLIT_EQUALCOUNTS\n" ); break; + case SPLIT_SAH: printf( "using SPLIT_SAH\n" ); break; + case SPLIT_HLBVH: printf( "using SPLIT_HLBVH\n" ); break; + } + + printf( " BVH created with %d nodes (%.2f MB)\n", + totalNodes, float(treeBytes) / (1024.f * 1024.f) ); + printf( "////////////////////////////////////////////////////////////////////////////////\n\n" ); +#endif +} + + +CBVH_PBRT::~CBVH_PBRT() +{ + if( !m_addresses_pointer_to_mm_free.empty() ) + { + for( std::list::iterator ii = m_addresses_pointer_to_mm_free.begin(); + ii != m_addresses_pointer_to_mm_free.end(); + ++ii ) + { + _mm_free( *ii ); + *ii = NULL; + } + } + + m_addresses_pointer_to_mm_free.clear(); +} + + +struct ComparePoints +{ + explicit ComparePoints(int d) { dim = d; } + + int dim; + + bool operator()( const BVHPrimitiveInfo &a, + const BVHPrimitiveInfo &b ) const + { + return a.centroid[dim] < b.centroid[dim]; + } +}; + + +struct CompareToMid +{ + explicit CompareToMid( int d, float m ) { dim = d; mid = m; } + + int dim; + float mid; + + bool operator()( const BVHPrimitiveInfo &a ) const + { + return a.centroid[dim] < mid; + } +}; + + +struct CompareToBucket +{ + CompareToBucket( int split, int num, int d, const CBBOX &b ) + : centroidBounds(b) + { splitBucket = split; nBuckets = num; dim = d; } + + bool operator()(const BVHPrimitiveInfo &p) const; + + int splitBucket, nBuckets, dim; + + const CBBOX ¢roidBounds; +}; + + +bool CompareToBucket::operator()( const BVHPrimitiveInfo &p ) const +{ + const float centroid = p.centroid[dim]; + + int b = nBuckets * + // Computes the offset (0.0 - 1.0) for one axis + ( ( centroid - centroidBounds.Min()[dim] ) / + ( centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) ); + + if( b == nBuckets ) + b = nBuckets - 1; + + wxASSERT( (b >= 0) && (b < nBuckets) ); + + return b <= splitBucket; +} + + +struct HLBVH_SAH_Evaluator +{ + HLBVH_SAH_Evaluator( int split, int num, int d, const CBBOX &b ) + : centroidBounds(b) + { minCostSplitBucket = split; nBuckets = num; dim = d; } + + bool operator()(const BVHBuildNode *node) const; + + int minCostSplitBucket, nBuckets, dim; + const CBBOX ¢roidBounds; +}; + + +bool HLBVH_SAH_Evaluator::operator()( const BVHBuildNode *node ) const +{ + const float centroid = node->bounds.GetCenter( dim ); + + int b = nBuckets * + // Computes the offset (0.0 - 1.0) for one axis + ( ( centroid - centroidBounds.Min()[dim] ) / + ( centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) ); + + if( b == nBuckets ) + b = nBuckets - 1; + + wxASSERT( b >= 0 && b < nBuckets ); + + return b <= minCostSplitBucket; +} + + +struct BucketInfo +{ + int count; + CBBOX bounds; +}; + + +BVHBuildNode *CBVH_PBRT::recursiveBuild ( std::vector &primitiveInfo, + int start, + int end, + int *totalNodes, + CONST_VECTOR_OBJECT &orderedPrims ) +{ + wxASSERT( totalNodes != NULL ); + wxASSERT( start >= 0 ); + wxASSERT( end >= 0 ); + wxASSERT( start != end ); + wxASSERT( start < end ); + wxASSERT( start <= (int)primitiveInfo.size() ); + wxASSERT( end <= (int)primitiveInfo.size() ); + + (*totalNodes)++; + + // !TODO: implement an memory Arena + BVHBuildNode *node = static_cast( _mm_malloc( sizeof( BVHBuildNode ), + L1_CACHE_LINE_SIZE ) ); + m_addresses_pointer_to_mm_free.push_back( node ); + + node->bounds.Reset(); + node->firstPrimOffset = 0; + node->nPrimitives = 0; + node->splitAxis = 0; + node->children[0] = NULL; + node->children[1] = NULL; + + // Compute bounds of all primitives in BVH node + CBBOX bounds; + bounds.Reset(); + + for( int i = start; i < end; ++i ) + bounds.Union( primitiveInfo[i].bounds ); + + int nPrimitives = end - start; + + if( nPrimitives == 1 ) + { + // Create leaf _BVHBuildNode_ + int firstPrimOffset = orderedPrims.size(); + + for( int i = start; i < end; ++i ) + { + int primitiveNr = primitiveInfo[i].primitiveNumber; + wxASSERT( primitiveNr < (int)m_primitives.size() ); + orderedPrims.push_back( m_primitives[ primitiveNr ] ); + } + + node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); + } + else + { + // Compute bound of primitive centroids, choose split dimension _dim_ + CBBOX centroidBounds; + centroidBounds.Reset(); + + for( int i = start; i < end; ++i ) + centroidBounds.Union( primitiveInfo[i].centroid ); + + const int dim = centroidBounds.MaxDimension(); + + // Partition primitives into two sets and build children + int mid = (start + end) / 2; + + if( fabs( centroidBounds.Max()[dim] - + centroidBounds.Min()[dim] ) < (FLT_EPSILON + FLT_EPSILON) ) + { + // Create leaf _BVHBuildNode_ + const int firstPrimOffset = orderedPrims.size(); + + for( int i = start; i < end; ++i ) + { + int primitiveNr = primitiveInfo[i].primitiveNumber; + + wxASSERT( (primitiveNr >= 0) && + (primitiveNr < (int)m_primitives.size()) ); + + const COBJECT *obj = static_cast( m_primitives[ primitiveNr ] ); + + wxASSERT( obj != NULL ); + + orderedPrims.push_back( obj ); + } + + node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); + } + else + { + // Partition primitives based on _splitMethod_ + switch( m_splitMethod ) + { + case SPLIT_MIDDLE: + { + // Partition primitives through node's midpoint + float pmid = centroidBounds.GetCenter( dim ); + + BVHPrimitiveInfo *midPtr = std::partition( &primitiveInfo[start], + &primitiveInfo[end - 1] + 1, + CompareToMid( dim, pmid ) ); + mid = midPtr - &primitiveInfo[0]; + + wxASSERT( (mid >= start) && + (mid <= end) ); + + if( (mid != start) && (mid != end) ) + // for lots of prims with large overlapping bounding boxes, this + // may fail to partition; in that case don't break and fall through + // to SPLIT_EQUAL_COUNTS + break; + } + + case SPLIT_EQUALCOUNTS: + { + // Partition primitives into equally-sized subsets + mid = (start + end) / 2; + + std::nth_element( &primitiveInfo[start], + &primitiveInfo[mid], + &primitiveInfo[end - 1] + 1, + ComparePoints( dim ) ); + + break; + } + + case SPLIT_SAH: + default: + { + // Partition primitives using approximate SAH + if( nPrimitives <= 2 ) + { + // Partition primitives into equally-sized subsets + mid = (start + end) / 2; + + std::nth_element( &primitiveInfo[start], + &primitiveInfo[mid], + &primitiveInfo[end - 1] + 1, + ComparePoints( dim ) ); + } + else + { + // Allocate _BucketInfo_ for SAH partition buckets + const int nBuckets = 12; + + BucketInfo buckets[nBuckets]; + + for( int i = 0; i < nBuckets; ++i ) + { + buckets[i].count = 0; + buckets[i].bounds.Reset(); + } + + // Initialize _BucketInfo_ for SAH partition buckets + for( int i = start; i < end; ++i ) + { + int b = nBuckets * + centroidBounds.Offset( primitiveInfo[i].centroid )[dim]; + + if( b == nBuckets ) + b = nBuckets - 1; + + wxASSERT( b >= 0 && b < nBuckets ); + + buckets[b].count++; + buckets[b].bounds.Union( primitiveInfo[i].bounds ); + } + + // Compute costs for splitting after each bucket + float cost[nBuckets - 1]; + + for( int i = 0; i < (nBuckets - 1); ++i ) + { + CBBOX b0, b1; + + b0.Reset(); + b1.Reset(); + + int count0 = 0; + int count1 = 0; + + for( int j = 0; j <= i; ++j ) + { + if( buckets[j].count ) + { + count0 += buckets[j].count; + b0.Union( buckets[j].bounds ); + } + } + + for( int j = i + 1; j < nBuckets; ++j ) + { + if( buckets[j].count ) + { + count1 += buckets[j].count; + b1.Union( buckets[j].bounds ); + } + } + + cost[i] = 1.0f + + ( count0 * b0.SurfaceArea() + + count1 * b1.SurfaceArea() ) / + bounds.SurfaceArea(); + } + + // Find bucket to split at that minimizes SAH metric + float minCost = cost[0]; + int minCostSplitBucket = 0; + + for( int i = 1; i < (nBuckets - 1); ++i ) + { + if( cost[i] < minCost ) + { + minCost = cost[i]; + minCostSplitBucket = i; + } + } + + // Either create leaf or split primitives at selected SAH + // bucket + if( (nPrimitives > m_maxPrimsInNode) || + (minCost < (float)nPrimitives) ) + { + BVHPrimitiveInfo *pmid = + std::partition( &primitiveInfo[start], + &primitiveInfo[end - 1] + 1, + CompareToBucket( minCostSplitBucket, + nBuckets, + dim, + centroidBounds ) ); + mid = pmid - &primitiveInfo[0]; + + wxASSERT( (mid >= start) && + (mid <= end) ); + } + else + { + // Create leaf _BVHBuildNode_ + const int firstPrimOffset = orderedPrims.size(); + + for( int i = start; i < end; ++i ) + { + const int primitiveNr = primitiveInfo[i].primitiveNumber; + + wxASSERT( primitiveNr < (int)m_primitives.size() ); + + orderedPrims.push_back( m_primitives[ primitiveNr ] ); + } + + node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); + + return node; + } + } + break; + } + } + + node->InitInterior( dim, + recursiveBuild( primitiveInfo, + start, + mid, + totalNodes, + orderedPrims ), + recursiveBuild( primitiveInfo, + mid, + end, + totalNodes, + orderedPrims) ); + } + } + + return node; +} + + +BVHBuildNode *CBVH_PBRT::HLBVHBuild( const std::vector &primitiveInfo, + int *totalNodes, + CONST_VECTOR_OBJECT &orderedPrims ) +{ + // Compute bounding box of all primitive centroids + CBBOX bounds; + bounds.Reset(); + + for( unsigned int i = 0; i < primitiveInfo.size(); ++i ) + bounds.Union( primitiveInfo[i].centroid ); + + // Compute Morton indices of primitives + std::vector mortonPrims( primitiveInfo.size() ); + + for( int i = 0; i < (int)primitiveInfo.size(); ++i ) + { + // Initialize _mortonPrims[i]_ for _i_th primitive + const int mortonBits = 10; + const int mortonScale = 1 << mortonBits; + + wxASSERT( primitiveInfo[i].primitiveNumber < (int)primitiveInfo.size() ); + + mortonPrims[i].primitiveIndex = primitiveInfo[i].primitiveNumber; + + const SFVEC3F centroidOffset = bounds.Offset( primitiveInfo[i].centroid ); + + wxASSERT( (centroidOffset.x >= 0.0f) && (centroidOffset.x <= 1.0f) ); + wxASSERT( (centroidOffset.y >= 0.0f) && (centroidOffset.y <= 1.0f) ); + wxASSERT( (centroidOffset.z >= 0.0f) && (centroidOffset.z <= 1.0f) ); + + mortonPrims[i].mortonCode = EncodeMorton3( centroidOffset * + SFVEC3F( (float)mortonScale ) ); + } + + // Radix sort primitive Morton indices + RadixSort( &mortonPrims ); + + // Create LBVH treelets at bottom of BVH + + // Find intervals of primitives for each treelet + std::vector treeletsToBuild; + + for( int start = 0, end = 1; end <= (int)mortonPrims.size(); ++end ) + { + const uint32_t mask = 0b00111111111111000000000000000000; + + if( (end == (int)mortonPrims.size()) || + ( (mortonPrims[start].mortonCode & mask) != + (mortonPrims[end].mortonCode & mask) ) ) + { + // Add entry to _treeletsToBuild_ for this treelet + const int numPrimitives = end - start; + const int maxBVHNodes = 2 * numPrimitives; + + // !TODO: implement a memory arena + BVHBuildNode *nodes = static_cast( _mm_malloc( maxBVHNodes * + sizeof( BVHBuildNode ), + L1_CACHE_LINE_SIZE ) ); + + m_addresses_pointer_to_mm_free.push_back( nodes ); + + for( int i = 0; i < maxBVHNodes; ++i ) + { + nodes[i].bounds.Reset(); + nodes[i].firstPrimOffset = 0; + nodes[i].nPrimitives = 0; + nodes[i].splitAxis = 0; + nodes[i].children[0] = NULL; + nodes[i].children[1] = NULL; + } + + LBVHTreelet tmpTreelet; + + tmpTreelet.startIndex = start; + tmpTreelet.numPrimitives = numPrimitives; + tmpTreelet.buildNodes = nodes; + + treeletsToBuild.push_back( tmpTreelet ); + + start = end; + } + } + + // Create LBVHs for treelets in parallel + int atomicTotal = 0; + int orderedPrimsOffset = 0; + + orderedPrims.resize( m_primitives.size() ); + + for( int index = 0; index < (int)treeletsToBuild.size(); ++index ) + { + // Generate _index_th LBVH treelet + int nodesCreated = 0; + const int firstBit = 29 - 12; + + LBVHTreelet &tr = treeletsToBuild[index]; + + wxASSERT( tr.startIndex < (int)mortonPrims.size() ); + + tr.buildNodes = emitLBVH( tr.buildNodes, + primitiveInfo, + &mortonPrims[tr.startIndex], + tr.numPrimitives, + &nodesCreated, + orderedPrims, + &orderedPrimsOffset, + firstBit ); + + atomicTotal += nodesCreated; + } + + *totalNodes = atomicTotal; + + // Initialize _finishedTreelets_ with treelet root node pointers + std::vector finishedTreelets; + finishedTreelets.reserve( treeletsToBuild.size() ); + + for( int index = 0; index < (int)treeletsToBuild.size(); ++index ) + finishedTreelets.push_back( treeletsToBuild[index].buildNodes ); + + // Create and return SAH BVH from LBVH treelets + return buildUpperSAH( finishedTreelets, + 0, + finishedTreelets.size(), + totalNodes ); +} + + +BVHBuildNode *CBVH_PBRT::emitLBVH( + BVHBuildNode *&buildNodes, + const std::vector &primitiveInfo, + MortonPrimitive *mortonPrims, int nPrimitives, int *totalNodes, + CONST_VECTOR_OBJECT &orderedPrims, + int *orderedPrimsOffset, int bit) +{ + wxASSERT( nPrimitives > 0 ); + wxASSERT( totalNodes != NULL ); + wxASSERT( orderedPrimsOffset != NULL ); + wxASSERT( nPrimitives > 0 ); + wxASSERT( mortonPrims != NULL ); + + if( (bit == -1) || (nPrimitives < m_maxPrimsInNode) ) + { + // Create and return leaf node of LBVH treelet + (*totalNodes)++; + + BVHBuildNode *node = buildNodes++; + CBBOX bounds; + bounds.Reset(); + + int firstPrimOffset = *orderedPrimsOffset; + *orderedPrimsOffset += nPrimitives; + + wxASSERT( (firstPrimOffset + (nPrimitives - 1)) < (int)orderedPrims.size() ); + + for( int i = 0; i < nPrimitives; ++i ) + { + const int primitiveIndex = mortonPrims[i].primitiveIndex; + + wxASSERT( primitiveIndex < (int)m_primitives.size() ); + + orderedPrims[firstPrimOffset + i] = m_primitives[primitiveIndex]; + bounds.Union( primitiveInfo[primitiveIndex].bounds ); + } + + node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); + + return node; + } + else + { + int mask = 1 << bit; + + // Advance to next subtree level if there's no LBVH split for this bit + if( (mortonPrims[0].mortonCode & mask) == + (mortonPrims[nPrimitives - 1].mortonCode & mask) ) + return emitLBVH( buildNodes, primitiveInfo, mortonPrims, nPrimitives, + totalNodes, orderedPrims, orderedPrimsOffset, + bit - 1 ); + + // Find LBVH split point for this dimension + int searchStart = 0; + int searchEnd = nPrimitives - 1; + + while( searchStart + 1 != searchEnd ) + { + wxASSERT(searchStart != searchEnd); + + const int mid = (searchStart + searchEnd) / 2; + + if( (mortonPrims[searchStart].mortonCode & mask) == + (mortonPrims[mid].mortonCode & mask) ) + searchStart = mid; + else + { + wxASSERT( (mortonPrims[mid].mortonCode & mask) == + (mortonPrims[searchEnd].mortonCode & mask) ); + searchEnd = mid; + } + } + + const int splitOffset = searchEnd; + + wxASSERT( splitOffset <= (nPrimitives - 1) ); + wxASSERT( (mortonPrims[splitOffset - 1].mortonCode & mask) != + (mortonPrims[splitOffset].mortonCode & mask) ); + + // Create and return interior LBVH node + (*totalNodes)++; + + BVHBuildNode *node = buildNodes++; + BVHBuildNode *lbvh[2]; + + lbvh[0] = emitLBVH( buildNodes, primitiveInfo, mortonPrims, splitOffset, + totalNodes, orderedPrims, orderedPrimsOffset, bit - 1 ); + + lbvh[1] = emitLBVH( buildNodes, primitiveInfo, &mortonPrims[splitOffset], + nPrimitives - splitOffset, totalNodes, orderedPrims, + orderedPrimsOffset, bit - 1 ); + + const int axis = bit % 3; + + node->InitInterior( axis, lbvh[0], lbvh[1] ); + + return node; + } +} + + +BVHBuildNode *CBVH_PBRT::buildUpperSAH( + std::vector &treeletRoots, + int start, int end, + int *totalNodes ) +{ + wxASSERT( totalNodes != NULL ); + wxASSERT( start < end ); + wxASSERT( end <= (int)treeletRoots.size() ); + + int nNodes = end - start; + + if( nNodes == 1 ) + return treeletRoots[start]; + + + (*totalNodes)++; + + BVHBuildNode *node = static_cast( _mm_malloc( sizeof( BVHBuildNode ), + L1_CACHE_LINE_SIZE ) ); + + m_addresses_pointer_to_mm_free.push_back( node ); + + node->bounds.Reset(); + node->firstPrimOffset = 0; + node->nPrimitives = 0; + node->splitAxis = 0; + node->children[0] = NULL; + node->children[1] = NULL; + + // Compute bounds of all nodes under this HLBVH node + CBBOX bounds; + bounds.Reset(); + + for( int i = start; i < end; ++i ) + bounds.Union( treeletRoots[i]->bounds ); + + // Compute bound of HLBVH node centroids, choose split dimension _dim_ + CBBOX centroidBounds; + centroidBounds.Reset(); + + for( int i = start; i < end; ++i ) + { + SFVEC3F centroid = + (treeletRoots[i]->bounds.Min() + treeletRoots[i]->bounds.Max()) * + 0.5f; + + centroidBounds.Union(centroid); + } + + const int dim = centroidBounds.MaxDimension(); + + // FIXME: if this hits, what do we need to do? + // Make sure the SAH split below does something... ? + wxASSERT( centroidBounds.Max()[dim] != centroidBounds.Min()[dim] ); + + // Allocate _BucketInfo_ for SAH partition buckets + const int nBuckets = 12; + + BucketInfo buckets[nBuckets]; + + for( int i = 0; i < nBuckets; ++i ) + { + buckets[i].count = 0; + buckets[i].bounds.Reset(); + } + + // Initialize _BucketInfo_ for HLBVH SAH partition buckets + for( int i = start; i < end; ++i ) + { + const float centroid = ( treeletRoots[i]->bounds.Min()[dim] + + treeletRoots[i]->bounds.Max()[dim] ) * + 0.5f; + int b = + nBuckets * ( (centroid - centroidBounds.Min()[dim] ) / + (centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) ); + + if( b == nBuckets ) + b = nBuckets - 1; + + wxASSERT( (b >= 0) && (b < nBuckets) ); + + buckets[b].count++; + buckets[b].bounds.Union( treeletRoots[i]->bounds ); + } + + // Compute costs for splitting after each bucket + float cost[nBuckets - 1]; + + for( int i = 0; i < nBuckets - 1; ++i ) + { + CBBOX b0, b1; + b0.Reset(); + b1.Reset(); + + int count0 = 0, count1 = 0; + + for( int j = 0; j <= i; ++j ) + { + if( buckets[j].count ) + { + count0 += buckets[j].count; + b0.Union( buckets[j].bounds ); + } + } + + for( int j = i + 1; j < nBuckets; ++j ) + { + if( buckets[j].count ) + { + count1 += buckets[j].count; + b1.Union( buckets[j].bounds ); + } + } + + cost[i] = .125f + + ( count0 * b0.SurfaceArea() + count1 * b1.SurfaceArea() ) / + bounds.SurfaceArea(); + } + + // Find bucket to split at that minimizes SAH metric + float minCost = cost[0]; + int minCostSplitBucket = 0; + + for( int i = 1; i < nBuckets - 1; ++i ) + { + if( cost[i] < minCost ) + { + minCost = cost[i]; + minCostSplitBucket = i; + } + } + + // Split nodes and create interior HLBVH SAH node + BVHBuildNode **pmid = std::partition( &treeletRoots[start], + &treeletRoots[end - 1] + 1, + HLBVH_SAH_Evaluator( minCostSplitBucket, + nBuckets, + dim, + centroidBounds ) ); + + const int mid = pmid - &treeletRoots[0]; + + wxASSERT( (mid > start) && (mid < end) ); + + node->InitInterior( dim, + buildUpperSAH( treeletRoots, start, mid, totalNodes ), + buildUpperSAH( treeletRoots, mid, end, totalNodes ) ); + + return node; +} + + +int CBVH_PBRT::flattenBVHTree( BVHBuildNode *node, uint32_t *offset ) +{ + LinearBVHNode *linearNode = &m_nodes[*offset]; + + linearNode->bounds = node->bounds; + + int myOffset = (*offset)++; + + if( node->nPrimitives > 0 ) + { + wxASSERT( (!node->children[0]) && (!node->children[1]) ); + wxASSERT( node->nPrimitives < 65536 ); + + linearNode->primitivesOffset = node->firstPrimOffset; + linearNode->nPrimitives = node->nPrimitives; + } + else + { + // Creater interior flattened BVH node + linearNode->axis = node->splitAxis; + linearNode->nPrimitives = 0; + flattenBVHTree( node->children[0], offset ); + linearNode->secondChildOffset = flattenBVHTree( node->children[1], offset ); + } + + return myOffset; +} + + +#define MAX_TODOS 64 + +bool CBVH_PBRT::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +{ + if( !m_nodes ) + return false; + + bool hit = false; + + // Follow ray through BVH nodes to find primitive intersections + int todoOffset = 0, nodeNum = 0; + int todo[MAX_TODOS]; + + while( true ) + { + const LinearBVHNode *node = &m_nodes[nodeNum]; + + wxASSERT( todoOffset < MAX_TODOS ); + + // Check ray against BVH node + float hitBox = 0.0f; + + const bool hitted = node->bounds.Intersect( aRay, &hitBox ); + + if( hitted && (hitBox < aHitInfo.m_tHit) ) + { + if( node->nPrimitives > 0 ) + { + // Intersect ray with primitives in leaf BVH node + for( int i = 0; i < node->nPrimitives; ++i ) + { + if( m_primitives[node->primitivesOffset + i]->Intersect( aRay, + aHitInfo ) ) + { + aHitInfo.m_acc_node_info = nodeNum; + hit = true; + } + } + } + else + { + // Put far BVH node on _todo_ stack, advance to near node + if( aRay.m_dirIsNeg[node->axis] ) + { + todo[todoOffset++] = nodeNum + 1; + nodeNum = node->secondChildOffset; + } + else + { + todo[todoOffset++] = node->secondChildOffset; + nodeNum = nodeNum + 1; + } + + continue; + } + } + + if( todoOffset == 0 ) + break; + + nodeNum = todo[--todoOffset]; + } + + return hit; +} + +// !TODO: this may be optimized +bool CBVH_PBRT::Intersect( const RAY &aRay, + HITINFO &aHitInfo, + unsigned int aAccNodeInfo ) const +{ + if( !m_nodes ) + return false; + + bool hit = false; + + // Follow ray through BVH nodes to find primitive intersections + int todoOffset = 0, nodeNum = aAccNodeInfo; + int todo[MAX_TODOS]; + + while( true ) + { + const LinearBVHNode *node = &m_nodes[nodeNum]; + + wxASSERT( todoOffset < MAX_TODOS ); + + // Check ray against BVH node + float hitBox = 0.0f; + + const bool hitted = node->bounds.Intersect( aRay, &hitBox ); + + if( hitted && (hitBox < aHitInfo.m_tHit) ) + { + if( node->nPrimitives > 0 ) + { + // Intersect ray with primitives in leaf BVH node + for( int i = 0; i < node->nPrimitives; ++i ) + { + if( m_primitives[node->primitivesOffset + i]->Intersect( aRay, + aHitInfo ) ) + { + //aHitInfo.m_acc_node_info = nodeNum; + hit = true; + } + } + } + else + { + // Put far BVH node on _todo_ stack, advance to near node + if( aRay.m_dirIsNeg[node->axis] ) + { + todo[todoOffset++] = nodeNum + 1; + nodeNum = node->secondChildOffset; + } + else + { + todo[todoOffset++] = node->secondChildOffset; + nodeNum = nodeNum + 1; + } + + continue; + } + } + + if( todoOffset == 0 ) + break; + + nodeNum = todo[--todoOffset]; + } + + return hit; +} + + +bool CBVH_PBRT::IntersectP( const RAY &aRay, float aMaxDistance ) const +{ + if( !m_nodes ) + return false; + + // Follow ray through BVH nodes to find primitive intersections + int todoOffset = 0, nodeNum = 0; + int todo[MAX_TODOS]; + + while( true ) + { + const LinearBVHNode *node = &m_nodes[nodeNum]; + + wxASSERT( todoOffset < MAX_TODOS ); + + // Check ray against BVH node + float hitBox = 0.0f; + + const bool hitted = node->bounds.Intersect( aRay, &hitBox ); + + if( hitted && (hitBox < aMaxDistance) ) + { + if( node->nPrimitives > 0 ) + { + // Intersect ray with primitives in leaf BVH node + for( int i = 0; i < node->nPrimitives; ++i ) + { + const COBJECT *obj = m_primitives[node->primitivesOffset + i]; + + if( obj->GetMaterial()->GetCastShadows() ) + if( obj->IntersectP( aRay, aMaxDistance ) ) + return true; + } + } + else + { + // Put far BVH node on _todo_ stack, advance to near node + if( aRay.m_dirIsNeg[node->axis] ) + { + todo[todoOffset++] = nodeNum + 1; + nodeNum = node->secondChildOffset; + } + else + { + todo[todoOffset++] = node->secondChildOffset; + nodeNum = nodeNum + 1; + } + + continue; + } + } + + if( todoOffset == 0 ) + break; + + nodeNum = todo[--todoOffset]; + } + + return false; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_pbrt.h b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_pbrt.h new file mode 100644 index 0000000000..67b7208479 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/cbvh_pbrt.h @@ -0,0 +1,168 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cbvh_pbrt.h + * @brief This BVH implementation is based on the source code implementation + * from the book "Physically Based Rendering" (v2 and v3) + * + * Adaptions performed: + * - Types and class types adapted to KiCad project + * - Convert some source to build in the C++ specification of KiCad + * - Code style to match KiCad + * - Asserts converted + * - Use compare functions/structures for std::partition and std::nth_element + * + * The original source code have the following licence: + * + * "pbrt source code is Copyright(c) 1998-2015 + * Matt Pharr, Greg Humphreys, and Wenzel Jakob. + * + * This file is part of pbrt. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * 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." + * + */ + + +#ifndef _CBVH_PBRT_H_ +#define _CBVH_PBRT_H_ + +#include "caccelerator.h" +#include +#include + +// Forward Declarations +struct BVHBuildNode; +struct BVHPrimitiveInfo; +struct MortonPrimitive; + +struct LinearBVHNode +{ + // 24 bytes + CBBOX bounds; + + // 4 bytes + union + { + int primitivesOffset; ///< leaf + int secondChildOffset; ///< interior + }; + + // 4 bytes + uint16_t nPrimitives; ///< 0 -> interior node + uint8_t axis; ///< interior node: xyz + uint8_t pad[1]; ///< ensure 32 byte total size +}; + + +enum SPLITMETHOD +{ + SPLIT_MIDDLE, + SPLIT_EQUALCOUNTS, + SPLIT_SAH, + SPLIT_HLBVH +}; + + +class CBVH_PBRT : public CGENERICACCELERATOR +{ + +public: + CBVH_PBRT( const CGENERICCONTAINER &aObjectContainer, + int aMaxPrimsInNode = 4, + SPLITMETHOD aSplitMethod = SPLIT_SAH ); + + ~CBVH_PBRT(); + + // Imported from CGENERICACCELERATOR + bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; + bool Intersect( const RAY &aRay, HITINFO &aHitInfo, unsigned int aAccNodeInfo ) const; + bool Intersect(const RAYPACKET &aRayPacket, HITINFO_PACKET *aHitInfoPacket ) const; + bool IntersectP( const RAY &aRay, float aMaxDistance ) const; + +private: + + BVHBuildNode *recursiveBuild( std::vector &primitiveInfo, + int start, + int end, + int *totalNodes, + CONST_VECTOR_OBJECT &orderedPrims ); + + BVHBuildNode *HLBVHBuild( const std::vector &primitiveInfo, + int *totalNodes, + CONST_VECTOR_OBJECT &orderedPrims ); + + //!TODO: after implement memory arena, put const back to this functions + BVHBuildNode *emitLBVH( BVHBuildNode *&buildNodes, + const std::vector &primitiveInfo, + MortonPrimitive *mortonPrims, + int nPrimitives, + int *totalNodes, + CONST_VECTOR_OBJECT &orderedPrims, + int *orderedPrimsOffset, + int bit ); + + BVHBuildNode *buildUpperSAH( std::vector &treeletRoots, + int start, + int end, + int *totalNodes ); + + int flattenBVHTree( BVHBuildNode *node, + uint32_t *offset ); + + // BVH Private Data + const int m_maxPrimsInNode; + SPLITMETHOD m_splitMethod; + CONST_VECTOR_OBJECT m_primitives; + LinearBVHNode *m_nodes; + + std::list m_addresses_pointer_to_mm_free; + + // Partition traversal + unsigned int m_I[RAYPACKET_RAYS_PER_PACKET]; +}; + +#endif // _CBVH_PBRT_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer.cpp new file mode 100644 index 0000000000..9f23b3803b --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer.cpp @@ -0,0 +1,124 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ccontainer.cpp + * @brief + */ + +#include "ccontainer.h" +#include + +CGENERICCONTAINER::CGENERICCONTAINER() +{ + m_objects.clear(); + m_bbox.Reset(); +} + +void CGENERICCONTAINER::Clear() +{ + if( !m_objects.empty() ) + { + for( LIST_OBJECT::iterator ii = m_objects.begin(); + ii != m_objects.end(); + ++ii ) + { + delete *ii; + *ii = NULL; + } + + m_objects.clear(); + } + + m_bbox.Reset(); +} + + +CGENERICCONTAINER::~CGENERICCONTAINER() +{ + Clear(); +} + + +void CGENERICCONTAINER::ConvertTo( CONST_VECTOR_OBJECT &aOutVector ) const +{ + aOutVector.resize( m_objects.size() ); + + if( !m_objects.empty() ) + { + unsigned int i = 0; + + for( LIST_OBJECT::const_iterator ii = m_objects.begin(); + ii != m_objects.end(); + ++ii ) + { + wxASSERT( (*ii) != NULL ); + + aOutVector[i++] = static_cast(*ii); + } + } +} + + +bool CCONTAINER::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +{ + + if( !m_bbox.Intersect( aRay ) ) + return false; + + bool hitted = false; + + for( LIST_OBJECT::const_iterator ii = m_objects.begin(); + ii != m_objects.end(); + ++ii ) + { + const COBJECT *object = static_cast(*ii); + + if( object->Intersect( aRay, aHitInfo) ) + hitted = true; + } + + return hitted; +} + + +bool CCONTAINER::IntersectP( const RAY &aRay, float aMaxDistance ) const +{ +/* + if( !m_bbox.Inside( aRay.m_Origin ) ) + if( !m_bbox.Intersect( aRay ) ) + return false; +*/ + for( LIST_OBJECT::const_iterator ii = m_objects.begin(); + ii != m_objects.end(); + ++ii ) + { + const COBJECT *object = static_cast(*ii); + + if( object->IntersectP( aRay, aMaxDistance ) ) + return true; + } + + return false; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer.h b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer.h index 7026d4e746..8d8a4e1126 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -38,7 +38,7 @@ typedef std::list LIST_OBJECT; typedef std::vector VECTOR_OBJECT; typedef std::vector CONST_VECTOR_OBJECT; -class GLM_ALIGN(CLASS_ALIGNMENT) CGENERICCONTAINER +class CGENERICCONTAINER { protected: CBBOX m_bbox; @@ -62,7 +62,7 @@ public: const LIST_OBJECT &GetList() const { return m_objects; } - const void ConvertTo( CONST_VECTOR_OBJECT &aOutVector ) const; + void ConvertTo( CONST_VECTOR_OBJECT &aOutVector ) const; const CBBOX &GetBBox() const { return m_bbox; } @@ -73,7 +73,7 @@ private: }; -class GLM_ALIGN(CLASS_ALIGNMENT) CCONTAINER : public CGENERICCONTAINER +class CCONTAINER : public CGENERICCONTAINER { public: bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.cpp new file mode 100644 index 0000000000..11c09bb795 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.cpp @@ -0,0 +1,437 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ccontainer2d.cpp + * @brief + */ + +#include "ccontainer2d.h" +#include +#include +#include +#include + + +// ///////////////////////////////////////////////////////////////////////////// +// CGENERICCONTAINER2 +// ///////////////////////////////////////////////////////////////////////////// + +CGENERICCONTAINER2D::CGENERICCONTAINER2D( OBJECT2D_TYPE aObjType ) +{ + m_bbox.Reset(); +} + + +void CGENERICCONTAINER2D::Clear() +{ + m_bbox.Reset(); + + for( LIST_OBJECT2D::iterator ii = m_objects.begin(); + ii != m_objects.end(); + ++ii ) + { + delete *ii; + *ii = NULL; + } + + m_objects.clear(); +} + + +CGENERICCONTAINER2D::~CGENERICCONTAINER2D() +{ + Clear(); +} + + + + +// ///////////////////////////////////////////////////////////////////////////// +// CCONTAINER2D +// ///////////////////////////////////////////////////////////////////////////// + +CCONTAINER2D::CCONTAINER2D() : CGENERICCONTAINER2D( OBJ2D_CONTAINER ) +{ + +} +/* + +bool CCONTAINER2D::Intersects( const CBBOX2D &aBBox ) const +{ + return false; +} + + +bool CCONTAINER2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // NOT IMPLEMENTED + return false; +} + + +bool CCONTAINER2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut ) const +{ + if( !m_bbox.Intersect( aSegRay ) ) + return false; + + bool hitted = false; + + for( LIST_OBJECT2D::const_iterator ii = m_objects.begin(); + ii != m_objects.end(); + ii++ ) + { + const COBJECT2D *object = static_cast(*ii); + + float t; + SFVEC2F hitNormal; + if( object->Intersect( aSegRay, &t, &hitNormal ) ) + if( (hitted == false) || (t < *aOutT ) ) + { + hitted = true; + *aOutT = t; + *aNormalOut = hitNormal; + } + } + + return hitted; +} + + +INTERSECTION_RESULT CCONTAINER2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + return INTR_MISSES; +} + + +bool CCONTAINER2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + if( !m_bbox.Inside( aPoint ) ) + return false; + + for( LIST_OBJECT2D::const_iterator ii = m_objects.begin(); + ii != m_objects.end(); + ii++ ) + { + const COBJECT2D *object = static_cast(*ii); + + if( object->IsPointInside( aPoint ) ) + return true; + } + + return false; +} + +*/ + +void CCONTAINER2D::GetListObjectsIntersects( const CBBOX2D & aBBox, + CONST_LIST_OBJECT2D &aOutList ) const +{ + // !TODO: +} + + + + +// ///////////////////////////////////////////////////////////////////////////// +// CBVHCONTAINER2D +// ///////////////////////////////////////////////////////////////////////////// + +CBVHCONTAINER2D::CBVHCONTAINER2D() : CGENERICCONTAINER2D( OBJ2D_BVHCONTAINER ) +{ + m_isInitialized = false; + m_bbox.Reset(); + m_elements_to_delete.clear(); + m_Tree = NULL; +} + +/* +bool CBVHCONTAINER2D::Intersects( const CBBOX2D &aBBox ) const +{ + // !TODO: implement the BVH + return m_bbox.Intersects( aBBox ); +} + + +bool CBVHCONTAINER2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // NOT IMPLEMENTED + return false; +} + + +bool CBVHCONTAINER2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, SFVEC2F *aNormalOut ) const +{ + // !TODO: implement the BVH + + if( !m_bbox.Intersect( aSegRay ) ) + return false; + + bool hitted = false; + + for( LIST_OBJECT2D::const_iterator ii = m_objects.begin(); + ii != m_objects.end(); + ii++ ) + { + const COBJECT2D *object = static_cast(*ii); + + float t; + SFVEC2F hitNormal; + if( object->Intersect( aSegRay, &t, &hitNormal ) ) + if( (hitted == false) || (t < *aOutT ) ) + { + hitted = true; + *aOutT = t; + *aNormalOut = hitNormal; + } + } + + return hitted; +} + + +INTERSECTION_RESULT CBVHCONTAINER2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + return INTR_MISSES; +} + + +bool CBVHCONTAINER2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + // !TODO: implement the BVH + + if( !m_bbox.Inside( aPoint ) ) + return false; + + for( LIST_OBJECT2D::const_iterator ii = m_objects.begin(); + ii != m_objects.end(); + ii++ ) + { + const COBJECT2D *object = static_cast(*ii); + + if( object->IsPointInside( aPoint ) ) + return true; + } + + return false; +} +*/ + +void CBVHCONTAINER2D::destroy() +{ + for( std::list::iterator ii = m_elements_to_delete.begin(); + ii != m_elements_to_delete.end(); + ++ii ) + { + delete *ii; + *ii = NULL; + } + m_elements_to_delete.clear(); + + m_isInitialized = false; +} + + +CBVHCONTAINER2D::~CBVHCONTAINER2D() +{ + destroy(); +} + + +#define BVH_CONTAINER2D_MAX_OBJ_PER_LEAF 4 + + +void CBVHCONTAINER2D::BuildBVH() +{ + if( m_isInitialized ) + destroy(); + + if( m_objects.empty() ) + { + return; + } + + m_isInitialized = true; + m_Tree = new BVH_CONTAINER_NODE_2D; + + m_elements_to_delete.push_back( m_Tree ); + m_Tree->m_BBox = m_bbox; + + for( LIST_OBJECT2D::const_iterator ii = m_objects.begin(); + ii != m_objects.end(); + ++ii ) + { + m_Tree->m_LeafList.push_back( static_cast(*ii) ); + } + + recursiveBuild_MIDDLE_SPLIT( m_Tree ); +} + + +// Based on a blog post by VADIM KRAVCENKO +// http://www.vadimkravcenko.com/bvh-tree-building +// Implements: + +// "Split in the middle of the longest Axis" +// "Creates a binary tree with Top-Down approach. +// Fastest BVH building, but least [speed] accuracy." + +static bool sortByCentroid_X( const COBJECT2D *a, const COBJECT2D *b ) +{ + return a->GetCentroid()[0] < b->GetCentroid()[0]; +} + +static bool sortByCentroid_Y( const COBJECT2D *a, const COBJECT2D *b ) +{ + return a->GetCentroid()[0] < b->GetCentroid()[0]; +} + +static bool sortByCentroid_Z( const COBJECT2D *a, const COBJECT2D *b ) +{ + return a->GetCentroid()[0] < b->GetCentroid()[0]; +} + +void CBVHCONTAINER2D::recursiveBuild_MIDDLE_SPLIT( BVH_CONTAINER_NODE_2D *aNodeParent ) +{ + wxASSERT( aNodeParent != NULL ); + wxASSERT( aNodeParent->m_BBox.IsInitialized() == true ); + wxASSERT( aNodeParent->m_LeafList.size() > 0 ); + + if( aNodeParent->m_LeafList.size() > BVH_CONTAINER2D_MAX_OBJ_PER_LEAF ) + { + // Create Leaf Nodes + BVH_CONTAINER_NODE_2D *leftNode = new BVH_CONTAINER_NODE_2D; + BVH_CONTAINER_NODE_2D *rightNode = new BVH_CONTAINER_NODE_2D; + m_elements_to_delete.push_back( leftNode ); + m_elements_to_delete.push_back( rightNode ); + + leftNode->m_BBox.Reset(); + rightNode->m_BBox.Reset(); + leftNode->m_LeafList.clear(); + rightNode->m_LeafList.clear(); + + // Decide wich axis to split + const unsigned int axis_to_split = aNodeParent->m_BBox.MaxDimension(); + + // Divide the objects + switch( axis_to_split ) + { + case 0: aNodeParent->m_LeafList.sort( sortByCentroid_X ); + case 1: aNodeParent->m_LeafList.sort( sortByCentroid_Y ); + case 2: aNodeParent->m_LeafList.sort( sortByCentroid_Z ); + } + + unsigned int i = 0; + + for( CONST_LIST_OBJECT2D::const_iterator ii = aNodeParent->m_LeafList.begin(); + ii != aNodeParent->m_LeafList.end(); + ++ii ) + { + const COBJECT2D *object = static_cast(*ii); + + if( i < (aNodeParent->m_LeafList.size() / 2 ) ) + { + leftNode->m_BBox.Union( object->GetBBox() ); + leftNode->m_LeafList.push_back( object ); + } + else + { + rightNode->m_BBox.Union( object->GetBBox() ); + rightNode->m_LeafList.push_back( object ); + } + + i++; + } + + wxASSERT( leftNode->m_LeafList.size() > 0 ); + wxASSERT( rightNode->m_LeafList.size() > 0 ); + wxASSERT( ( leftNode->m_LeafList.size() + rightNode->m_LeafList.size() ) == + aNodeParent->m_LeafList.size() ); + + aNodeParent->m_Children[0] = leftNode; + aNodeParent->m_Children[1] = rightNode; + aNodeParent->m_LeafList.clear(); + + recursiveBuild_MIDDLE_SPLIT( leftNode ); + recursiveBuild_MIDDLE_SPLIT( rightNode ); + } + else + { + // It is a Leaf + aNodeParent->m_Children[0] = NULL; + aNodeParent->m_Children[1] = NULL; + } +} + + +void CBVHCONTAINER2D::GetListObjectsIntersects( const CBBOX2D &aBBox, + CONST_LIST_OBJECT2D &aOutList ) const +{ + wxASSERT( aBBox.IsInitialized() == true ); + wxASSERT( m_isInitialized == true ); + + aOutList.clear(); + + if( m_Tree ) + recursiveGetListObjectsIntersects( m_Tree, aBBox, aOutList ); +} + + +void CBVHCONTAINER2D::recursiveGetListObjectsIntersects( const BVH_CONTAINER_NODE_2D *aNode, + const CBBOX2D & aBBox, + CONST_LIST_OBJECT2D &aOutList ) const +{ + wxASSERT( aNode != NULL ); + wxASSERT( aBBox.IsInitialized() == true ); + + if( aNode->m_BBox.Intersects( aBBox ) ) + { + if( !aNode->m_LeafList.empty() ) + { + wxASSERT( aNode->m_Children[0] == NULL ); + wxASSERT( aNode->m_Children[1] == NULL ); + + // Leaf + for( CONST_LIST_OBJECT2D::const_iterator ii = aNode->m_LeafList.begin(); + ii != aNode->m_LeafList.end(); + ++ii ) + { + const COBJECT2D *obj = static_cast(*ii); + + if( obj->Intersects( aBBox ) ) + aOutList.push_back( obj ); + } + } + else + { + wxASSERT( aNode->m_Children[0] != NULL ); + wxASSERT( aNode->m_Children[1] != NULL ); + + // Node + recursiveGetListObjectsIntersects( aNode->m_Children[0], aBBox, aOutList ); + recursiveGetListObjectsIntersects( aNode->m_Children[1], aBBox, aOutList ); + } + } +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.h index eda1aa94b2..2f5edf14ea 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -37,14 +37,14 @@ typedef std::list LIST_OBJECT2D; typedef std::list CONST_LIST_OBJECT2D; -class GLM_ALIGN(CLASS_ALIGNMENT) CGENERICCONTAINER2D +class CGENERICCONTAINER2D { protected: CBBOX2D m_bbox; LIST_OBJECT2D m_objects; public: - CGENERICCONTAINER2D( OBJECT2D_TYPE aObjType ); + explicit CGENERICCONTAINER2D( OBJECT2D_TYPE aObjType ); virtual ~CGENERICCONTAINER2D(); @@ -55,7 +55,7 @@ public: m_objects.push_back( aObject ); m_bbox.Union( aObject->GetBBox() ); } - }// automatically releases the lock when lck goes out of scope. + } void Clear(); @@ -66,19 +66,21 @@ public: * @param aBBox - a bbox to make the query * @param aOutList - A list of objects that intersects the bbox */ - virtual void GetListObjectsIntersects( const CBBOX2D & aBBox, CONST_LIST_OBJECT2D &aOutList ) const = 0; + virtual void GetListObjectsIntersects( const CBBOX2D & aBBox, + CONST_LIST_OBJECT2D &aOutList ) const = 0; private: }; -class GLM_ALIGN(CLASS_ALIGNMENT) CCONTAINER2D : public CGENERICCONTAINER2D +class CCONTAINER2D : public CGENERICCONTAINER2D { public: CCONTAINER2D(); // Imported from CGENERICCONTAINER2D - void GetListObjectsIntersects( const CBBOX2D & aBBox, CONST_LIST_OBJECT2D &aOutList ) const; + void GetListObjectsIntersects( const CBBOX2D & aBBox, + CONST_LIST_OBJECT2D &aOutList ) const; }; @@ -86,11 +88,13 @@ struct BVH_CONTAINER_NODE_2D { CBBOX2D m_BBox; BVH_CONTAINER_NODE_2D *m_Children[2]; - CONST_LIST_OBJECT2D m_LeafList; ///< Store the list of objects if that node is a Leaf + + /// Store the list of objects if that node is a Leaf + CONST_LIST_OBJECT2D m_LeafList; }; -class GLM_ALIGN(CLASS_ALIGNMENT) CBVHCONTAINER2D : public CGENERICCONTAINER2D +class CBVHCONTAINER2D : public CGENERICCONTAINER2D { public: CBVHCONTAINER2D(); @@ -105,12 +109,15 @@ private: void destroy(); void recursiveBuild_MIDDLE_SPLIT( BVH_CONTAINER_NODE_2D *aNodeParent ); - void recursiveGetListObjectsIntersects( const BVH_CONTAINER_NODE_2D *aNode, const CBBOX2D & aBBox, CONST_LIST_OBJECT2D &aOutList ) const; + void recursiveGetListObjectsIntersects( const BVH_CONTAINER_NODE_2D *aNode, + const CBBOX2D & aBBox, + CONST_LIST_OBJECT2D &aOutList ) const; public: // Imported from CGENERICCONTAINER2D - void GetListObjectsIntersects( const CBBOX2D & aBBox, CONST_LIST_OBJECT2D &aOutList ) const; + void GetListObjectsIntersects( const CBBOX2D & aBBox, + CONST_LIST_OBJECT2D &aOutList ) const; }; #endif // _CCONTAINER2D_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_createscene.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_createscene.cpp new file mode 100644 index 0000000000..9ee0a189e5 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_createscene.cpp @@ -0,0 +1,1419 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file c3d_render_createscene.cpp + * @brief + */ + + +#include "c3d_render_raytracing.h" +#include "shapes3D/cplane.h" +#include "shapes3D/croundseg.h" +#include "shapes3D/clayeritem.h" +#include "shapes3D/ccylinder.h" +#include "shapes3D/ctriangle.h" +#include "shapes2D/citemlayercsg2d.h" +#include "shapes2D/cring2d.h" +#include "shapes2D/cpolygon2d.h" +#include "shapes2D/cfilledcircle2d.h" +#include "accelerators/cbvh_pbrt.h" +#include "3d_fastmath.h" +#include "3d_math.h" + +#include +#include + +#include + +/** + * Scale convertion from 3d model units to pcb units + */ +#define UNITS3D_TO_UNITSPCB (IU_PER_MM) + +void C3D_RENDER_RAYTRACING::setupMaterials() +{ + // http://devernay.free.fr/cours/opengl/materials.html + + // Copper + + // This guess the material type(ex: copper vs gold) to determine the + // shininess factor between 0.1 and 0.4 + float shininessfactor = 0.40f - mapf( fabs( m_settings.m_CopperColor.r - + m_settings.m_CopperColor.g ), + 0.15f, 1.00f, + 0.00f, 0.30f ); + + m_materials.m_Copper = CBLINN_PHONG_MATERIAL( + (SFVEC3F)m_settings.m_CopperColor * (SFVEC3F)(0.28f), // ambient + SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive + glm::clamp( ((SFVEC3F)(1.0f) - + (SFVEC3F)m_settings.m_CopperColor), + SFVEC3F( 0.0f ), + SFVEC3F( 0.40f ) ), // specular + shininessfactor * 128.0f, // shiness + 0.0f, // transparency + 0.0f ); + + m_materials.m_Paste = CBLINN_PHONG_MATERIAL( + (SFVEC3F)m_settings.m_SolderPasteColor * + (SFVEC3F)m_settings.m_SolderPasteColor, // ambient + SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive + (SFVEC3F)m_settings.m_SolderPasteColor * + (SFVEC3F)m_settings.m_SolderPasteColor, // specular + 0.10f * 128.0f, // shiness + 0.0f, // transparency + 0.0f ); + + m_materials.m_SilkS = CBLINN_PHONG_MATERIAL( + SFVEC3F( 0.11f ), // ambient + SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive + glm::clamp( ((SFVEC3F)(1.0f) - + (SFVEC3F)m_settings.m_SilkScreenColor), + SFVEC3F( 0.0f ), + SFVEC3F( 0.1f ) ), // specular + 0.078125f * 128.0f, // shiness + 0.0f, // transparency + 0.0f ); + + m_materials.m_SolderMask = CBLINN_PHONG_MATERIAL( + (SFVEC3F)m_settings.m_SolderMaskColor * + 0.10f, // ambient + SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive + glm::clamp( ( (SFVEC3F)( 1.0f ) - + (SFVEC3F)m_settings.m_SolderMaskColor ), + SFVEC3F( 0.0f ), + SFVEC3F( 0.35f ) ), // specular + 0.85f * 128.0f, // shiness + 0.12f, // transparency + 0.16f ); // reflection + + m_materials.m_SolderMask.SetCastShadows( true ); + + m_materials.m_EpoxyBoard = CBLINN_PHONG_MATERIAL( + SFVEC3F( 16.0f / 255.0f, + 14.0f / 255.0f, + 10.0f / 255.0f ), // ambient + SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive + SFVEC3F( 10.0f / 255.0f, + 8.0f / 255.0f, + 10.0f / 255.0f ), // specular + 0.1f * 128.0f, // shiness + 0.10f, // transparency + 0.0f ); // reflection + + SFVEC3F bgTop = (SFVEC3F)m_settings.m_BgColorTop; + //SFVEC3F bgBot = (SFVEC3F)m_settings.m_BgColorBot; + + m_materials.m_Floor = CBLINN_PHONG_MATERIAL( + bgTop * 0.125f, // ambient + SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive + (SFVEC3F(1.0f) - bgTop) / 3.0f, // specular + 0.10f * 128.0f, // shiness + 0.0f, // transparency + 0.50f ); // reflection +} + + + +/** Function create_3d_object_from + * @brief Creates on or more 3D objects form a 2D object and Z positions. It try + * optimize some types of objects that will be faster to trace than the + * CLAYERITEM object. + * @param aObject2D + * @param aZMin + * @param aZMax + */ +void C3D_RENDER_RAYTRACING::create_3d_object_from( CCONTAINER &aDstContainer, + const COBJECT2D *aObject2D, + float aZMin, float aZMax, + const CMATERIAL *aMaterial, + const SFVEC3F &aObjColor ) +{ + switch( aObject2D->GetObjectType() ) + { + case OBJ2D_DUMMYBLOCK: + { + m_stats_converted_dummy_to_plane++; +#if 1 + CXYPLANE *objPtr; + objPtr = new CXYPLANE( CBBOX ( SFVEC3F( aObject2D->GetBBox().Min().x, + aObject2D->GetBBox().Min().y, + aZMin ), + SFVEC3F( aObject2D->GetBBox().Max().x, + aObject2D->GetBBox().Max().y, + aZMin ) ) ); + objPtr->SetMaterial( aMaterial ); + objPtr->SetColor( aObjColor ); + aDstContainer.Add( objPtr ); + + objPtr = new CXYPLANE( CBBOX ( SFVEC3F( aObject2D->GetBBox().Min().x, + aObject2D->GetBBox().Min().y, + aZMax ), + SFVEC3F( aObject2D->GetBBox().Max().x, + aObject2D->GetBBox().Max().y, + aZMax ) ) ); + objPtr->SetMaterial( aMaterial ); + objPtr->SetColor( aObjColor ); + aDstContainer.Add( objPtr ); +#else + objPtr = new CDUMMYBLOCK( CBBOX ( SFVEC3F( aObject2D->GetBBox().Min().x, + aObject2D->GetBBox().Min().y, + aZMin ), + SFVEC3F( aObject2D->GetBBox().Max().x, + aObject2D->GetBBox().Max().y, + aZMax ) ) ); + objPtr->SetMaterial( aMaterial ); + aDstContainer.Add( objPtr ); +#endif + } + break; + + case OBJ2D_ROUNDSEG: + { + m_stats_converted_roundsegment2d_to_roundsegment++; + + const CROUNDSEGMENT2D *aRoundSeg2D = static_cast( aObject2D ); + CROUNDSEG *objPtr = new CROUNDSEG( *aRoundSeg2D, aZMin, aZMax ); + objPtr->SetMaterial( aMaterial ); + objPtr->SetColor( aObjColor ); + aDstContainer.Add( objPtr ); + } + break; + + + default: + { + CLAYERITEM *objPtr = new CLAYERITEM( aObject2D, aZMin, aZMax ); + objPtr->SetMaterial( aMaterial ); + objPtr->SetColor( aObjColor ); + aDstContainer.Add( objPtr ); + } + break; + } +} + + +void C3D_RENDER_RAYTRACING::reload( REPORTER *aStatusTextReporter ) +{ + m_reloadRequested = false; + + m_model_materials.clear(); + + COBJECT2D_STATS::Instance().ResetStats(); + COBJECT3D_STATS::Instance().ResetStats(); + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf("InitSettings...\n"); +#endif + + unsigned stats_startReloadTime = GetRunningMicroSecs(); + + m_settings.InitSettings( aStatusTextReporter ); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_endReloadTime = GetRunningMicroSecs(); + unsigned stats_startConvertTime = GetRunningMicroSecs(); + #endif + + SFVEC3F camera_pos = m_settings.GetBoardCenter3DU(); + m_settings.CameraGet().SetBoardLookAtPos( camera_pos ); + + // Init initial lights + m_lights.Clear(); + + // This will work as the front camera light. + const float light_camera_intensity = 0.15f; + const float light_directional_intensity_top = 0.20f; + const float light_directional_intensity = ( 1.0f - ( light_camera_intensity + + light_directional_intensity_top ) ) / 4.0f; + + m_camera_light = new CDIRECTIONALLIGHT( SFVEC3F( 0.0f, 0.0f, 0.0f ), + SFVEC3F( light_camera_intensity ) ); + m_camera_light->SetCastShadows( false ); + m_lights.Add( m_camera_light ); + + // http://www.flashandmath.com/mathlets/multicalc/coords/shilmay23fin.html + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 0.03f, + glm::pi() * 0.25f ), + SFVEC3F( light_directional_intensity_top ) ) ); + + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 0.97f, + glm::pi() * 1.25f ), + SFVEC3F( light_directional_intensity_top ) ) ); + + + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 1.0f / 8.0f, + glm::pi() * 1 / 4.0f ), + SFVEC3F( light_directional_intensity ) ) ); + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 1.0f / 8.0f, + glm::pi() * 3 / 4.0f ), + SFVEC3F( light_directional_intensity ) ) ); + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 1.0f / 8.0f, + glm::pi() * 5 / 4.0f ), + SFVEC3F( light_directional_intensity ) ) ); + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 1.0f / 8.0f, + glm::pi() * 7 / 4.0f ), + SFVEC3F( light_directional_intensity ) ) ); + + + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 7.0f / 8.0f, + glm::pi() * 1 / 4.0f ), + SFVEC3F( light_directional_intensity ) ) ); + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 7.0f / 8.0f, + glm::pi() * 3 / 4.0f ), + SFVEC3F( light_directional_intensity ) ) ); + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 7.0f / 8.0f, + glm::pi() * 5 / 4.0f ), + SFVEC3F( light_directional_intensity ) ) ); + m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi() * 7.0f / 8.0f, + glm::pi() * 7 / 4.0f ), + SFVEC3F( light_directional_intensity ) ) ); + + + m_object_container.Clear(); + m_containerWithObjectsToDelete.Clear(); + + + // Create and add the outline board + // ///////////////////////////////////////////////////////////////////////// + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf("Create outline board...\n"); +#endif + + delete m_outlineBoard2dObjects; + + m_outlineBoard2dObjects = new CCONTAINER2D; + + if( ((const SHAPE_POLY_SET &)m_settings.GetBoardPoly()).OutlineCount() == 1 ) + { + float divFactor = 0.0f; + + if( m_settings.GetStats_Nr_Vias() ) + divFactor = m_settings.GetStats_Med_Via_Hole_Diameter3DU() * 18.0f; + else + if( m_settings.GetStats_Nr_Holes() ) + divFactor = m_settings.GetStats_Med_Hole_Diameter3DU() * 8.0f; + + SHAPE_POLY_SET boardPolyCopy = m_settings.GetBoardPoly(); + boardPolyCopy.Fracture( SHAPE_POLY_SET::PM_FAST ); + + Convert_path_polygon_to_polygon_blocks_and_dummy_blocks( + boardPolyCopy, + *m_outlineBoard2dObjects, + m_settings.BiuTo3Dunits(), + divFactor, + (const BOARD_ITEM &)*m_settings.GetBoard() ); + + if( m_settings.GetFlag( FL_SHOW_BOARD_BODY ) ) + { + const LIST_OBJECT2D &listObjects = m_outlineBoard2dObjects->GetList(); + + for( LIST_OBJECT2D::const_iterator object2d_iterator = listObjects.begin(); + object2d_iterator != listObjects.end(); + ++object2d_iterator ) + { + const COBJECT2D *object2d_A = static_cast(*object2d_iterator); + + std::vector *object2d_B = new std::vector(); + + // Check if there are any THT that intersects this outline object part + if( !m_settings.GetThroughHole_Outer().GetList().empty() ) + { + + CONST_LIST_OBJECT2D intersectionList; + m_settings.GetThroughHole_Outer().GetListObjectsIntersects( + object2d_A->GetBBox(), + intersectionList ); + + if( !intersectionList.empty() ) + { + for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin(); + hole != intersectionList.end(); + ++hole ) + { + const COBJECT2D *hole2d = static_cast(*hole); + + if( object2d_A->Intersects( hole2d->GetBBox() ) ) + //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) + object2d_B->push_back( hole2d ); + } + } + } + + if( object2d_B->empty() ) + { + delete object2d_B; + object2d_B = CSGITEM_EMPTY; + } + + if( object2d_B == CSGITEM_EMPTY ) + { + #if 0 + create_3d_object_from( m_object_container, object2d_A, + m_settings.GetLayerBottomZpos3DU( F_Cu ), + m_settings.GetLayerBottomZpos3DU( B_Cu ), + &m_materials.m_EpoxyBoard, + g_epoxyColor ); + #else + CLAYERITEM *objPtr = new CLAYERITEM( object2d_A, + m_settings.GetLayerBottomZpos3DU( F_Cu ), + m_settings.GetLayerBottomZpos3DU( B_Cu ) ); + + objPtr->SetMaterial( &m_materials.m_EpoxyBoard ); + objPtr->SetColor( (SFVEC3F)m_settings.m_BoardBodyColor ); + m_object_container.Add( objPtr ); + #endif + } + else + { + CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( + object2d_A, + object2d_B, + CSGITEM_FULL, + (const BOARD_ITEM &)*m_settings.GetBoard() ); + + m_containerWithObjectsToDelete.Add( itemCSG2d ); + + CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d, + m_settings.GetLayerBottomZpos3DU( F_Cu ), + m_settings.GetLayerBottomZpos3DU( B_Cu ) ); + + objPtr->SetMaterial( &m_materials.m_EpoxyBoard ); + objPtr->SetColor( (SFVEC3F)m_settings.m_BoardBodyColor ); + m_object_container.Add( objPtr ); + } + } + + // Add cylinders of the board body to container + // Note: This is actually a workarround for the holes in the board. + // The issue is because if a hole is in a border of a divided polygon ( ex + // a polygon or dummyblock) it will cut also the render of the hole. + // So this will add a full hole. + // In fact, that is not need if the hole have copper. + // ///////////////////////////////////////////////////////////////////////// + if( !m_settings.GetThroughHole_Outer().GetList().empty() ) + { + const LIST_OBJECT2D &holeList = m_settings.GetThroughHole_Outer().GetList(); + + for( LIST_OBJECT2D::const_iterator hole = holeList.begin(); + hole != holeList.end(); + ++hole ) + { + const COBJECT2D *hole2d = static_cast(*hole); + + switch( hole2d->GetObjectType() ) + { + case OBJ2D_FILLED_CIRCLE: + { + const float radius = hole2d->GetBBox().GetExtent().x * 0.5f * 0.999f; + + CVCYLINDER *objPtr = new CVCYLINDER( + hole2d->GetCentroid(), + NextFloatDown( m_settings.GetLayerBottomZpos3DU( F_Cu ) ), + NextFloatUp( m_settings.GetLayerBottomZpos3DU( B_Cu ) ), + radius ); + + objPtr->SetMaterial( &m_materials.m_EpoxyBoard ); + objPtr->SetColor( (SFVEC3F)m_settings.m_BoardBodyColor ); + + m_object_container.Add( objPtr ); + } + break; + + default: + break; + } + } + } + } + } + + + // Add layers maps (except B_Mask and F_Mask) + // ///////////////////////////////////////////////////////////////////////// + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf("Add layers maps...\n"); +#endif + + for( MAP_CONTAINER_2D::const_iterator ii = m_settings.GetMapLayers().begin(); + ii != m_settings.GetMapLayers().end(); + ++ii ) + { + LAYER_ID layer_id = static_cast(ii->first); + + // Mask kayers are not processed here because they are a special case + if( (layer_id == B_Mask) || (layer_id == F_Mask) ) + continue; + + CMATERIAL *materialLayer = &m_materials.m_SilkS; + SFVEC3F layerColor = SFVEC3F( 0.0f, 0.0f, 0.0f ); + + switch( layer_id ) + { + case B_Adhes: + case F_Adhes: + break; + + case B_Paste: + case F_Paste: + materialLayer = &m_materials.m_Paste; + + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + layerColor = m_settings.m_SolderPasteColor; + else + layerColor = m_settings.GetLayerColor( layer_id ); + break; + + case B_SilkS: + case F_SilkS: + materialLayer = &m_materials.m_SilkS; + + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + layerColor = m_settings.m_SilkScreenColor; + else + layerColor = m_settings.GetLayerColor( layer_id ); + break; + + case Dwgs_User: + case Cmts_User: + case Eco1_User: + case Eco2_User: + case Edge_Cuts: + case Margin: + break; + + case B_CrtYd: + case F_CrtYd: + break; + + case B_Fab: + case F_Fab: + break; + + default: + materialLayer = &m_materials.m_Copper; + + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + layerColor = m_settings.m_CopperColor; + else + layerColor = m_settings.GetLayerColor( layer_id ); + break; + } + + const CBVHCONTAINER2D *container2d = static_cast(ii->second); + const LIST_OBJECT2D &listObject2d = container2d->GetList(); + + for( LIST_OBJECT2D::const_iterator itemOnLayer = listObject2d.begin(); + itemOnLayer != listObject2d.end(); + ++itemOnLayer ) + { + const COBJECT2D *object2d_A = static_cast(*itemOnLayer); + + // not yet used / implemented (can be used in future to clip the objects in the board borders + COBJECT2D *object2d_C = CSGITEM_FULL; + + std::vector *object2d_B = CSGITEM_EMPTY; + + if( m_settings.GetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES ) ) + { + object2d_B = new std::vector(); + + // Check if there are any layerhole that intersects this object + // Eg: a segment is cutted by a via hole or THT hole. + // ///////////////////////////////////////////////////////////// + const MAP_CONTAINER_2D &layerHolesMap = m_settings.GetMapLayersHoles(); + + if( layerHolesMap.find(layer_id) != layerHolesMap.end() ) + { + MAP_CONTAINER_2D::const_iterator ii_hole = layerHolesMap.find(layer_id); + + const CBVHCONTAINER2D *containerLayerHoles2d = + static_cast(ii_hole->second); + + + CONST_LIST_OBJECT2D intersectionList; + containerLayerHoles2d->GetListObjectsIntersects( object2d_A->GetBBox(), + intersectionList ); + + if( !intersectionList.empty() ) + { + for( CONST_LIST_OBJECT2D::const_iterator holeOnLayer = + intersectionList.begin(); + holeOnLayer != intersectionList.end(); + ++holeOnLayer ) + { + const COBJECT2D *hole2d = static_cast(*holeOnLayer); + + //if( object2d_A->Intersects( hole2d->GetBBox() ) ) + //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) + object2d_B->push_back( hole2d ); + } + } + } + + // Check if there are any THT that intersects this object + // ///////////////////////////////////////////////////////////// + if( !m_settings.GetThroughHole_Outer().GetList().empty() ) + { + CONST_LIST_OBJECT2D intersectionList; + + m_settings.GetThroughHole_Outer().GetListObjectsIntersects( + object2d_A->GetBBox(), + intersectionList ); + + if( !intersectionList.empty() ) + { + for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin(); + hole != intersectionList.end(); + ++hole ) + { + const COBJECT2D *hole2d = static_cast(*hole); + + //if( object2d_A->Intersects( hole2d->GetBBox() ) ) + //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) + object2d_B->push_back( hole2d ); + } + } + } + + if( object2d_B->empty() ) + { + delete object2d_B; + object2d_B = CSGITEM_EMPTY; + } + } + + if( (object2d_B == CSGITEM_EMPTY) && + (object2d_C == CSGITEM_FULL) ) + { +#if 0 + create_3d_object_from( m_object_container, + object2d_A, + m_settings.GetLayerBottomZpos3DU( layer_id ), + m_settings.GetLayerTopZpos3DU( layer_id ), + materialLayer, + layerColor ); +#else + CLAYERITEM *objPtr = new CLAYERITEM( object2d_A, + m_settings.GetLayerBottomZpos3DU( layer_id ), + m_settings.GetLayerTopZpos3DU( layer_id ) ); + objPtr->SetMaterial( materialLayer ); + objPtr->SetColor( layerColor ); + m_object_container.Add( objPtr ); +#endif + } + else + { +#if 1 + CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A, + object2d_B, + object2d_C, + object2d_A->GetBoardItem() ); + m_containerWithObjectsToDelete.Add( itemCSG2d ); + + CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d, + m_settings.GetLayerBottomZpos3DU( layer_id ), + m_settings.GetLayerTopZpos3DU( layer_id ) ); + + objPtr->SetMaterial( materialLayer ); + objPtr->SetColor( layerColor ); + + m_object_container.Add( objPtr ); +#endif + } + } + }// for each layer on map + + + + // Add Mask layer + // Solder mask layers are "negative" layers so the elements that we have + // (in the container) should remove the board outline. + // We will check for all objects in the outline if it intersects any object + // in the layer container and also any hole. + // ///////////////////////////////////////////////////////////////////////// + if( m_settings.GetFlag( FL_SOLDERMASK ) && + (m_outlineBoard2dObjects->GetList().size() >= 1) ) + { + CMATERIAL *materialLayer = &m_materials.m_SolderMask; + + for( MAP_CONTAINER_2D::const_iterator ii = m_settings.GetMapLayers().begin(); + ii != m_settings.GetMapLayers().end(); + ++ii ) + { + LAYER_ID layer_id = static_cast(ii->first); + + const CBVHCONTAINER2D *containerLayer2d = + static_cast(ii->second); + + // Only get the Solder mask layers + if( !((layer_id == B_Mask) || (layer_id == F_Mask)) ) + continue; + + SFVEC3F layerColor; + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + layerColor = m_settings.m_SolderMaskColor; + else + layerColor = m_settings.GetLayerColor( layer_id ); + + const float zLayerMin = m_settings.GetLayerBottomZpos3DU( layer_id ); + const float zLayerMax = m_settings.GetLayerTopZpos3DU( layer_id ); + + // Get the outline board objects + const LIST_OBJECT2D &listObjects = m_outlineBoard2dObjects->GetList(); + + for( LIST_OBJECT2D::const_iterator object2d_iterator = listObjects.begin(); + object2d_iterator != listObjects.end(); + ++object2d_iterator ) + { + const COBJECT2D *object2d_A = static_cast(*object2d_iterator); + + std::vector *object2d_B = new std::vector(); + + // Check if there are any THT that intersects this outline object part + if( !m_settings.GetThroughHole_Outer().GetList().empty() ) + { + + CONST_LIST_OBJECT2D intersectionList; + + m_settings.GetThroughHole_Outer().GetListObjectsIntersects( + object2d_A->GetBBox(), + intersectionList ); + + if( !intersectionList.empty() ) + { + for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin(); + hole != intersectionList.end(); + ++hole ) + { + const COBJECT2D *hole2d = static_cast(*hole); + + if( object2d_A->Intersects( hole2d->GetBBox() ) ) + //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) + object2d_B->push_back( hole2d ); + } + } + } + + // Check if there are any objects in the layer to subtract with the + // corrent object + if( !containerLayer2d->GetList().empty() ) + { + CONST_LIST_OBJECT2D intersectionList; + + containerLayer2d->GetListObjectsIntersects( object2d_A->GetBBox(), + intersectionList ); + + if( !intersectionList.empty() ) + { + for( CONST_LIST_OBJECT2D::const_iterator obj = intersectionList.begin(); + obj != intersectionList.end(); + ++obj ) + { + const COBJECT2D *obj2d = static_cast(*obj); + + //if( object2d_A->Intersects( obj2d->GetBBox() ) ) + //if( object2d_A->GetBBox().Intersects( obj2d->GetBBox() ) ) + object2d_B->push_back( obj2d ); + } + } + } + + if( object2d_B->empty() ) + { + delete object2d_B; + object2d_B = CSGITEM_EMPTY; + } + + if( object2d_B == CSGITEM_EMPTY ) + { + #if 0 + create_3d_object_from( m_object_container, + object2d_A, + zLayerMin, + zLayerMax, + materialLayer, + layerColor ); + #else + CLAYERITEM *objPtr = new CLAYERITEM( object2d_A, + zLayerMin, + zLayerMax ); + + objPtr->SetMaterial( materialLayer ); + objPtr->SetColor( layerColor ); + + m_object_container.Add( objPtr ); + #endif + } + else + { + CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A, + object2d_B, + CSGITEM_FULL, + object2d_A->GetBoardItem() ); + + m_containerWithObjectsToDelete.Add( itemCSG2d ); + + CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d, + zLayerMin, + zLayerMax ); + objPtr->SetMaterial( materialLayer ); + objPtr->SetColor( layerColor ); + + m_object_container.Add( objPtr ); + } + } + } + } + + add_3D_vias_and_pads_to_container(); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_endConvertTime = GetRunningMicroSecs(); + unsigned stats_startLoad3DmodelsTime = stats_endConvertTime; +#endif + + + load_3D_models(); + + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_endLoad3DmodelsTime = GetRunningMicroSecs(); +#endif + + // Add floor + // ///////////////////////////////////////////////////////////////////////// + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_BACKFLOOR ) ) + { + CBBOX boardBBox = m_settings.GetBBox3DU(); + + if( boardBBox.IsInitialized() ) + { + boardBBox.Scale( 3.0f ); + + if( m_object_container.GetList().size() > 0 ) + { + CBBOX containerBBox = m_object_container.GetBBox(); + + containerBBox.Scale( 1.3f ); + + const SFVEC3F centerBBox = containerBBox.GetCenter(); + + const float minZ = glm::min( containerBBox.Min().z, + boardBBox.Min().z ); + + const SFVEC3F v1 = SFVEC3F( -RANGE_SCALE_3D * 4.0f, + -RANGE_SCALE_3D * 4.0f, + minZ ) + + SFVEC3F( centerBBox.x, + centerBBox.y, + 0.0f ); + + const SFVEC3F v3 = SFVEC3F( +RANGE_SCALE_3D * 4.0f, + +RANGE_SCALE_3D * 4.0f, + minZ ) + + SFVEC3F( centerBBox.x, + centerBBox.y, + 0.0f ); + + const SFVEC3F v2 = SFVEC3F( v1.x, v3.y, v1.z ); + const SFVEC3F v4 = SFVEC3F( v3.x, v1.y, v1.z ); + + CTRIANGLE *newTriangle1 = new CTRIANGLE( v1, v2, v3 ); + CTRIANGLE *newTriangle2 = new CTRIANGLE( v3, v4, v1 ); + + m_object_container.Add( newTriangle1 ); + m_object_container.Add( newTriangle2 ); + + newTriangle1->SetMaterial( (const CMATERIAL *)&m_materials.m_Floor ); + newTriangle2->SetMaterial( (const CMATERIAL *)&m_materials.m_Floor ); + + newTriangle1->SetColor( (SFVEC3F)m_settings.m_BgColorTop ); + newTriangle2->SetColor( (SFVEC3F)m_settings.m_BgColorTop ); + } + } + } + + + // Create an accelerator + // ///////////////////////////////////////////////////////////////////////// + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_startAcceleratorTime = GetRunningMicroSecs(); +#endif + + if( m_accelerator ) + { + delete m_accelerator; + } + m_accelerator = 0; + + //m_accelerator = new CGRID( m_object_container ); + m_accelerator = new CBVH_PBRT( m_object_container ); + +#ifdef PRINT_STATISTICS_3D_VIEWER + unsigned stats_endAcceleratorTime = GetRunningMicroSecs(); +#endif + + setupMaterials(); + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "C3D_RENDER_RAYTRACING::reload times:\n" ); + printf( " Reload board: %.3f ms\n", (float)( stats_endReloadTime - + stats_startReloadTime ) / + + 1000.0f ); + printf( " Convert to 3D objects: %.3f ms\n", (float)( stats_endConvertTime - + stats_startConvertTime ) / + 1000.0f ); + printf( " Accelerator construction: %.3f ms\n", (float)( stats_endAcceleratorTime - + stats_startAcceleratorTime ) / + 1000.0f ); + printf( " Load and add 3D models: %.3f ms\n", (float)( stats_endLoad3DmodelsTime - + stats_startLoad3DmodelsTime ) / + 1000.0f ); + printf( "Optimizations\n" ); + + printf( " m_stats_converted_dummy_to_plane: %u\n", + m_stats_converted_dummy_to_plane ); + + printf( " m_stats_converted_roundsegment2d_to_roundsegment: %u\n", + m_stats_converted_roundsegment2d_to_roundsegment ); + + COBJECT2D_STATS::Instance().PrintStats(); + COBJECT3D_STATS::Instance().PrintStats(); +#endif + + if( aStatusTextReporter ) + { + // Calculation time in seconds + const double calculation_time = (double)( GetRunningMicroSecs() - + stats_startReloadTime ) / 1e6; + + aStatusTextReporter->Report( wxString::Format( _( "Reload time %.3f s" ), + calculation_time ) ); + } +} + + + +// Based on draw3DViaHole from +// 3d_draw_helper_functions.cpp +void C3D_RENDER_RAYTRACING::insert3DViaHole( const VIA* aVia ) +{ + LAYER_ID top_layer, bottom_layer; + int radiusBUI = (aVia->GetDrillValue() / 2); + + aVia->LayerPair( &top_layer, &bottom_layer ); + + float topZ = m_settings.GetLayerBottomZpos3DU( top_layer ) + + m_settings.GetCopperThickness3DU(); + + float botZ = m_settings.GetLayerBottomZpos3DU( bottom_layer ) - + m_settings.GetCopperThickness3DU(); + + const SFVEC2F center = SFVEC2F( aVia->GetStart().x * m_settings.BiuTo3Dunits(), + -aVia->GetStart().y * m_settings.BiuTo3Dunits() ); + + CRING2D *ring = new CRING2D( center, + radiusBUI * m_settings.BiuTo3Dunits(), + ( radiusBUI + m_settings.GetCopperThicknessBIU() ) * + m_settings.BiuTo3Dunits(), + *aVia ); + + m_containerWithObjectsToDelete.Add( ring ); + + + CLAYERITEM *objPtr = new CLAYERITEM( ring, topZ, botZ ); + + objPtr->SetMaterial( &m_materials.m_Copper ); + + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + objPtr->SetColor( (SFVEC3F)m_settings.m_CopperColor ); + else + objPtr->SetColor( m_settings.GetItemColor( VIAS_VISIBLE + aVia->GetViaType() ) ); + + m_object_container.Add( objPtr ); +} + + +// Based on draw3DPadHole from +// 3d_draw_helper_functions.cpp +void C3D_RENDER_RAYTRACING::insert3DPadHole( const D_PAD* aPad ) +{ + const COBJECT2D *object2d_A = NULL; + + SFVEC3F objColor; + + if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) + objColor = (SFVEC3F)m_settings.m_CopperColor; + else + objColor = m_settings.GetItemColor( PADS_VISIBLE ); + + const wxSize drillsize = aPad->GetDrillSize(); + const bool hasHole = drillsize.x && drillsize.y; + + if( !hasHole ) + return; + + const float topZ = m_settings.GetLayerBottomZpos3DU( F_Cu ) + + m_settings.GetCopperThickness3DU(); + + const float botZ = m_settings.GetLayerBottomZpos3DU( B_Cu ) - + m_settings.GetCopperThickness3DU(); + + if( drillsize.x == drillsize.y ) // usual round hole + { + SFVEC2F center = SFVEC2F( aPad->GetPosition().x * m_settings.BiuTo3Dunits(), + -aPad->GetPosition().y * m_settings.BiuTo3Dunits() ); + + CRING2D *ring = new CRING2D( center, + ( drillsize.x / 2 ) * m_settings.BiuTo3Dunits(), + ( ( drillsize.x / 2 ) + + m_settings.GetCopperThicknessBIU() ) * + m_settings.BiuTo3Dunits(), + *aPad ); + + m_containerWithObjectsToDelete.Add( ring ); + + object2d_A = ring; + } + else // Oblong hole + { + wxPoint ends_offset; + int width; + + if( drillsize.x > drillsize.y ) // Horizontal oval + { + ends_offset.x = ( drillsize.x - drillsize.y ) / 2; + width = drillsize.y; + } + else // Vertical oval + { + ends_offset.y = ( drillsize.y - drillsize.x ) / 2; + width = drillsize.x; + } + + RotatePoint( &ends_offset, aPad->GetOrientation() ); + + wxPoint start = aPad->GetPosition() + ends_offset; + wxPoint end = aPad->GetPosition() - ends_offset; + + CROUNDSEGMENT2D *innerSeg = new CROUNDSEGMENT2D( + SFVEC2F( start.x * m_settings.BiuTo3Dunits(), + -start.y * m_settings.BiuTo3Dunits() ), + SFVEC2F( end.x * m_settings.BiuTo3Dunits(), + -end.y * m_settings.BiuTo3Dunits() ), + width * m_settings.BiuTo3Dunits(), + *aPad ); + + CROUNDSEGMENT2D *outterSeg = new CROUNDSEGMENT2D( + SFVEC2F( start.x * m_settings.BiuTo3Dunits(), + -start.y * m_settings.BiuTo3Dunits() ), + SFVEC2F( end.x * m_settings.BiuTo3Dunits(), + -end.y * m_settings.BiuTo3Dunits() ), + ( width + m_settings.GetCopperThicknessBIU() * 2 ) * + m_settings.BiuTo3Dunits(), + *aPad ); + + // NOTE: the round segment width is the "diameter", so we double the thickness + + std::vector *object2d_B = new std::vector(); + object2d_B->push_back( innerSeg ); + + CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( outterSeg, + object2d_B, + CSGITEM_FULL, + *aPad ); + + m_containerWithObjectsToDelete.Add( itemCSG2d ); + m_containerWithObjectsToDelete.Add( innerSeg ); + m_containerWithObjectsToDelete.Add( outterSeg ); + + object2d_A = itemCSG2d; + } + + + if( object2d_A ) + { + std::vector *object2d_B = new std::vector(); + + // Check if there are any other THT that intersects this hole + // It will use the non inflated holes + if( !m_settings.GetThroughHole_Inner().GetList().empty() ) + { + + CONST_LIST_OBJECT2D intersectionList; + m_settings.GetThroughHole_Inner().GetListObjectsIntersects( object2d_A->GetBBox(), + intersectionList ); + + if( !intersectionList.empty() ) + { + for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin(); + hole != intersectionList.end(); + ++hole ) + { + const COBJECT2D *hole2d = static_cast(*hole); + + if( object2d_A->Intersects( hole2d->GetBBox() ) ) + //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) + object2d_B->push_back( hole2d ); + } + } + } + + if( object2d_B->empty() ) + { + delete object2d_B; + object2d_B = CSGITEM_EMPTY; + } + + if( object2d_B == CSGITEM_EMPTY ) + { + CLAYERITEM *objPtr = new CLAYERITEM( object2d_A, topZ, botZ ); + + objPtr->SetMaterial( &m_materials.m_Copper ); + objPtr->SetColor( objColor ); + m_object_container.Add( objPtr ); + } + else + { + CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A, + object2d_B, + CSGITEM_FULL, + (const BOARD_ITEM &)*aPad ); + + m_containerWithObjectsToDelete.Add( itemCSG2d ); + + CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d, topZ, botZ ); + + objPtr->SetMaterial( &m_materials.m_Copper ); + objPtr->SetColor( objColor ); + + m_object_container.Add( objPtr ); + } + } +} + + +void C3D_RENDER_RAYTRACING::add_3D_vias_and_pads_to_container() +{ + // Insert plated vertical holes inside the board + // ///////////////////////////////////////////////////////////////////////// + + // Insert vias holes (vertical cylinders) + for( const TRACK* track = m_settings.GetBoard()->m_Track; + track; + track = track->Next() ) + { + if( track->Type() == PCB_VIA_T ) + { + const VIA *via = static_cast(track); + insert3DViaHole( via ); + } + } + + // Insert pads holes (vertical cylinders) + for( const MODULE* module = m_settings.GetBoard()->m_Modules; + module; + module = module->Next() ) + { + for( const D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) + if( pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED ) + { + insert3DPadHole( pad ); + } + } +} + + +void C3D_RENDER_RAYTRACING::load_3D_models() +{ + // Go for all modules + for( const MODULE* module = m_settings.GetBoard()->m_Modules; + module; + module = module->Next() ) + { + if( (!module->Models().empty() ) && + m_settings.ShouldModuleBeDisplayed( (MODULE_ATTR_T)module->GetAttributes() ) ) + { + double zpos = m_settings.GetModulesZcoord3DIU( module->IsFlipped() ); + + wxPoint pos = module->GetPosition(); + + glm::mat4 moduleMatrix = glm::mat4(); + + moduleMatrix = glm::translate( moduleMatrix, + SFVEC3F( pos.x * m_settings.BiuTo3Dunits(), + -pos.y * m_settings.BiuTo3Dunits(), + zpos ) ); + + if( module->GetOrientation() ) + { + moduleMatrix = glm::rotate( moduleMatrix, + ( (float)(module->GetOrientation() / 10.0f) / 180.0f ) * + glm::pi(), + SFVEC3F( 0.0f, 0.0f, 1.0f ) ); + } + + + if( module->IsFlipped() ) + { + moduleMatrix = glm::rotate( moduleMatrix, + glm::pi(), + SFVEC3F( 0.0f, 1.0f, 0.0f ) ); + + moduleMatrix = glm::rotate( moduleMatrix, + glm::pi(), + SFVEC3F( 0.0f, 0.0f, 1.0f ) ); + } + + const double modelunit_to_3d_units_factor = m_settings.BiuTo3Dunits() * + UNITS3D_TO_UNITSPCB; + + moduleMatrix = glm::scale( moduleMatrix, + SFVEC3F( modelunit_to_3d_units_factor, + modelunit_to_3d_units_factor, + modelunit_to_3d_units_factor ) ); + + + // Get the list of model files for this model + std::list::const_iterator sM = module->Models().begin(); + std::list::const_iterator eM = module->Models().end(); + + while( sM != eM ) + { + // get it from cache + const S3DMODEL *modelPtr = + m_settings.Get3DCacheManager()->GetModel( sM->m_Filename ); + + // only add it if the return is not NULL + if( modelPtr ) + { + glm::mat4 modelMatrix = moduleMatrix; + + modelMatrix = glm::translate( modelMatrix, + SFVEC3F( sM->m_Offset.x * 25.4f, + sM->m_Offset.y * 25.4f, + sM->m_Offset.z * 25.4f ) ); + + modelMatrix = glm::rotate( modelMatrix, + (float)-( sM->m_Rotation.z / 180.0f ) * + glm::pi(), + SFVEC3F( 0.0f, 0.0f, 1.0f ) ); + + modelMatrix = glm::rotate( modelMatrix, + (float)-( sM->m_Rotation.y / 180.0f ) * + glm::pi(), + SFVEC3F( 0.0f, 1.0f, 0.0f ) ); + + modelMatrix = glm::rotate( modelMatrix, + (float)-( sM->m_Rotation.x / 180.0f ) * + glm::pi(), + SFVEC3F( 1.0f, 0.0f, 0.0f ) ); + + modelMatrix = glm::scale( modelMatrix, + SFVEC3F( sM->m_Scale.x, + sM->m_Scale.y, + sM->m_Scale.z ) ); + + add_3D_models( modelPtr, modelMatrix ); + } + + ++sM; + } + } + } +} + + +void C3D_RENDER_RAYTRACING::add_3D_models( const S3DMODEL *a3DModel, + const glm::mat4 &aModelMatrix ) +{ + + // Validate a3DModel pointers + wxASSERT( a3DModel != NULL ); + + if( a3DModel == NULL ) + return; + + wxASSERT( a3DModel->m_Materials != NULL ); + wxASSERT( a3DModel->m_Meshes != NULL ); + wxASSERT( a3DModel->m_MaterialsSize > 0 ); + wxASSERT( a3DModel->m_MeshesSize > 0 ); + + if( (a3DModel->m_Materials != NULL) && (a3DModel->m_Meshes != NULL) && + (a3DModel->m_MaterialsSize > 0) && (a3DModel->m_MeshesSize > 0) ) + { + + MODEL_MATERIALS *materialVector; + + // Try find if the materials already exists in the map list + if( m_model_materials.find( a3DModel ) != m_model_materials.end() ) + { + // Found it, so get the pointer + materialVector = &m_model_materials[a3DModel]; + } + else + { + // Materials was not found in the map, so it will create a new for + // this model. + + m_model_materials[a3DModel] = MODEL_MATERIALS(); + materialVector = &m_model_materials[a3DModel]; + + materialVector->resize( a3DModel->m_MaterialsSize ); + + for( unsigned int imat = 0; + imat < a3DModel->m_MaterialsSize; + ++imat ) + { + if( m_settings.MaterialModeGet() == MATERIAL_MODE_NORMAL ) + { + const SMATERIAL &material = a3DModel->m_Materials[imat]; + + float reflectionFactor = glm::clamp( material.m_Shininess * + 0.75f - 0.125f, + 0.0f, + 1.0f ); + + (*materialVector)[imat] = CBLINN_PHONG_MATERIAL( + material.m_Ambient, + material.m_Emissive, + material.m_Specular, + material.m_Shininess * 180.0f, + material.m_Transparency, + reflectionFactor ); + } + else + { + (*materialVector)[imat] = CBLINN_PHONG_MATERIAL( SFVEC3F( 0.2f ), + SFVEC3F( 0.0f ), + SFVEC3F( 0.0f ), + 0.0f, + 0.0f, + 0.0f ); + } + } + } + + const glm::mat3 normalMatrix = glm::transpose( glm::inverse( glm::mat3( aModelMatrix ) ) ); + + for( unsigned int mesh_i = 0; + mesh_i < a3DModel->m_MeshesSize; + ++mesh_i ) + { + const SMESH &mesh = a3DModel->m_Meshes[mesh_i]; + + // Validate the mesh pointers + wxASSERT( mesh.m_Positions != NULL ); + wxASSERT( mesh.m_FaceIdx != NULL ); + wxASSERT( mesh.m_Normals != NULL ); + wxASSERT( mesh.m_FaceIdxSize > 0 ); + wxASSERT( (mesh.m_FaceIdxSize % 3) == 0 ); + + + if( (mesh.m_Positions != NULL) && + (mesh.m_Normals != NULL) && + (mesh.m_FaceIdx != NULL) && + (mesh.m_FaceIdxSize > 0) && + (mesh.m_VertexSize > 0) && + ((mesh.m_FaceIdxSize % 3) == 0) && + (mesh.m_MaterialIdx < a3DModel->m_MaterialsSize) ) + { + const CBLINN_PHONG_MATERIAL &blinn_material = (*materialVector)[mesh.m_MaterialIdx]; + + // Add all face triangles + for( unsigned int faceIdx = 0; + faceIdx < mesh.m_FaceIdxSize; + faceIdx += 3 ) + { + const unsigned int idx0 = mesh.m_FaceIdx[faceIdx + 0]; + const unsigned int idx1 = mesh.m_FaceIdx[faceIdx + 1]; + const unsigned int idx2 = mesh.m_FaceIdx[faceIdx + 2]; + + wxASSERT( idx0 < mesh.m_VertexSize ); + wxASSERT( idx1 < mesh.m_VertexSize ); + wxASSERT( idx2 < mesh.m_VertexSize ); + + if( ( idx0 < mesh.m_VertexSize ) && + ( idx1 < mesh.m_VertexSize ) && + ( idx2 < mesh.m_VertexSize ) ) + { + const SFVEC3F &v0 = mesh.m_Positions[idx0]; + const SFVEC3F &v1 = mesh.m_Positions[idx1]; + const SFVEC3F &v2 = mesh.m_Positions[idx2]; + + const SFVEC3F &n0 = mesh.m_Normals[idx0]; + const SFVEC3F &n1 = mesh.m_Normals[idx1]; + const SFVEC3F &n2 = mesh.m_Normals[idx2]; + + // Transform vertex with the model matrix + const SFVEC3F vt0 = SFVEC3F( aModelMatrix * glm::vec4( v0, 1.0f) ); + const SFVEC3F vt1 = SFVEC3F( aModelMatrix * glm::vec4( v1, 1.0f) ); + const SFVEC3F vt2 = SFVEC3F( aModelMatrix * glm::vec4( v2, 1.0f) ); + + const SFVEC3F nt0 = glm::normalize( SFVEC3F( normalMatrix * n0 ) ); + const SFVEC3F nt1 = glm::normalize( SFVEC3F( normalMatrix * n1 ) ); + const SFVEC3F nt2 = glm::normalize( SFVEC3F( normalMatrix * n2 ) ); + + CTRIANGLE *newTriangle = new CTRIANGLE( vt0, vt2, vt1, + nt0, nt2, nt1 ); + + + + m_object_container.Add( newTriangle ); + newTriangle->SetMaterial( (const CMATERIAL *)&blinn_material ); + + if( mesh.m_Color == NULL ) + { + const SFVEC3F diffuseColor = + a3DModel->m_Materials[mesh.m_MaterialIdx].m_Diffuse; + + if( m_settings.MaterialModeGet() == MATERIAL_MODE_CAD_MODE ) + newTriangle->SetColor( MaterialDiffuseToColorCAD( diffuseColor ) ); + else + newTriangle->SetColor( diffuseColor ); + } + else + { + if( m_settings.MaterialModeGet() == MATERIAL_MODE_CAD_MODE ) + newTriangle->SetColor( + MaterialDiffuseToColorCAD( mesh.m_Color[idx0] ), + MaterialDiffuseToColorCAD( mesh.m_Color[idx1] ), + MaterialDiffuseToColorCAD( mesh.m_Color[idx2] ) ); + else + newTriangle->SetColor( mesh.m_Color[idx0], + mesh.m_Color[idx1], + mesh.m_Color[idx2] ); + } + } + } + } + } + } +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp new file mode 100644 index 0000000000..3d96e8a36d --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp @@ -0,0 +1,2328 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file c3d_render_raytracing.cpp + * @brief + */ + +#include + +#include "c3d_render_raytracing.h" +#include "mortoncodes.h" +#include "../ccolorrgb.h" +#include "3d_fastmath.h" +#include "3d_math.h" +#include "../common_ogl/ogl_utils.h" + +#ifdef _OPENMP +#include +#endif + +C3D_RENDER_RAYTRACING::C3D_RENDER_RAYTRACING( CINFO3D_VISU &aSettings ) : + C3D_RENDER_BASE( aSettings ), + m_postshader_ssao( aSettings.CameraGet() ) +{ + wxLogTrace( m_logTrace, wxT( "C3D_RENDER_RAYTRACING::C3D_RENDER_RAYTRACING" ) ); + + m_opengl_support_vertex_buffer_object = false; + m_pboId = GL_NONE; + m_pboDataSize = 0; + m_accelerator = NULL; + m_stats_converted_dummy_to_plane = 0; + m_stats_converted_roundsegment2d_to_roundsegment = 0; + m_oldWindowsSize.x = 0; + m_oldWindowsSize.y = 0; + m_outlineBoard2dObjects = NULL; + m_firstHitinfo = NULL; + m_shaderBuffer = NULL; + m_camera_light = NULL; + + m_xoffset = 0; + m_yoffset = 0; + + m_isPreview = false; + m_rt_render_state = RT_RENDER_STATE_MAX; // Set to an initial invalid state +} + + +C3D_RENDER_RAYTRACING::~C3D_RENDER_RAYTRACING() +{ + wxLogTrace( m_logTrace, wxT( "C3D_RENDER_RAYTRACING::~C3D_RENDER_RAYTRACING" ) ); + + delete m_accelerator; + m_accelerator = NULL; + + delete m_outlineBoard2dObjects; + m_outlineBoard2dObjects = NULL; + + delete m_shaderBuffer; + m_shaderBuffer = NULL; + + opengl_delete_pbo(); +} + + +int C3D_RENDER_RAYTRACING::GetWaitForEditingTimeOut() +{ + return 1000; // ms +} + + +void C3D_RENDER_RAYTRACING::opengl_delete_pbo() +{ + // Delete PBO if it was created + if( m_opengl_support_vertex_buffer_object ) + { + if( glIsBufferARB( m_pboId ) ) + glDeleteBuffers( 1, &m_pboId ); + + m_pboId = GL_NONE; + } +} + + +void C3D_RENDER_RAYTRACING::SetCurWindowSize( const wxSize &aSize ) +{ + if( m_windowSize != aSize ) + { + m_windowSize = aSize; + glViewport( 0, 0, m_windowSize.x, m_windowSize.y ); + + initializeNewWindowSize(); + } +} + + +void C3D_RENDER_RAYTRACING::restart_render_state() +{ + m_stats_start_rendering_time = GetRunningMicroSecs(); + + m_rt_render_state = RT_RENDER_STATE_TRACING; + m_nrBlocksRenderProgress = 0; + + m_postshader_ssao.InitFrame(); + + m_blockPositionsWasProcessed.resize( m_blockPositions.size() ); + + // Mark the blocks not processed yet + std::fill( m_blockPositionsWasProcessed.begin(), + m_blockPositionsWasProcessed.end(), + false ); +} + + +static inline void SetPixel( GLubyte *p, const CCOLORRGB &v ) +{ + p[0] = v.c[0]; p[1] = v.c[1]; p[2] = v.c[2]; p[3] = 255; +} + + +bool C3D_RENDER_RAYTRACING::Redraw( bool aIsMoving, REPORTER *aStatusTextReporter ) +{ + bool requestRedraw = false; + + // Initialize openGL if need + // ///////////////////////////////////////////////////////////////////////// + if( !m_is_opengl_initialized ) + { + if( !initializeOpenGL() ) + return false; + + //aIsMoving = true; + requestRedraw = true; + + // It will assign the first time the windows size, so it will now + // revert to preview mode the first time the Redraw is called + m_oldWindowsSize = m_windowSize; + initialize_block_positions(); + } + + + // Reload board if it was requested + // ///////////////////////////////////////////////////////////////////////// + if( m_reloadRequested ) + { + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Loading..." ) ); + + //aIsMoving = true; + requestRedraw = true; + reload( aStatusTextReporter ); + } + + + // Recalculate constants if windows size was changed + // ///////////////////////////////////////////////////////////////////////// + if( m_windowSize != m_oldWindowsSize ) + { + m_oldWindowsSize = m_windowSize; + aIsMoving = true; + requestRedraw = true; + + initialize_block_positions(); + } + + + // Clear buffers + // ///////////////////////////////////////////////////////////////////////// + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glClearDepth( 1.0f ); + glClearStencil( 0x00 ); + glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + + // 4-byte pixel alignment + glPixelStorei( GL_UNPACK_ALIGNMENT, 4 ); + + glDisable( GL_STENCIL_TEST ); + glDisable( GL_LIGHTING ); + glDisable( GL_COLOR_MATERIAL ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + + + const bool was_camera_changed = m_settings.CameraGet().ParametersChanged(); + + if( requestRedraw || aIsMoving || was_camera_changed ) + m_rt_render_state = RT_RENDER_STATE_MAX; // Set to an invalid state, + // so it will restart again latter + + + // This will only render if need, otherwise it will redraw the PBO on the screen again + if( aIsMoving || was_camera_changed ) + { + // Set head light (camera view light) with the oposite direction of the camera + if( m_camera_light ) + m_camera_light->SetDirection( -m_settings.CameraGet().GetDir() ); + + OGL_DrawBackground( SFVEC3F(m_settings.m_BgColorTop), + SFVEC3F(m_settings.m_BgColorBot) ); + + // Bind PBO + glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId ); + + // Get the PBO pixel pointer to write the data + GLubyte *ptrPBO = (GLubyte *)glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, + GL_WRITE_ONLY_ARB ); + + if( ptrPBO ) + { + render_preview( ptrPBO ); + + // release pointer to mapping buffer, this initialize the coping to PBO + glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB ); + } + + glWindowPos2i( m_xoffset, m_yoffset ); + } + else + { + // Bind PBO + glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId ); + + if( m_rt_render_state != RT_RENDER_STATE_FINISH ) + { + // Get the PBO pixel pointer to write the data + GLubyte *ptrPBO = (GLubyte *)glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, + GL_WRITE_ONLY_ARB ); + + if( ptrPBO ) + { + render( ptrPBO, aStatusTextReporter ); + + if( m_rt_render_state != RT_RENDER_STATE_FINISH ) + requestRedraw = true; + + // release pointer to mapping buffer, this initialize the coping to PBO + glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); + } + } + + if( m_rt_render_state == RT_RENDER_STATE_FINISH ) + { + glClear( GL_COLOR_BUFFER_BIT ); + // Options if we want draw background instead + //OGL_DrawBackground( SFVEC3F(m_settings.m_BgColorTop), + // SFVEC3F(m_settings.m_BgColorBot) ); + } + + glWindowPos2i( m_xoffset, m_yoffset ); + } + + // This way it will blend the progress rendering with the last buffer. eg: + // if it was called after a openGL. + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_ALPHA_TEST ); + + glDrawPixels( m_realBufferSize.x, + m_realBufferSize.y, + GL_RGBA, + GL_UNSIGNED_BYTE, + 0 ); + + glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 ); + + return requestRedraw; +} + + +void C3D_RENDER_RAYTRACING::render( GLubyte *ptrPBO , REPORTER *aStatusTextReporter ) +{ + if( (m_rt_render_state == RT_RENDER_STATE_FINISH) || + (m_rt_render_state >= RT_RENDER_STATE_MAX) ) + { + restart_render_state(); + + if( m_camera_light ) + m_camera_light->SetDirection( -m_settings.CameraGet().GetDir() ); + + if( m_settings.RenderEngineGet() == RENDER_ENGINE_OPENGL_LEGACY ) + { + // Set all pixels of PBO transparent (Alpha to 0) + // This way it will draw the full buffer but only shows the updated ( + // already calculated) squares + // ///////////////////////////////////////////////////////////////////// + unsigned int nPixels = m_realBufferSize.x * m_realBufferSize.y; + GLubyte *tmp_ptrPBO = ptrPBO + 3; // PBO is RGBA + + for( unsigned int i = 0; i < nPixels; ++i ) + { + *tmp_ptrPBO = 0; + tmp_ptrPBO += 4; // PBO is RGBA + } + } + } + + switch( m_rt_render_state ) + { + case RT_RENDER_STATE_TRACING: + rt_render_tracing( ptrPBO, aStatusTextReporter ); + break; + + case RT_RENDER_STATE_POST_PROCESS_SHADE: + rt_render_post_process_shade( ptrPBO, aStatusTextReporter ); + break; + + case RT_RENDER_STATE_POST_PROCESS_BLUR_AND_FINISH: + rt_render_post_process_blur_finish( ptrPBO, aStatusTextReporter ); + break; + + default: + wxASSERT_MSG( false, "Invalid state on m_rt_render_state"); + restart_render_state(); + break; + } + + if( aStatusTextReporter && (m_rt_render_state == RT_RENDER_STATE_FINISH) ) + { + // Calculation time in seconds + const double calculation_time = (double)( GetRunningMicroSecs() - + m_stats_start_rendering_time ) / 1e6; + + aStatusTextReporter->Report( wxString::Format( _( "Rendering time %.3f s" ), + calculation_time ) ); + } +} + + +void C3D_RENDER_RAYTRACING::rt_render_tracing( GLubyte *ptrPBO , + REPORTER *aStatusTextReporter ) +{ + m_isPreview = false; + + unsigned int nrBlocks = m_blockPositions.size(); // Cache the number of blocks + unsigned startTime = GetRunningMicroSecs(); // Get time that started render this block + bool breakLoop = false; // It will be used to break the loop + + #pragma omp parallel for schedule(dynamic) + for( signed int iBlock = 0; iBlock < (int)nrBlocks; iBlock++ ) + { + + #pragma omp flush(breakLoop) + if( !breakLoop ) // That is used to break the other threads + { + // Check if this block was already processed + if( !m_blockPositionsWasProcessed[iBlock] ) + { + m_blockPositionsWasProcessed[iBlock] = true; + + // Render this block + rt_render_trace_block( ptrPBO, iBlock ); + + #pragma omp atomic + m_nrBlocksRenderProgress++; + + + // Check if it spend already some time render and request to exit + // to display the progress + #ifdef _OPENMP + // This makes possible that only one thread (id 0) can check the time + if( omp_get_thread_num() == 0 ) + #endif + if( (GetRunningMicroSecs() - startTime) > 150000 ) + { + breakLoop = true; + #pragma omp flush(breakLoop) + } + } + } + } + + if( aStatusTextReporter ) + aStatusTextReporter->Report( wxString::Format( _( "Rendering: %.0f %%" ), + (float)(m_nrBlocksRenderProgress * 100) / + (float)nrBlocks ) ); + + // Check if it finish the rendering and if should continue to a post processing + // or mark it as finished + if( m_nrBlocksRenderProgress >= nrBlocks ) + { + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ) + m_rt_render_state = RT_RENDER_STATE_POST_PROCESS_SHADE; + else + { + m_rt_render_state = RT_RENDER_STATE_FINISH; + } + } +} + + +void C3D_RENDER_RAYTRACING::rt_render_trace_block( GLubyte *ptrPBO , + signed int iBlock ) +{ + // Initialize ray packets + // ///////////////////////////////////////////////////////////////////////// + const SFVEC2UI &blockPos = m_blockPositions[iBlock]; + const SFVEC2I blockPosI = SFVEC2I( blockPos.x + m_xoffset, + blockPos.y + m_yoffset ); + + RAYPACKET blockPacket( m_settings.CameraGet(), blockPosI ); + + HITINFO_PACKET hitPacket[RAYPACKET_RAYS_PER_PACKET]; + + // Initialize hitPacket with a "not hit" information + for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + { + hitPacket[i].m_HitInfo.m_tHit = std::numeric_limits::infinity(); + hitPacket[i].m_HitInfo.m_acc_node_info = 0; + hitPacket[i].m_hitresult = false; + hitPacket[i].m_HitInfo.m_HitNormal = SFVEC3F(); + hitPacket[i].m_HitInfo.m_ShadowFactor = 1.0f; + } + + // Calculate background gradient color + // ///////////////////////////////////////////////////////////////////////// + SFVEC3F bgColor[RAYPACKET_DIM];// Store a vertical gradient color + + for( unsigned int y = 0; y < RAYPACKET_DIM; ++y ) + { + const float posYfactor = (float)(blockPosI.y + y) / (float)m_windowSize.y; + + bgColor[y] = (SFVEC3F)m_settings.m_BgColorTop * SFVEC3F(posYfactor) + + (SFVEC3F)m_settings.m_BgColorBot * ( SFVEC3F(1.0f) - SFVEC3F(posYfactor) ); + } + + // Intersect ray packets (calculate the intersection with rays and objects) + // ///////////////////////////////////////////////////////////////////////// + if( !m_accelerator->Intersect( blockPacket, hitPacket ) ) + { + + // If block is empty then set shades and continue + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ) + { + for( unsigned int y = 0; y < RAYPACKET_DIM; ++y ) + { + const SFVEC3F &outColor = bgColor[y]; + + const unsigned int yBlockPos = blockPos.y + y; + + for( unsigned int x = 0; x < RAYPACKET_DIM; ++x ) + { + m_postshader_ssao.SetPixelData( blockPos.x + x, + yBlockPos, + SFVEC3F(), + outColor, + SFVEC3F(), + 0, + 1.0f ); + } + } + } + + // This will set the output color to be displayed + // If post processing is enabled, it will not reflect the final result + // (as the final color will be computed on post processing) + // but it is used for report progress + for( unsigned int y = 0; y < RAYPACKET_DIM; ++y ) + { + const SFVEC3F &outColor = bgColor[y]; + + const unsigned int yConst = blockPos.x + + ( (y + blockPos.y) * m_realBufferSize.x); + + for( unsigned int x = 0; x < RAYPACKET_DIM; ++x ) + { + GLubyte *ptr = &ptrPBO[ (yConst + x) * 4 ]; + + ptr[0] = (unsigned int)glm::clamp( (int)(outColor.r * 255), 0, 255 ); + ptr[1] = (unsigned int)glm::clamp( (int)(outColor.g * 255), 0, 255 ); + ptr[2] = (unsigned int)glm::clamp( (int)(outColor.b * 255), 0, 255 ); + ptr[3] = 255; + } + } + + // There is nothing more here to do.. there are no hits .. + // just background so continue + return; + } + + + // Shade hits ("paint" the intersected objects) + // ///////////////////////////////////////////////////////////////////////// + + SFVEC3F hitColor[RAYPACKET_RAYS_PER_PACKET]; + + for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y ) + { + for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i ) + { + if( hitPacket[i].m_hitresult == true ) + { + hitColor[i] = shadeHit( bgColor[y], + blockPacket.m_ray[i], + hitPacket[i].m_HitInfo, + false, + 0 ); + } + else + { + hitColor[i] = bgColor[y]; + } + } + } + + + // This code was a tentative to retrace the block but using small changes + // on ray direction (to work as a random anti-aliasing) + // but it was not producing good results with low passes, + // parked for future use / to be implemented + // ///////////////////////////////////////////////////////////////////////// + + /* + //if( m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) ) + if(0) + { + SFVEC3F absDirDiff; + absDirDiff.x = glm::abs( blockPacket.m_ray[ 1].m_Dir.x - blockPacket.m_ray[0].m_Dir.x ) * 0.45f; + absDirDiff.y = glm::abs( blockPacket.m_ray[RAYPACKET_DIM + 0].m_Dir.y - blockPacket.m_ray[0].m_Dir.y ) * 0.45f; + absDirDiff.z = glm::abs( blockPacket.m_ray[RAYPACKET_DIM + 1].m_Dir.z - blockPacket.m_ray[0].m_Dir.z ) * 0.45f; + + const unsigned int number_of_passes = 16; + + for( unsigned int aaPasses = 0; aaPasses < number_of_passes; ++aaPasses ) + { + + HITINFO_PACKET hitPacketAA[RAYPACKET_RAYS_PER_PACKET]; + + // Initialize hitPacket with a "not hit" information + for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + { + hitPacketAA[i].m_HitInfo.m_tHit = std::numeric_limits::infinity(); + hitPacketAA[i].m_HitInfo.m_acc_node_info = 0; + hitPacketAA[i].m_hitresult = false; + hitPacketAA[i].m_HitInfo.m_HitNormal = SFVEC3F(); + hitPacketAA[i].m_HitInfo.m_ShadowFactor = 1.0f; + } + + RAYPACKET blockPacketAA( m_settings.CameraGet(), blockPosI, absDirDiff ); + + if( m_accelerator->Intersect( blockPacketAA, hitPacketAA ) ) + { + for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y ) + { + for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i ) + { + if( hitPacketAA[i].m_hitresult == true ) + { + hitColor[i] += shadeHit( bgColor[y], + blockPacketAA.m_ray[i], + hitPacketAA[i].m_HitInfo, + false, + 0 ); + } + else + { + hitColor[i] += bgColor[y]; + } + } + } + } + } + + const float aaPasses_inv = 1.0f / (float)(number_of_passes + 1); + + for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + hitColor[i] *= aaPasses_inv; + } + */ + + // If anti-aliasing is enabled, trace a new random rays over the hits + // already calculated. + // On this pass it will calculate pixels near the hitted ray, + // so it will reuse the nodes found on that hits + // ///////////////////////////////////////////////////////////////////// + + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) ) + { + + SFVEC3F hitColorAA[RAYPACKET_RAYS_PER_PACKET]; + + for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + hitColorAA[i] = SFVEC3F(0.0f); + + // This just get some difference between two pixels + // There is not logic on this approach, it trys to guess the xyz increments + SFVEC3F absDirDiff; + absDirDiff.x = glm::abs( blockPacket.m_ray[RAYPACKET_DIM + 0].m_Dir.x - + blockPacket.m_ray[0].m_Dir.x ) * 0.55f; + + absDirDiff.y = glm::abs( blockPacket.m_ray[RAYPACKET_DIM + 0].m_Dir.y - + blockPacket.m_ray[0].m_Dir.y ) * 0.55f; + + absDirDiff.z = glm::abs( blockPacket.m_ray[RAYPACKET_DIM + 0].m_Dir.z - + blockPacket.m_ray[0].m_Dir.z ) * 0.55f; + + const unsigned int number_of_passes = 3; + + for( unsigned int aaPasses = 0; aaPasses < number_of_passes; ++aaPasses ) + { + for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y ) + { + for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i ) + { + if( hitPacket[i].m_hitresult == true ) + { + const SFVEC3F randVector = SFVEC3F( Fast_RandFloat() * absDirDiff.x, + Fast_RandFloat() * absDirDiff.y, + Fast_RandFloat() * absDirDiff.z ); + + RAY rayAA; + rayAA.Init( blockPacket.m_ray[i].m_Origin, + glm::normalize( blockPacket.m_ray[i].m_Dir + + randVector ) ); + + HITINFO hitAA; + hitAA.m_tHit = std::numeric_limits::infinity(); + hitAA.m_acc_node_info = 0; + hitAA.m_ShadowFactor = 1.0f; + + bool hitted = false; + + const unsigned int idx0y1 = ( x + 0 ) + RAYPACKET_DIM * ( y + 1 ); + const unsigned int idx1y1 = ( x + 1 ) + RAYPACKET_DIM * ( y + 1 ); + + // Gets the node info from the hit. If there was no hit, return 0 + const unsigned int nodex0y0 = (hitPacket[ i ].m_hitresult == false)?0: + hitPacket[ i ].m_HitInfo.m_acc_node_info; + + unsigned int nodex1y0 = 0; + + if( x < (RAYPACKET_DIM - 1) ) + nodex1y0 = (hitPacket[ i + 1 ].m_hitresult == false)?0: + hitPacket[ i + 1 ].m_HitInfo.m_acc_node_info; + + unsigned int nodex0y1 = 0; + + if( y < (RAYPACKET_DIM - 1) ) + nodex0y1 = (hitPacket[ idx0y1 ].m_hitresult == false)?0: + hitPacket[ idx0y1 ].m_HitInfo.m_acc_node_info; + + unsigned int nodex1y1 = 0; + + if( ((x < (RAYPACKET_DIM - 1)) && + (y < (RAYPACKET_DIM - 1))) ) + nodex1y1 = (hitPacket[ idx1y1 ].m_hitresult == false)?0: + hitPacket[ idx1y1 ].m_HitInfo.m_acc_node_info; + + + if( (nodex0y0 == nodex1y0) && // + (nodex0y0 == nodex0y1) && + (nodex0y0 == nodex1y1) ) + { + // Option 1 + // This option will give a very good quality on reflections (slow) + /* + if( m_accelerator->Intersect( rayAA, hitAA, nodex0y0 ) ) + { + hitColorAA[i] += shadeHit( bgColor[y], rayAA, hitAA, false, 0 ); + } + else + { + if( m_accelerator->Intersect( rayAA, hitAA ) ) + hitColorAA[i] += shadeHit( bgColor[y], rayAA, hitAA, false, 0 ); + else + hitColorAA[i] += hitColor[i]; + } + */ + + // Option 2 + // Trace again with the same node, + // then if miss just give the same color as before + //if( m_accelerator->Intersect( rayAA, hitAA, nodex0y0 ) ) + // hitColorAA[i] += shadeHit( bgColor[y], rayAA, hitAA, false, 0 ); + //else + // This option will give the same color as the hit before (faster) + hitColorAA[i] += hitColor[i]; + } + else + { + // Try to intersect the different nodes + // It tests the possible combination of hitted or not hitted points + // This will try to get the best hit for this ray + + if( nodex0y0 != 0 ) + hitted |= m_accelerator->Intersect( rayAA, hitAA, nodex0y0 ); + + if( nodex1y0 != 0 ) + if( ( nodex0y0 != nodex1y0 ) || ( nodex0y0 == 0 ) ) + hitted |= m_accelerator->Intersect( rayAA, hitAA, nodex1y0 ); + + if( nodex0y1 != 0 ) + if( ( ( nodex0y0 != nodex0y1 ) || ( nodex0y0 == 0 ) ) && + ( ( nodex1y0 != nodex0y1 ) || ( nodex1y0 == 0 ) ) ) + hitted |= m_accelerator->Intersect( rayAA, hitAA, nodex0y1 ); + + if( nodex1y1 != 0 ) + if( ( ( nodex0y0 != nodex1y1 ) || ( nodex0y0 == 0 ) ) && + ( ( nodex0y1 != nodex1y1 ) || ( nodex0y1 == 0 ) ) && + ( ( nodex1y0 != nodex1y1 ) || ( nodex1y0 == 0 ) ) ) + hitted |= m_accelerator->Intersect( rayAA, hitAA, nodex1y1 ); + + if( hitted ) + { + // If he got any result, shade it + hitColorAA[i] += shadeHit( bgColor[y], rayAA, hitAA, false, 0 ); + } + else + { + // It was missed the 'last nodes' so, trace a ray from the beginning + if( m_accelerator->Intersect( rayAA, hitAA ) ) + hitColorAA[i] += shadeHit( bgColor[y], rayAA, hitAA, false, 0 ); + else + hitColorAA[i] += hitColor[i]; + } + } + } + else + { + hitColorAA[i] += hitColor[i]; + } + } + } + } + + const float aaPasses_inv = 1.0f / (float)number_of_passes; + + for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + hitColor[i] = hitColor[i] * 0.40f + (hitColorAA[i] * aaPasses_inv) * 0.60f; + } + + + // Trace adaptative anti-aliasing middle pixels + // ///////////////////////////////////////////////////////////////////// + + HITINFO_PACKET hitPacketAA[ (RAYPACKET_DIM-1) * (RAYPACKET_DIM-1) ]; + RAY raysAA[ (RAYPACKET_DIM-1) * (RAYPACKET_DIM-1) ]; + SFVEC3F hitColorAA[ (RAYPACKET_DIM-1) * (RAYPACKET_DIM-1) ]; + bool hittedAA[ (RAYPACKET_DIM-1) * (RAYPACKET_DIM-1) ]; + + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) ) + { + for( unsigned int y = 0, i = 0; y < (RAYPACKET_DIM - 1); ++y ) + { + for( unsigned int x = 0; x < (RAYPACKET_DIM - 1); ++x, ++i ) + { + hitColorAA[i] = bgColor[y]; + hittedAA[i] = false; + + const unsigned int idx0y0 = (x + 0) + RAYPACKET_DIM * (y + 0); + const unsigned int idx1y0 = (x + 1) + RAYPACKET_DIM * (y + 0); + const unsigned int idx0y1 = (x + 0) + RAYPACKET_DIM * (y + 1); + const unsigned int idx1y1 = (x + 1) + RAYPACKET_DIM * (y + 1); + + // Evaluate if we can skip the pixel from trace if the adjacent + // pixels are similar (hitted, similar color and normal) + + if( hitPacket[ idx0y0 ].m_hitresult && + hitPacket[ idx1y0 ].m_hitresult && + hitPacket[ idx0y1 ].m_hitresult && + hitPacket[ idx1y1 ].m_hitresult ) + { + // Calc the average gray scale + + // Average + /* + const float gray_idx0y0 = + (hitColor[idx0y0].r + hitColor[idx0y0].g + hitColor[idx0y0].b ) / 3.0f; + const float gray_idx1y0 = + (hitColor[idx1y0].r + hitColor[idx1y0].g + hitColor[idx1y0].b ) / 3.0f; + const float gray_idx0y1 = + (hitColor[idx0y1].r + hitColor[idx0y1].g + hitColor[idx0y1].b ) / 3.0f; + const float gray_idx1y1 = + (hitColor[idx1y1].r + hitColor[idx1y1].g + hitColor[idx1y1].b ) / 3.0f; + */ + + + // Luminance + const float gray_idx0y0 = (hitColor[idx0y0].r * 0.2126f + + hitColor[idx0y0].g * 0.7152f + + hitColor[idx0y0].b * 0.0722f); + + const float gray_idx1y0 = (hitColor[idx1y0].r * 0.2126f + + hitColor[idx1y0].g * 0.7152f + + hitColor[idx1y0].b * 0.0722f); + + const float gray_idx0y1 = (hitColor[idx0y1].r * 0.2126f + + hitColor[idx0y1].g * 0.7152f + + hitColor[idx0y1].b * 0.0722f); + + const float gray_idx1y1 = (hitColor[idx1y1].r * 0.2126f + + hitColor[idx1y1].g * 0.7152f + + hitColor[idx1y1].b * 0.0722f); + + const float threshould_color = 0.070f; + + // Check if there are no big difference, if not, + // it will continue and not process anti-aliasing + if( ( glm::abs( gray_idx0y0 - gray_idx1y0) < threshould_color ) && + ( glm::abs( gray_idx0y0 - gray_idx0y1) < threshould_color ) && + ( glm::abs( gray_idx0y1 - gray_idx1y1) < threshould_color ) && + ( glm::abs( gray_idx1y1 - gray_idx1y0) < threshould_color ) ) + { + continue; + } + } + + + // Use this code if you want to see (debug) the pixels that are interpolated + // hitColorAA[i] = SFVEC3F(1.0f, 0.0f, 1.0f); + // hittedAA[i] = true; + // continue; + + + + // Initialize a ray that is in the middle of the 4 pixeis and + // have an average direction of the 4 pixels + raysAA[i].Init( ( blockPacket.m_ray[idx0y0].m_Origin + + blockPacket.m_ray[idx1y0].m_Origin + + blockPacket.m_ray[idx0y1].m_Origin + + blockPacket.m_ray[idx1y1].m_Origin ) / 4.0f, + glm::normalize( ( blockPacket.m_ray[idx0y0].m_Dir + + blockPacket.m_ray[idx1y0].m_Dir + + blockPacket.m_ray[idx0y1].m_Dir + + blockPacket.m_ray[idx1y1].m_Dir ) ) ); + + hitPacketAA[i].m_HitInfo.m_tHit = std::numeric_limits::infinity(); + hitPacketAA[i].m_HitInfo.m_acc_node_info = 0; + hitPacketAA[i].m_HitInfo.m_ShadowFactor = 1.0f; + hitPacketAA[i].m_hitresult = false; + + bool hitted = false; + + // Gets the node info from the hit. If there was no hit, return 0 + const unsigned int nodex0y0 = (hitPacket[ idx0y0 ].m_hitresult == false)?0: + hitPacket[ idx0y0 ].m_HitInfo.m_acc_node_info; + + const unsigned int nodex1y0 = (hitPacket[ idx1y0 ].m_hitresult == false)?0: + hitPacket[ idx1y0 ].m_HitInfo.m_acc_node_info; + + const unsigned int nodex0y1 = (hitPacket[ idx0y1 ].m_hitresult == false)?0: + hitPacket[ idx0y1 ].m_HitInfo.m_acc_node_info; + + const unsigned int nodex1y1 = (hitPacket[ idx1y1 ].m_hitresult == false)?0: + hitPacket[ idx1y1 ].m_HitInfo.m_acc_node_info; + + // Try to intersect the different nodes + // It tests the possible combination of hitted or not hitted points + // This will try to get the best hit for this ray + + if( nodex0y0 != 0 ) + hitted |= m_accelerator->Intersect( raysAA[i], + hitPacketAA[i].m_HitInfo, + nodex0y0 ); + + if( nodex1y0 != 0 ) + if( ( nodex0y0 != nodex1y0 ) || ( nodex0y0 == 0 ) ) + hitted |= m_accelerator->Intersect( raysAA[i], + hitPacketAA[i].m_HitInfo, + nodex1y0 ); + + if( nodex0y1 != 0 ) + if( ( ( nodex0y0 != nodex0y1 ) || ( nodex0y0 == 0 ) ) && + ( ( nodex1y0 != nodex0y1 ) || ( nodex1y0 == 0 ) ) ) + hitted |= m_accelerator->Intersect( raysAA[i], + hitPacketAA[i].m_HitInfo, + nodex0y1 ); + + if( nodex1y1 != 0 ) + if( ( ( nodex0y0 != nodex1y1 ) || ( nodex0y0 == 0 ) ) && + ( ( nodex0y1 != nodex1y1 ) || ( nodex0y1 == 0 ) ) && + ( ( nodex1y0 != nodex1y1 ) || ( nodex1y0 == 0 ) ) ) + hitted |= m_accelerator->Intersect( raysAA[i], + hitPacketAA[i].m_HitInfo, + nodex1y1 ); + + if( hitted ) + { + // If he got any result, shade it + hitColorAA[i] = shadeHit( bgColor[y], + raysAA[i], + hitPacketAA[i].m_HitInfo, + false, + 0 ); + } + else + { + // It was missed the 'last nodes' so, + // trace a ray from the beginning + if( m_accelerator->Intersect( raysAA[i], hitPacketAA[i].m_HitInfo ) ) + hitColorAA[i] = shadeHit( bgColor[y], + raysAA[i], + hitPacketAA[i].m_HitInfo, + false, + 0 ); + } + + hittedAA[i] = true; + } + } + } + + + // Blend original hitted pixels with anti-alised pixels + // ///////////////////////////////////////////////////////////////////// + for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y ) + { + for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i ) + { + SFVEC3F hColor = hitColor[i]; + + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) ) + { + SFVEC3F aaColor = bgColor[y]; + + if( (x > 0) && + (y > 0) && + ( x < (RAYPACKET_DIM - 1) ) && + ( y < (RAYPACKET_DIM - 1) ) ) + { + // It makes a blur of the hitColor + const SFVEC3F averageHitColor = + hitColor[ (x - 1) + RAYPACKET_DIM * (y - 1) ] * 0.0625f + + hitColor[ (x + 0) + RAYPACKET_DIM * (y - 1) ] * 0.1250f + + hitColor[ (x + 1) + RAYPACKET_DIM * (y - 1) ] * 0.0625f + + hitColor[ (x - 1) + RAYPACKET_DIM * (y + 0) ] * 0.1250f + + hitColor[ (x + 0) + RAYPACKET_DIM * (y + 0) ] * 0.2500f + + hitColor[ (x + 1) + RAYPACKET_DIM * (y + 0) ] * 0.1250f + + hitColor[ (x - 1) + RAYPACKET_DIM * (y + 1) ] * 0.0625f + + hitColor[ (x + 0) + RAYPACKET_DIM * (y + 1) ] * 0.1250f + + hitColor[ (x + 1) + RAYPACKET_DIM * (y + 1) ] * 0.0625f; + + + const unsigned x0y0 = (x - 1) + (RAYPACKET_DIM - 1) * (y - 1); + const unsigned x1y0 = (x - 0) + (RAYPACKET_DIM - 1) * (y - 1); + const unsigned x0y1 = (x - 1) + (RAYPACKET_DIM - 1) * (y - 0); + const unsigned x1y1 = (x - 0) + (RAYPACKET_DIM - 1) * (y - 0); + + aaColor = (hittedAA[ x0y0 ]? hitColorAA[ x0y0 ]: averageHitColor) + + (hittedAA[ x1y0 ]? hitColorAA[ x1y0 ]: averageHitColor) + + (hittedAA[ x0y1 ]? hitColorAA[ x0y1 ]: averageHitColor) + + (hittedAA[ x1y1 ]? hitColorAA[ x1y1 ]: averageHitColor); + + aaColor /= 4.0f; + } + else + { + if( (x == 0) && (y == 0) ) + { + const unsigned x0y0 = (x - 0) + (RAYPACKET_DIM - 1) * (y - 0); + aaColor = (hittedAA[ x0y0 ]? hitColorAA[ x0y0 ]: hColor); + } + else + { + if( (x == (RAYPACKET_DIM - 1)) && (y == (RAYPACKET_DIM - 1)) ) + { + const unsigned x0y0 = (x - 1) + (RAYPACKET_DIM - 1) * (y - 1); + aaColor = (hittedAA[ x0y0 ]? hitColorAA[ x0y0 ]: hColor); + } + else + { + if( (x == (RAYPACKET_DIM - 1)) && (y == 0) ) + { + const unsigned x0y0 = (x - 1) + (RAYPACKET_DIM - 1) * (y - 0); + aaColor = (hittedAA[ x0y0 ]? hitColorAA[ x0y0 ]: hColor); + } + else + { + if( (x == 0) && (y == (RAYPACKET_DIM - 1)) ) + { + const unsigned x0y0 = (x - 0) + (RAYPACKET_DIM - 1) * (y - 1); + aaColor = (hittedAA[ x0y0 ]? hitColorAA[ x0y0 ]: hColor); + } + else + { + if( ( y == 0 ) && ( x < (RAYPACKET_DIM - 1) ) ) + { + const unsigned x0y0 = (x - 1) + (RAYPACKET_DIM - 1) * + (y - 0); + const unsigned x1y0 = (x - 0) + (RAYPACKET_DIM - 1) * + (y - 0); + + aaColor = (hittedAA[ x0y0 ]? hitColorAA[ x0y0 ]: hColor) + + (hittedAA[ x1y0 ]? hitColorAA[ x1y0 ]: hColor); + + aaColor = aaColor / 2.0f; + } + else + { + if( ( y == (RAYPACKET_DIM - 1) ) && + ( x < (RAYPACKET_DIM - 1) ) ) + { + const unsigned x0y0 = (x - 1) + (RAYPACKET_DIM - 1) * + (y - 1); + const unsigned x1y0 = (x - 0) + (RAYPACKET_DIM - 1) * + (y - 1); + + aaColor = (hittedAA[ x0y0 ]?hitColorAA[ x0y0 ]:hColor) + + (hittedAA[ x1y0 ]?hitColorAA[ x1y0 ]:hColor); + + aaColor = aaColor / 2.0f; + } + else + { + if( ( x == 0 ) && ( y < (RAYPACKET_DIM - 1) ) ) + { + const unsigned x0y0 = (x - 0) + + (RAYPACKET_DIM - 1) * (y - 1); + const unsigned x0y1 = (x - 0) + + (RAYPACKET_DIM - 1) * (y - 0); + + aaColor = (hittedAA[x0y0]?hitColorAA[x0y0]:hColor) + + (hittedAA[x0y1]?hitColorAA[x0y1]:hColor); + + aaColor = aaColor / 2.0f; + } + else + { + if( ( x == (RAYPACKET_DIM - 1) ) && + ( y < (RAYPACKET_DIM - 1) ) ) + { + const unsigned x0y0 = (x - 1) + + (RAYPACKET_DIM - 1) * + (y - 1); + + const unsigned x0y1 = (x - 1) + + (RAYPACKET_DIM - 1) * + (y - 0); + + aaColor = + (hittedAA[x0y0]?hitColorAA[x0y0]:hColor) + + (hittedAA[x0y1]?hitColorAA[x0y1]:hColor); + + aaColor = aaColor / 2.0f; + } + else + { + aaColor = SFVEC3F(1.0f, 0.0f, 1.0f ); // Invalid + } + } + } + } + } + } + } + } + } + + // Calculate the final blend color. It gives more importance + // to the original color + hColor = hColor * 0.60f + aaColor * 0.40f; + } + + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ) + { + if( hitPacket[i].m_hitresult == true ) + m_postshader_ssao.SetPixelData( blockPos.x + x, blockPos.y + y, + hitPacket[i].m_HitInfo.m_HitNormal, + hColor, + blockPacket.m_ray[i].at( + hitPacket[i].m_HitInfo.m_tHit ), + hitPacket[i].m_HitInfo.m_tHit, + hitPacket[i].m_HitInfo.m_ShadowFactor ); + else + m_postshader_ssao.SetPixelData( blockPos.x + x, blockPos.y + y, + SFVEC3F(), + hColor, + SFVEC3F(), + 0, + 1.0f ); + + } + + // This will set the output color to be displayed + // If post processing is enabled, it will not reflect the final result + // (as the final color will be computed on post processing) + // but it is used for report progress + GLubyte *ptr = &ptrPBO[ ( blockPos.x + x + + ((y + blockPos.y) * m_realBufferSize.x) ) * 4 ]; + + ptr[0] = (unsigned int)glm::clamp( (int)(hColor.r * 255), 0, 255 ); + ptr[1] = (unsigned int)glm::clamp( (int)(hColor.g * 255), 0, 255 ); + ptr[2] = (unsigned int)glm::clamp( (int)(hColor.b * 255), 0, 255 ); + ptr[3] = 255; + } + } +} + + +void C3D_RENDER_RAYTRACING::rt_render_post_process_shade( GLubyte *ptrPBO, + REPORTER *aStatusTextReporter ) +{ + (void)ptrPBO; // unused + + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ) + { + if( aStatusTextReporter ) + aStatusTextReporter->Report( _("Rendering: Post processing shader") ); + + // Compute the shader value + #pragma omp parallel for schedule(dynamic) + for( signed int y = 0; y < (int)m_realBufferSize.y; ++y ) + { + SFVEC3F *ptr = &m_shaderBuffer[ y * m_realBufferSize.x ]; + + for( signed int x = 0; x < (int)m_realBufferSize.x; ++x ) + { + *ptr = m_postshader_ssao.Shade( SFVEC2I( x, y ) ); + ptr++; + } + } + + // Wait for all threads to finish + #pragma omp barrier + + // Set next state + m_rt_render_state = RT_RENDER_STATE_POST_PROCESS_BLUR_AND_FINISH; + } + else + { + // As this was an invalid state, set to finish + m_rt_render_state = RT_RENDER_STATE_FINISH; + } +} + + +void C3D_RENDER_RAYTRACING::rt_render_post_process_blur_finish( GLubyte *ptrPBO, + REPORTER *aStatusTextReporter ) +{ + (void)aStatusTextReporter; //unused + + if( m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ) + { + // Now blurs the shader result and compute the final color + #pragma omp parallel for schedule(dynamic) + for( signed int y = 0; y < (int)m_realBufferSize.y; ++y ) + { + GLubyte *ptr = &ptrPBO[ y * m_realBufferSize.x * 4 ]; + + SFVEC3F *ptrShaderY0 = + &m_shaderBuffer[ glm::max((int)y - 2, 0) * m_realBufferSize.x ]; + SFVEC3F *ptrShaderY1 = + &m_shaderBuffer[ glm::max((int)y - 1, 0) * m_realBufferSize.x ]; + SFVEC3F *ptrShaderY2 = + &m_shaderBuffer[ y * m_realBufferSize.x ]; + SFVEC3F *ptrShaderY3 = + &m_shaderBuffer[ glm::min((int)y + 1, (int)(m_realBufferSize.y - 1)) * + m_realBufferSize.x ]; + SFVEC3F *ptrShaderY4 = + &m_shaderBuffer[ glm::min((int)y + 2, (int)(m_realBufferSize.y - 1)) * + m_realBufferSize.x ]; + + for( signed int x = 0; x < (int)m_realBufferSize.x; ++x ) + { +// This #if should be 1, it is here that can be used for debug proposes during development +#if 1 + + SFVEC3F bluredShadeColor = (*ptrShaderY0) * 1.0f / 273.0f + + (*ptrShaderY1) * 4.0f / 273.0f + + (*ptrShaderY2) * 7.0f / 273.0f + + (*ptrShaderY3) * 4.0f / 273.0f + + (*ptrShaderY4) * 1.0f / 273.0f; + if( x > 1 ) + { + ptrShaderY0++; + ptrShaderY1++; + ptrShaderY2++; + ptrShaderY3++; + ptrShaderY4++; + } + + bluredShadeColor += (*ptrShaderY0) * 4.0f / 273.0f + + (*ptrShaderY1) *16.0f / 273.0f + + (*ptrShaderY2) *26.0f / 273.0f + + (*ptrShaderY3) *16.0f / 273.0f + + (*ptrShaderY4) * 4.0f / 273.0f; + + if( x > 0 ) + { + ptrShaderY0++; + ptrShaderY1++; + ptrShaderY2++; + ptrShaderY3++; + ptrShaderY4++; + } + + bluredShadeColor += (*ptrShaderY0) * 7.0f / 273.0f + + (*ptrShaderY1) *26.0f / 273.0f + + (*ptrShaderY2) *41.0f / 273.0f + + (*ptrShaderY3) *26.0f / 273.0f + + (*ptrShaderY4) * 7.0f / 273.0f; + + if( x < ((int)m_realBufferSize.x - 1) ) + { + ptrShaderY0++; + ptrShaderY1++; + ptrShaderY2++; + ptrShaderY3++; + ptrShaderY4++; + } + + bluredShadeColor += (*ptrShaderY0) * 4.0f / 273.0f + + (*ptrShaderY1) *16.0f / 273.0f + + (*ptrShaderY2) *26.0f / 273.0f + + (*ptrShaderY3) *16.0f / 273.0f + + (*ptrShaderY4) * 4.0f / 273.0f; + + if( x < ((int)m_realBufferSize.x - 2) ) + { + ptrShaderY0++; + ptrShaderY1++; + ptrShaderY2++; + ptrShaderY3++; + ptrShaderY4++; + } + + bluredShadeColor += (*ptrShaderY0) * 1.0f / 273.0f + + (*ptrShaderY1) * 4.0f / 273.0f + + (*ptrShaderY2) * 7.0f / 273.0f + + (*ptrShaderY3) * 4.0f / 273.0f + + (*ptrShaderY4) * 1.0f / 273.0f; + + ptrShaderY0-= 3; + ptrShaderY1-= 3; + ptrShaderY2-= 3; + ptrShaderY3-= 3; + ptrShaderY4-= 3; + + const float grayBluredColor = ( bluredShadeColor.r + + bluredShadeColor.g + + bluredShadeColor.b ) / 3.0f; + + const SFVEC3F shadedColor = m_postshader_ssao.GetColorAtNotProtected( + SFVEC2I( x, y ) ) * ( SFVEC3F(1.0f) - + bluredShadeColor ) - + ( bluredShadeColor - grayBluredColor * 0.5f ); + + // Debug code + //const SFVEC3F shadedColor = ( bluredShadeColor - grayBluredColor * 0.5f); + //const SFVEC3F shadedColor = - glm::min( bluredShadeColor, SFVEC3F(0.0f) ); + //const SFVEC3F shadedColor = 0.5f * (SFVEC3F(1.0f) - bluredShadeColor) - + // glm::min( bluredShadeColor, SFVEC3F(0.0f) ); + //const SFVEC3F shadedColor = bluredShadeColor; +#else + // Debug code + //const SFVEC3F shadedColor = SFVEC3F( 1.0f ) - + // m_shaderBuffer[ y * m_realBufferSize.x + x]; + const SFVEC3F shadedColor = m_shaderBuffer[ y * m_realBufferSize.x + x ]; +#endif + ptr[0] = (unsigned int)glm::clamp( (int)(shadedColor.r * 255), 0, 255 ); + ptr[1] = (unsigned int)glm::clamp( (int)(shadedColor.g * 255), 0, 255 ); + ptr[2] = (unsigned int)glm::clamp( (int)(shadedColor.b * 255), 0, 255 ); + ptr[3] = 255; + ptr += 4; + } + } + + // Wait for all threads to finish + #pragma omp barrier + + // Debug code + //m_postshader_ssao.DebugBuffersOutputAsImages(); + } + + // End rendering + m_rt_render_state = RT_RENDER_STATE_FINISH; +} + + +void C3D_RENDER_RAYTRACING::render_preview( GLubyte *ptrPBO ) +{ + m_isPreview = true; + + unsigned int nrBlocks = m_blockPositionsFast.size(); + + #pragma omp parallel for schedule(dynamic) + for( signed int iBlock = 0; iBlock < (int)nrBlocks; iBlock++ ) + { + const SFVEC2UI &windowPosUI = m_blockPositionsFast[ iBlock ]; + const SFVEC2I windowsPos = SFVEC2I( windowPosUI.x + m_xoffset, + windowPosUI.y + m_yoffset ); + + RAYPACKET blockPacket( m_settings.CameraGet(), windowsPos, 4 ); + + HITINFO_PACKET hitPacket[RAYPACKET_RAYS_PER_PACKET]; + + // Initialize hitPacket with a "not hit" information + for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + { + hitPacket[i].m_HitInfo.m_tHit = std::numeric_limits::infinity(); + hitPacket[i].m_HitInfo.m_acc_node_info = 0; + hitPacket[i].m_hitresult = false; + } + + // Intersect packet block + m_accelerator->Intersect( blockPacket, hitPacket ); + + + // Calculate background gradient color + // ///////////////////////////////////////////////////////////////////// + SFVEC3F bgColor[RAYPACKET_DIM]; + + for( unsigned int y = 0; y < RAYPACKET_DIM; ++y ) + { + const float posYfactor = (float)(windowsPos.y + y * 4.0f) / (float)m_windowSize.y; + + bgColor[y] = (SFVEC3F)m_settings.m_BgColorTop * SFVEC3F(posYfactor) + + (SFVEC3F)m_settings.m_BgColorBot * ( SFVEC3F(1.0f) - SFVEC3F(posYfactor) ); + } + + CCOLORRGB hitColorShading[RAYPACKET_RAYS_PER_PACKET]; + + for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i ) + { + const SFVEC3F bhColorY = bgColor[i/RAYPACKET_DIM]; + + if( hitPacket[i].m_hitresult == true ) + { + const SFVEC3F hitColor = shadeHit( bhColorY, + blockPacket.m_ray[i], + hitPacket[i].m_HitInfo, + false, + 0 ); + + hitColorShading[i] = CCOLORRGB( hitColor ); + } + else + hitColorShading[i] = bhColorY; + } + + CCOLORRGB cLRB_old[(RAYPACKET_DIM - 1)]; + + for( unsigned int y = 0; y < (RAYPACKET_DIM - 1); ++y ) + { + + const SFVEC3F bgColorY = bgColor[y]; + const CCOLORRGB bgColorYRGB = CCOLORRGB( bgColorY ); + + // This stores cRTB from the last block to be reused next time in a cLTB pixel + CCOLORRGB cRTB_old; + + //RAY cRTB_ray; + //HITINFO cRTB_hitInfo; + + for( unsigned int x = 0; x < (RAYPACKET_DIM - 1); ++x ) + { + // pxl 0 pxl 1 pxl 2 pxl 3 pxl 4 + // x0 x1 ... + // .---------------------------. + // y0 | cLT | cxxx | cLRT | cxxx | cRT | + // | cxxx | cLTC | cxxx | cRTC | cxxx | + // | cLTB | cxxx | cC | cxxx | cRTB | + // | cxxx | cLBC | cxxx | cRBC | cxxx | + // '---------------------------' + // y1 | cLB | cxxx | cLRB | cxxx | cRB | + + const unsigned int iLT = ((x + 0) + RAYPACKET_DIM * (y + 0)); + const unsigned int iRT = ((x + 1) + RAYPACKET_DIM * (y + 0)); + const unsigned int iLB = ((x + 0) + RAYPACKET_DIM * (y + 1)); + const unsigned int iRB = ((x + 1) + RAYPACKET_DIM * (y + 1)); + + // !TODO: skip when there are no hits + + + const CCOLORRGB &cLT = hitColorShading[ iLT ]; + const CCOLORRGB &cRT = hitColorShading[ iRT ]; + const CCOLORRGB &cLB = hitColorShading[ iLB ]; + const CCOLORRGB &cRB = hitColorShading[ iRB ]; + + // Trace and shade cC + // ///////////////////////////////////////////////////////////// + CCOLORRGB cC = bgColorYRGB; + + const SFVEC3F &oriLT = blockPacket.m_ray[ iLT ].m_Origin; + const SFVEC3F &oriRB = blockPacket.m_ray[ iRB ].m_Origin; + + const SFVEC3F &dirLT = blockPacket.m_ray[ iLT ].m_Dir; + const SFVEC3F &dirRB = blockPacket.m_ray[ iRB ].m_Dir; + + SFVEC3F oriC; + SFVEC3F dirC; + + HITINFO centerHitInfo; + centerHitInfo.m_tHit = std::numeric_limits::infinity(); + + bool hittedC = false; + + if( (hitPacket[ iLT ].m_hitresult == true) || + (hitPacket[ iRT ].m_hitresult == true) || + (hitPacket[ iLB ].m_hitresult == true) || + (hitPacket[ iRB ].m_hitresult == true) ) + { + + oriC = ( oriLT + oriRB ) * 0.5f; + dirC = glm::normalize( ( dirLT + dirRB ) * 0.5f ); + + // Trace the center ray + RAY centerRay; + centerRay.Init( oriC, dirC ); + + const unsigned int nodeLT = (hitPacket[ iLT ].m_hitresult == false)?0: + hitPacket[ iLT ].m_HitInfo.m_acc_node_info; + + const unsigned int nodeRT = (hitPacket[ iRT ].m_hitresult == false)?0: + hitPacket[ iRT ].m_HitInfo.m_acc_node_info; + + const unsigned int nodeLB = (hitPacket[ iLB ].m_hitresult == false)?0: + hitPacket[ iLB ].m_HitInfo.m_acc_node_info; + + const unsigned int nodeRB = (hitPacket[ iRB ].m_hitresult == false)?0: + hitPacket[ iRB ].m_HitInfo.m_acc_node_info; + + if( nodeLT != 0 ) + hittedC |= m_accelerator->Intersect( centerRay, centerHitInfo, nodeLT ); + + if( nodeRT != 0 ) + if( ( nodeRT != nodeLT ) || ( nodeLT == 0 ) ) + hittedC |= m_accelerator->Intersect( centerRay, centerHitInfo, nodeRT ); + + if( nodeLB != 0 ) + if( ( ( nodeLB != nodeLT ) || ( nodeLT == 0 ) ) && + ( ( nodeLB != nodeRT ) || ( nodeRT == 0 ) ) ) + hittedC |= m_accelerator->Intersect( centerRay, centerHitInfo, nodeLB ); + + if( nodeRB != 0 ) + if( ( ( nodeRB != nodeLB ) || ( nodeLB == 0 ) ) && + ( ( nodeRB != nodeLT ) || ( nodeLT == 0 ) ) && + ( ( nodeRB != nodeRT ) || ( nodeRT == 0 ) ) ) + hittedC |= m_accelerator->Intersect( centerRay, centerHitInfo, nodeRB ); + + if( hittedC ) + cC = CCOLORRGB( shadeHit( bgColorY, centerRay, centerHitInfo, false, 0 ) ); + else + { + centerHitInfo.m_tHit = std::numeric_limits::infinity(); + hittedC = m_accelerator->Intersect( centerRay, centerHitInfo ); + + if( hittedC ) + cC = CCOLORRGB( shadeHit( bgColorY, + centerRay, + centerHitInfo, + false, + 0 ) ); + } + } + + // Trace and shade cLRT + // ///////////////////////////////////////////////////////////// + CCOLORRGB cLRT = bgColorYRGB; + + const SFVEC3F &oriRT = blockPacket.m_ray[ iRT ].m_Origin; + const SFVEC3F &dirRT = blockPacket.m_ray[ iRT ].m_Dir; + + if( y == 0 ) + { + // Trace the center ray + RAY rayLRT; + rayLRT.Init( ( oriLT + oriRT ) * 0.5f, + glm::normalize( ( dirLT + dirRT ) * 0.5f ) ); + + HITINFO hitInfoLRT; + hitInfoLRT.m_tHit = std::numeric_limits::infinity(); + + if( hitPacket[ iLT ].m_hitresult && + hitPacket[ iRT ].m_hitresult && + (hitPacket[ iLT ].m_HitInfo.pHitObject == hitPacket[ iRT ].m_HitInfo.pHitObject) ) + { + hitInfoLRT.pHitObject = hitPacket[ iLT ].m_HitInfo.pHitObject; + hitInfoLRT.m_tHit = ( hitPacket[ iLT ].m_HitInfo.m_tHit + + hitPacket[ iRT ].m_HitInfo.m_tHit ) * 0.5f; + hitInfoLRT.m_HitNormal = + glm::normalize( ( hitPacket[ iLT ].m_HitInfo.m_HitNormal + + hitPacket[ iRT ].m_HitInfo.m_HitNormal ) * 0.5f ); + + cLRT = CCOLORRGB( shadeHit( bgColorY, rayLRT, hitInfoLRT, false, 0 ) ); + cLRT = BlendColor( cLRT, BlendColor( cLT, cRT) ); + } + else + { + if( hitPacket[ iLT ].m_hitresult || + hitPacket[ iRT ].m_hitresult ) // If any hits + { + const unsigned int nodeLT = (hitPacket[ iLT ].m_hitresult == false)?0: + hitPacket[ iLT ].m_HitInfo.m_acc_node_info; + + const unsigned int nodeRT = (hitPacket[ iRT ].m_hitresult == false)?0: + hitPacket[ iRT ].m_HitInfo.m_acc_node_info; + + bool hittedLRT = false; + + if( nodeLT != 0 ) + hittedLRT |= m_accelerator->Intersect( rayLRT, hitInfoLRT, nodeLT ); + + if( nodeRT != 0 ) + if( ( nodeRT != nodeLT ) || ( nodeLT == 0 ) ) + hittedLRT |= m_accelerator->Intersect( rayLRT, + hitInfoLRT, + nodeRT ); + + if( hittedLRT ) + cLRT = CCOLORRGB( shadeHit( bgColorY, + rayLRT, + hitInfoLRT, + false, + 0 ) ); + else + { + hitInfoLRT.m_tHit = std::numeric_limits::infinity(); + + if( m_accelerator->Intersect( rayLRT,hitInfoLRT ) ) + cLRT = CCOLORRGB( shadeHit( bgColorY, + rayLRT, + hitInfoLRT, + false, + 0 ) ); + } + } + } + } + else + cLRT = cLRB_old[x]; + + + // Trace and shade cLTB + // ///////////////////////////////////////////////////////////// + CCOLORRGB cLTB = bgColorYRGB; + + if( x == 0 ) + { + const SFVEC3F &oriLB = blockPacket.m_ray[ iLB ].m_Origin; + const SFVEC3F &dirLB = blockPacket.m_ray[ iLB ].m_Dir; + + // Trace the center ray + RAY rayLTB; + rayLTB.Init( ( oriLT + oriLB ) * 0.5f, + glm::normalize( ( dirLT + dirLB ) * 0.5f ) ); + + HITINFO hitInfoLTB; + hitInfoLTB.m_tHit = std::numeric_limits::infinity(); + + if( hitPacket[ iLT ].m_hitresult && + hitPacket[ iLB ].m_hitresult && + ( hitPacket[ iLT ].m_HitInfo.pHitObject == + hitPacket[ iLB ].m_HitInfo.pHitObject ) ) + { + hitInfoLTB.pHitObject = hitPacket[ iLT ].m_HitInfo.pHitObject; + hitInfoLTB.m_tHit = ( hitPacket[ iLT ].m_HitInfo.m_tHit + + hitPacket[ iLB ].m_HitInfo.m_tHit ) * 0.5f; + hitInfoLTB.m_HitNormal = + glm::normalize( ( hitPacket[ iLT ].m_HitInfo.m_HitNormal + + hitPacket[ iLB ].m_HitInfo.m_HitNormal ) * 0.5f ); + cLTB = CCOLORRGB( shadeHit( bgColorY, rayLTB, hitInfoLTB, false, 0 ) ); + cLTB = BlendColor( cLTB, BlendColor( cLT, cLB) ); + } + else + { + if( hitPacket[ iLT ].m_hitresult || + hitPacket[ iLB ].m_hitresult ) // If any hits + { + const unsigned int nodeLT = (hitPacket[ iLT ].m_hitresult == false)?0: + hitPacket[ iLT ].m_HitInfo.m_acc_node_info; + + const unsigned int nodeLB = (hitPacket[ iLB ].m_hitresult == false)?0: + hitPacket[ iLB ].m_HitInfo.m_acc_node_info; + + bool hittedLTB = false; + + if( nodeLT != 0 ) + hittedLTB |= m_accelerator->Intersect( rayLTB, + hitInfoLTB, + nodeLT ); + + if( nodeLB != 0 ) + if( ( nodeLB != nodeLT ) || ( nodeLT == 0 ) ) + hittedLTB |= m_accelerator->Intersect( rayLTB, + hitInfoLTB, + nodeLB ); + + if( hittedLTB ) + cLTB = CCOLORRGB( shadeHit( bgColorY, + rayLTB, + hitInfoLTB, + false, + 0 ) ); + else + { + hitInfoLTB.m_tHit = std::numeric_limits::infinity(); + + if( m_accelerator->Intersect( rayLTB, hitInfoLTB ) ) + cLTB = CCOLORRGB( shadeHit( bgColorY, + rayLTB, + hitInfoLTB, + false, + 0 ) ); + } + } + } + } + else + cLTB = cRTB_old; + + + // Trace and shade cRTB + // ///////////////////////////////////////////////////////////// + CCOLORRGB cRTB = bgColorYRGB; + + // Trace the center ray + RAY rayRTB; + rayRTB.Init( ( oriRT + oriRB ) * 0.5f, + glm::normalize( ( dirRT + dirRB ) * 0.5f ) ); + + HITINFO hitInfoRTB; + hitInfoRTB.m_tHit = std::numeric_limits::infinity(); + + if( hitPacket[ iRT ].m_hitresult && + hitPacket[ iRB ].m_hitresult && + ( hitPacket[ iRT ].m_HitInfo.pHitObject == + hitPacket[ iRB ].m_HitInfo.pHitObject ) ) + { + hitInfoRTB.pHitObject = hitPacket[ iRT ].m_HitInfo.pHitObject; + + hitInfoRTB.m_tHit = ( hitPacket[ iRT ].m_HitInfo.m_tHit + + hitPacket[ iRB ].m_HitInfo.m_tHit ) * 0.5f; + + hitInfoRTB.m_HitNormal = + glm::normalize( ( hitPacket[ iRT ].m_HitInfo.m_HitNormal + + hitPacket[ iRB ].m_HitInfo.m_HitNormal ) * 0.5f ); + + cRTB = CCOLORRGB( shadeHit( bgColorY, rayRTB, hitInfoRTB, false, 0 ) ); + cRTB = BlendColor( cRTB, BlendColor( cRT, cRB) ); + } + else + { + if( hitPacket[ iRT ].m_hitresult || + hitPacket[ iRB ].m_hitresult ) // If any hits + { + const unsigned int nodeRT = (hitPacket[ iRT ].m_hitresult == false)?0: + hitPacket[ iRT ].m_HitInfo.m_acc_node_info; + const unsigned int nodeRB = (hitPacket[ iRB ].m_hitresult == false)?0: + hitPacket[ iRB ].m_HitInfo.m_acc_node_info; + + bool hittedRTB = false; + + if( nodeRT != 0 ) + hittedRTB |= m_accelerator->Intersect( rayRTB, hitInfoRTB, nodeRT ); + + if( nodeRB != 0 ) + if( ( nodeRB != nodeRT ) || ( nodeRT == 0 ) ) + hittedRTB |= m_accelerator->Intersect( rayRTB, hitInfoRTB, nodeRB ); + + if( hittedRTB ) + cRTB = CCOLORRGB( shadeHit( bgColorY, + rayRTB, + hitInfoRTB, + false, + 0 ) ); + else + { + hitInfoRTB.m_tHit = std::numeric_limits::infinity(); + + if( m_accelerator->Intersect( rayRTB, hitInfoRTB ) ) + cRTB = CCOLORRGB( shadeHit( bgColorY, + rayRTB, + hitInfoRTB, + false, + 0 ) ); + } + } + } + + cRTB_old = cRTB; + + + // Trace and shade cLRB + // ///////////////////////////////////////////////////////////// + CCOLORRGB cLRB = bgColorYRGB; + + const SFVEC3F &oriLB = blockPacket.m_ray[ iLB ].m_Origin; + const SFVEC3F &dirLB = blockPacket.m_ray[ iLB ].m_Dir; + + // Trace the center ray + RAY rayLRB; + rayLRB.Init( ( oriLB + oriRB ) * 0.5f, + glm::normalize( ( dirLB + dirRB ) * 0.5f ) ); + + HITINFO hitInfoLRB; + hitInfoLRB.m_tHit = std::numeric_limits::infinity(); + + if( hitPacket[ iLB ].m_hitresult && + hitPacket[ iRB ].m_hitresult && + ( hitPacket[ iLB ].m_HitInfo.pHitObject == + hitPacket[ iRB ].m_HitInfo.pHitObject ) ) + { + hitInfoLRB.pHitObject = hitPacket[ iLB ].m_HitInfo.pHitObject; + + hitInfoLRB.m_tHit = ( hitPacket[ iLB ].m_HitInfo.m_tHit + + hitPacket[ iRB ].m_HitInfo.m_tHit ) * 0.5f; + + hitInfoLRB.m_HitNormal = + glm::normalize( ( hitPacket[ iLB ].m_HitInfo.m_HitNormal + + hitPacket[ iRB ].m_HitInfo.m_HitNormal ) * 0.5f ); + + cLRB = CCOLORRGB( shadeHit( bgColorY, rayLRB, hitInfoLRB, false, 0 ) ); + cLRB = BlendColor( cLRB, BlendColor( cLB, cRB) ); + } + else + { + if( hitPacket[ iLB ].m_hitresult || + hitPacket[ iRB ].m_hitresult ) // If any hits + { + const unsigned int nodeLB = (hitPacket[ iLB ].m_hitresult == false)?0: + hitPacket[ iLB ].m_HitInfo.m_acc_node_info; + const unsigned int nodeRB = (hitPacket[ iRB ].m_hitresult == false)?0: + hitPacket[ iRB ].m_HitInfo.m_acc_node_info; + + bool hittedLRB = false; + + if( nodeLB != 0 ) + hittedLRB |= m_accelerator->Intersect( rayLRB, hitInfoLRB, nodeLB ); + + if( nodeRB != 0 ) + if( ( nodeRB != nodeLB ) || ( nodeLB == 0 ) ) + hittedLRB |= m_accelerator->Intersect( rayLRB, hitInfoLRB, nodeRB ); + + if( hittedLRB ) + cLRB = CCOLORRGB( shadeHit( bgColorY, rayLRB, hitInfoLRB, false, 0 ) ); + else + { + hitInfoLRB.m_tHit = std::numeric_limits::infinity(); + + if( m_accelerator->Intersect( rayLRB, hitInfoLRB ) ) + cLRB = CCOLORRGB( shadeHit( bgColorY, + rayLRB, + hitInfoLRB, + false, + 0 ) ); + } + } + } + + cLRB_old[x] = cLRB; + + + // Trace and shade cLTC + // ///////////////////////////////////////////////////////////// + CCOLORRGB cLTC = BlendColor( cLT , cC ); + + if( hitPacket[ iLT ].m_hitresult || hittedC ) + { + // Trace the center ray + RAY rayLTC; + rayLTC.Init( ( oriLT + oriC ) * 0.5f, + glm::normalize( ( dirLT + dirC ) * 0.5f ) ); + + HITINFO hitInfoLTC; + hitInfoLTC.m_tHit = std::numeric_limits::infinity(); + + bool hitted = false; + + if( hittedC ) + hitted = centerHitInfo.pHitObject->Intersect( rayLTC, hitInfoLTC ); + else + if( hitPacket[ iLT ].m_hitresult ) + hitted = hitPacket[ iLT ].m_HitInfo.pHitObject->Intersect( rayLTC, + hitInfoLTC ); + + if( hitted ) + cLTC = CCOLORRGB( shadeHit( bgColorY, rayLTC, hitInfoLTC, false, 0 ) ); + } + + + // Trace and shade cRTC + // ///////////////////////////////////////////////////////////// + CCOLORRGB cRTC = BlendColor( cRT , cC ); + + if( hitPacket[ iRT ].m_hitresult || hittedC ) + { + // Trace the center ray + RAY rayRTC; + rayRTC.Init( ( oriRT + oriC ) * 0.5f, + glm::normalize( ( dirRT + dirC ) * 0.5f ) ); + + HITINFO hitInfoRTC; + hitInfoRTC.m_tHit = std::numeric_limits::infinity(); + + bool hitted = false; + + if( hittedC ) + hitted = centerHitInfo.pHitObject->Intersect( rayRTC, hitInfoRTC ); + else + if( hitPacket[ iRT ].m_hitresult ) + hitted = hitPacket[ iRT ].m_HitInfo.pHitObject->Intersect( rayRTC, + hitInfoRTC ); + + if( hitted ) + cRTC = CCOLORRGB( shadeHit( bgColorY, rayRTC, hitInfoRTC, false, 0 ) ); + } + + + // Trace and shade cLBC + // ///////////////////////////////////////////////////////////// + CCOLORRGB cLBC = BlendColor( cLB , cC ); + + if( hitPacket[ iLB ].m_hitresult || hittedC ) + { + // Trace the center ray + RAY rayLBC; + rayLBC.Init( ( oriLB + oriC ) * 0.5f, + glm::normalize( ( dirLB + dirC ) * 0.5f ) ); + + HITINFO hitInfoLBC; + hitInfoLBC.m_tHit = std::numeric_limits::infinity(); + + bool hitted = false; + + if( hittedC ) + hitted = centerHitInfo.pHitObject->Intersect( rayLBC, hitInfoLBC ); + else + if( hitPacket[ iLB ].m_hitresult ) + hitted = hitPacket[ iLB ].m_HitInfo.pHitObject->Intersect( rayLBC, + hitInfoLBC ); + + if( hitted ) + cLBC = CCOLORRGB( shadeHit( bgColorY, rayLBC, hitInfoLBC, false, 0 ) ); + } + + + // Trace and shade cRBC + // ///////////////////////////////////////////////////////////// + CCOLORRGB cRBC = BlendColor( cRB , cC ); + + if( hitPacket[ iRB ].m_hitresult || hittedC ) + { + // Trace the center ray + RAY rayRBC; + rayRBC.Init( ( oriRB + oriC ) * 0.5f, + glm::normalize( ( dirRB + dirC ) * 0.5f ) ); + + HITINFO hitInfoRBC; + hitInfoRBC.m_tHit = std::numeric_limits::infinity(); + + bool hitted = false; + + if( hittedC ) + hitted = centerHitInfo.pHitObject->Intersect( rayRBC, hitInfoRBC ); + else + if( hitPacket[ iRB ].m_hitresult ) + hitted = hitPacket[ iRB ].m_HitInfo.pHitObject->Intersect( rayRBC, + hitInfoRBC ); + + if( hitted ) + cRBC = CCOLORRGB( shadeHit( bgColorY, rayRBC, hitInfoRBC, false, 0 ) ); + } + + + // Set pixel colors + // ///////////////////////////////////////////////////////////// + + GLubyte *ptr = &ptrPBO[ (4 * x + m_blockPositionsFast[iBlock].x + + m_realBufferSize.x * + (m_blockPositionsFast[iBlock].y + 4 * y)) * 4 ]; + SetPixel( ptr + 0, cLT ); + SetPixel( ptr + 4, BlendColor( cLT, cLRT, cLTC ) ); + SetPixel( ptr + 8, cLRT ); + SetPixel( ptr + 12, BlendColor( cLRT, cRT, cRTC ) ); + + ptr += m_realBufferSize.x * 4; + SetPixel( ptr + 0, BlendColor( cLT , cLTB, cLTC ) ); + SetPixel( ptr + 4, BlendColor( cLTC, BlendColor( cLT , cC ) ) ); + SetPixel( ptr + 8, BlendColor( cC, BlendColor( cLRT, cLTC, cRTC ) ) ); + SetPixel( ptr + 12, BlendColor( cRTC, BlendColor( cRT , cC ) ) ); + + ptr += m_realBufferSize.x * 4; + SetPixel( ptr + 0, cLTB ); + SetPixel( ptr + 4, BlendColor( cC, BlendColor( cLTB, cLTC, cLBC ) ) ); + SetPixel( ptr + 8, cC ); + SetPixel( ptr + 12, BlendColor( cC, BlendColor( cRTB, cRTC, cRBC ) ) ); + + ptr += m_realBufferSize.x * 4; + SetPixel( ptr + 0, BlendColor( cLB , cLTB, cLBC ) ); + SetPixel( ptr + 4, BlendColor( cLBC, BlendColor( cLB , cC ) ) ); + SetPixel( ptr + 8, BlendColor( cC, BlendColor( cLRB, cLBC, cRBC ) ) ); + SetPixel( ptr + 12, BlendColor( cRBC, BlendColor( cRB , cC ) ) ); + } + } + } + + // Wait for all threads to finish (not sure if this is need) + #pragma omp barrier +} + + +#define USE_EXPERIMENTAL_SOFT_SHADOWS 1 + +SFVEC3F C3D_RENDER_RAYTRACING::shadeHit( const SFVEC3F &aBgColor, + const RAY &aRay, + HITINFO &aHitInfo, + bool aIsInsideObject, + unsigned int aRecursiveLevel ) const +{ + if( aRecursiveLevel > 2 ) + return SFVEC3F( 0.0f ); + + SFVEC3F hitPoint; + + if( m_isPreview ) + hitPoint = aRay.at( aHitInfo.m_tHit ); + else + hitPoint = aRay.at( aHitInfo.m_tHit ) + + aHitInfo.m_HitNormal * ( 0.5f * m_settings.GetNonCopperLayerThickness3DU() * + glm::abs(Fast_RandFloat()) + + 0.5f * m_settings.GetNonCopperLayerThickness3DU() ); + + const CMATERIAL *objMaterial = aHitInfo.pHitObject->GetMaterial(); + wxASSERT( objMaterial != NULL ); + + const SFVEC3F diffuseColorObj = aHitInfo.pHitObject->GetDiffuseColor( aHitInfo ); + + SFVEC3F outColor = objMaterial->GetEmissiveColor(); + + const LIST_LIGHT &lightList = m_lights.GetList(); + + const bool is_testShadow = m_settings.GetFlag( FL_RENDER_RAYTRACING_SHADOWS ) && + (!m_isPreview); + +#if USE_EXPERIMENTAL_SOFT_SHADOWS + const bool is_aa_enabled = m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) && + (!m_isPreview); +#endif + + float shadow_att_factor_sum = 0.0f; + + unsigned int nr_lights_that_can_cast_shadows = 0; + + for( LIST_LIGHT::const_iterator ii = lightList.begin(); + ii != lightList.end(); + ++ii ) + { + const CLIGHT *light = (CLIGHT *)*ii; + + SFVEC3F vectorToLight; + SFVEC3F colorOfLight; + float distToLight; + + light->GetLightParameters( hitPoint, vectorToLight, colorOfLight, distToLight ); + + if( m_isPreview ) + colorOfLight = SFVEC3F( 1.0f ); + + /* + if( (!m_isPreview) && + // Little hack to make randomness to the shading and shadows + m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ) + vectorToLight = glm::normalize( vectorToLight + + UniformRandomHemisphereDirection() * 0.1f ); + */ + + const float NdotL = glm::dot( aHitInfo.m_HitNormal, vectorToLight ); + + // Only calc shade if the normal is facing the direction of light, + // otherwise it is in the shadow + if( NdotL >= FLT_EPSILON ) + { + float shadow_att_factor_light = 1.0f; + + if( is_testShadow && light->GetCastShadows() ) + { + nr_lights_that_can_cast_shadows++; +#if USE_EXPERIMENTAL_SOFT_SHADOWS + if( (!is_aa_enabled) || + + // For rays that are recursive, just calculate one hit shadow + (aRecursiveLevel > 0) || + + // Only use soft shadows if using post processing + (!m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ) + ) + { +#endif + RAY rayToLight; + rayToLight.Init( hitPoint, vectorToLight ); + + // Test if point is not in the shadow. + // Test for any hit from the point in the direction of light + if( m_accelerator->IntersectP( rayToLight, distToLight ) ) + shadow_att_factor_light = 0.0f; + +#if USE_EXPERIMENTAL_SOFT_SHADOWS + } + + // Experimental softshadow calculation + else + { + + const unsigned int shadow_number_of_samples = 2; + const float shadow_inc_factor = 1.0f / (float)(shadow_number_of_samples); + + for( unsigned int i=0; i < shadow_number_of_samples; ++i ) + { + const SFVEC3F unifVector = UniformRandomHemisphereDirection(); + const SFVEC3F disturbed_vector_to_light = glm::normalize( vectorToLight + + unifVector * + 0.05f ); + + RAY rayToLight; + rayToLight.Init( hitPoint, disturbed_vector_to_light ); + + // !TODO: there are multiple ways that this tests can be + // optimized. Eg: by packing rays or to test against the + // latest hit object. + + if( m_accelerator->IntersectP( rayToLight, distToLight ) ) + { + shadow_att_factor_light -= shadow_inc_factor; + } + } + } +#endif + shadow_att_factor_sum += shadow_att_factor_light; + } + + if( !m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ) + { + outColor += objMaterial->Shade( aRay, + aHitInfo, + NdotL, + diffuseColorObj, + vectorToLight, + colorOfLight, + shadow_att_factor_light ); + } + else + { + // This is a render hack in order to compensate for the lake of + // ambient and too much darkness when using post process shadeer + // It will calculate as it was not in shadow + outColor += objMaterial->Shade( aRay, + aHitInfo, + NdotL, + diffuseColorObj, + vectorToLight, + colorOfLight, + 1.0f ); + } + } + + if( nr_lights_that_can_cast_shadows > 0 ) + { + aHitInfo.m_ShadowFactor = shadow_att_factor_sum / + (float)(nr_lights_that_can_cast_shadows * 1.0f); + } + else + { + aHitInfo.m_ShadowFactor = 1.0f; + } + + // Only use the headlight for preview + if( m_isPreview ) + break; + } + + // Clamp color to not be brighter than 1.0f + outColor = glm::min( outColor, SFVEC3F( 1.0f ) ); + + if( !m_isPreview ) + { + // Reflections + // ///////////////////////////////////////////////////////////////////// + + if( !aIsInsideObject && + (objMaterial->GetReflection() > 0.0f) && + m_settings.GetFlag( FL_RENDER_RAYTRACING_REFLECTIONS ) ) + { + const unsigned int reflection_number_of_samples = 3; + + SFVEC3F sum_color = SFVEC3F(0.0f); + + const SFVEC3F reflectVector = aRay.m_Dir - + 2.0f * glm::dot( aRay.m_Dir, aHitInfo.m_HitNormal ) * + aHitInfo.m_HitNormal; + + for( unsigned int i = 0; i < reflection_number_of_samples; ++i ) + { + // If we want to apply some randomize to the reflected vector + const SFVEC3F random_reflectVector = + glm::normalize( reflectVector + + UniformRandomHemisphereDirection() * + 0.02f ); + + RAY reflectedRay; + reflectedRay.Init( hitPoint, random_reflectVector ); + + HITINFO reflectedHit; + reflectedHit.m_tHit = std::numeric_limits::infinity(); + + if( m_accelerator->Intersect( reflectedRay, reflectedHit ) ) + { + sum_color += objMaterial->GetReflection() * + shadeHit( aBgColor, + reflectedRay, + reflectedHit, + false, + aRecursiveLevel + 1 ) * + (1.0f / ( 1.0f + 0.75f * reflectedHit.m_tHit * + reflectedHit.m_tHit) ); // Falloff factor + } + } + + outColor += (sum_color / SFVEC3F( (float)reflection_number_of_samples) ); + } + + + // Refractions + // ///////////////////////////////////////////////////////////////////// + + if( (objMaterial->GetTransparency() > 0.0f) && + m_settings.GetFlag( FL_RENDER_RAYTRACING_REFRACTIONS ) ) + { + const float airIndex = 1.000293f; + const float glassIndex = 1.49f; + const float air_over_glass = airIndex / glassIndex; + const float glass_over_air = glassIndex / airIndex; + + float refractionRatio = aIsInsideObject?glass_over_air:air_over_glass; + + SFVEC3F refractedVector; + + if( Refract( aRay.m_Dir, + aHitInfo.m_HitNormal, + refractionRatio, + refractedVector ) ) + { + // If we want to apply some randomize to the refracted vector + //refractedVector = refractedVector + UniformRandomHemisphereDirection() * 0.01f; + refractedVector = glm::normalize( refractedVector ); + + // This increase the start point by a "fixed" factor so it will work the + // same for all distances + const SFVEC3F startPoint = aRay.at( NextFloatUp( + NextFloatUp( + NextFloatUp( aHitInfo.m_tHit ) ) ) ); + + RAY refractedRay; + refractedRay.Init( startPoint, refractedVector ); + + HITINFO refractedHit; + refractedHit.m_tHit = std::numeric_limits::infinity(); + + SFVEC3F refractedColor = aBgColor; + + float objTransparency = objMaterial->GetTransparency(); + + if( m_accelerator->Intersect( refractedRay, refractedHit ) ) + { + refractedColor = shadeHit( aBgColor, + refractedRay, + refractedHit, + true, + aRecursiveLevel + 1 ); + + const SFVEC3F absorbance = ( SFVEC3F(1.0f) - diffuseColorObj ) * + (1.0f - objTransparency ) * + 1.0000f * // Adjust falloff factor + -refractedHit.m_tHit; + + const SFVEC3F transparency = SFVEC3F( expf( absorbance.r ), + expf( absorbance.g ), + expf( absorbance.b ) ); + + outColor = outColor * (1.0f - objTransparency) + + refractedColor * transparency * objTransparency; + } + else + { + outColor = outColor * (1.0f - objTransparency) + + refractedColor * objTransparency; + } + } + } + } + + //outColor += glm::max( -glm::dot( aHitInfo.m_HitNormal, aRay.m_Dir ), 0.0f ) * + // objMaterial->GetAmbientColor(); + + return outColor; +} + + +void C3D_RENDER_RAYTRACING::initializeNewWindowSize() +{ + opengl_init_pbo(); +} + + +void C3D_RENDER_RAYTRACING::opengl_init_pbo() +{ + if( GLEW_ARB_pixel_buffer_object ) + { + m_opengl_support_vertex_buffer_object = true; + + // Try to delete vbo if it was already initialized + opengl_delete_pbo(); + + // Learn about Pixel buffer objects at: + // http://www.songho.ca/opengl/gl_pbo.html + // http://web.eecs.umich.edu/~sugih/courses/eecs487/lectures/25-PBO+Mipmapping.pdf + // "create 2 pixel buffer objects, you need to delete them when program exits. + // glBufferDataARB with NULL pointer reserves only memory space." + + // This sets the number of RGBA pixels + m_pboDataSize = m_realBufferSize.x * m_realBufferSize.y * 4; + + glGenBuffersARB( 1, &m_pboId ); + glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId ); + glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboDataSize, 0, GL_STREAM_DRAW_ARB ); + glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 ); + + wxLogTrace( m_logTrace, + wxT( "C3D_RENDER_RAYTRACING:: GLEW_ARB_pixel_buffer_object is supported" ) ); + } +} + + +bool C3D_RENDER_RAYTRACING::initializeOpenGL() +{ + m_is_opengl_initialized = true; + + return true; +} + + +void C3D_RENDER_RAYTRACING::initialize_block_positions() +{ + + m_realBufferSize = SFVEC2UI(); + + // Calc block positions for fast preview mode + // ///////////////////////////////////////////////////////////////////// + m_blockPositionsFast.clear(); + + unsigned int i = 0; + + while(1) + { + const unsigned int mX = DecodeMorton2X(i); + const unsigned int mY = DecodeMorton2Y(i); + + i++; + + const SFVEC2UI blockPos( mX * 4 * RAYPACKET_DIM - mX * 4, + mY * 4 * RAYPACKET_DIM - mY * 4); + + if( ( blockPos.x >= ( (unsigned int)m_windowSize.x - ( 4 * RAYPACKET_DIM + 4 ) ) ) && + ( blockPos.y >= ( (unsigned int)m_windowSize.y - ( 4 * RAYPACKET_DIM + 4 ) ) ) ) + break; + + if( ( blockPos.x < ( (unsigned int)m_windowSize.x - ( 4 * RAYPACKET_DIM + 4) ) ) && + ( blockPos.y < ( (unsigned int)m_windowSize.y - ( 4 * RAYPACKET_DIM + 4) ) ) ) + { + m_blockPositionsFast.push_back( blockPos ); + + if( blockPos.x > m_realBufferSize.x ) + m_realBufferSize.x = blockPos.x; + + if( blockPos.y > m_realBufferSize.y ) + m_realBufferSize.y = blockPos.y; + } + } + + m_fastPreviewModeSize = m_realBufferSize; + + m_realBufferSize.x = ((m_realBufferSize.x + RAYPACKET_DIM * 4) & RAYPACKET_INVMASK); + m_realBufferSize.y = ((m_realBufferSize.y + RAYPACKET_DIM * 4) & RAYPACKET_INVMASK); + + m_xoffset = (m_windowSize.x - m_realBufferSize.x) / 2; + m_yoffset = (m_windowSize.y - m_realBufferSize.y) / 2; + + m_postshader_ssao.UpdateSize( m_realBufferSize ); + + + // Calc block positions + // ///////////////////////////////////////////////////////////////////// + m_blockPositions.clear(); + m_blockPositions.reserve( (m_realBufferSize.x / RAYPACKET_DIM) * + (m_realBufferSize.y / RAYPACKET_DIM) ); + + i = 0; + + while(1) + { + SFVEC2UI blockPos( DecodeMorton2X(i) * RAYPACKET_DIM, + DecodeMorton2Y(i) * RAYPACKET_DIM ); + i++; + + if( (blockPos.x >= m_realBufferSize.x) && (blockPos.y >= m_realBufferSize.y) ) + break; + + if( (blockPos.x < m_realBufferSize.x) && (blockPos.y < m_realBufferSize.y) ) + m_blockPositions.push_back( blockPos ); + } + + // Create m_shader buffer + delete m_shaderBuffer; + m_shaderBuffer = new SFVEC3F[m_realBufferSize.x * m_realBufferSize.y]; + + opengl_init_pbo(); +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.h b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.h new file mode 100644 index 0000000000..e0424ea03a --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.h @@ -0,0 +1,187 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file c3d_render_raytracing.h + * @brief + */ + +#ifndef C3D_RENDER_RAYTRACING_H +#define C3D_RENDER_RAYTRACING_H + +#include "../../common_ogl/openGL_includes.h" +#include "accelerators/ccontainer.h" +#include "accelerators/caccelerator.h" +#include "../c3d_render_base.h" +#include "clight.h" +#include "../cpostshader_ssao.h" +#include "cmaterial.h" +#include + +#include + +/// Vector of materials +typedef std::vector< CBLINN_PHONG_MATERIAL > MODEL_MATERIALS; + +/// Maps a S3DMODEL pointer with a created CBLINN_PHONG_MATERIAL vector +typedef std::map< const S3DMODEL * , MODEL_MATERIALS > MAP_MODEL_MATERIALS; + +typedef enum +{ + RT_RENDER_STATE_TRACING = 0, + RT_RENDER_STATE_POST_PROCESS_SHADE, + RT_RENDER_STATE_POST_PROCESS_BLUR_AND_FINISH, + RT_RENDER_STATE_FINISH, + RT_RENDER_STATE_MAX +}RT_RENDER_STATE; + +class C3D_RENDER_RAYTRACING : public C3D_RENDER_BASE +{ +public: + explicit C3D_RENDER_RAYTRACING( CINFO3D_VISU &aSettings ); + + ~C3D_RENDER_RAYTRACING(); + + // Imported from C3D_RENDER_BASE + void SetCurWindowSize( const wxSize &aSize ); + bool Redraw(bool aIsMoving, REPORTER *aStatusTextReporter ); + + int GetWaitForEditingTimeOut(); + +private: + bool initializeOpenGL(); + void initializeNewWindowSize(); + void opengl_init_pbo(); + void opengl_delete_pbo(); + void reload( REPORTER *aStatusTextReporter ); + + void restart_render_state(); + void rt_render_tracing( GLubyte *ptrPBO , REPORTER *aStatusTextReporter ); + void rt_render_post_process_shade( GLubyte *ptrPBO , REPORTER *aStatusTextReporter ); + void rt_render_post_process_blur_finish( GLubyte *ptrPBO , REPORTER *aStatusTextReporter ); + void rt_render_trace_block( GLubyte *ptrPBO , signed int iBlock ); + + // Materials + void setupMaterials(); + + struct + { + CBLINN_PHONG_MATERIAL m_Paste; + CBLINN_PHONG_MATERIAL m_SilkS; + CBLINN_PHONG_MATERIAL m_SolderMask; + CBLINN_PHONG_MATERIAL m_EpoxyBoard; + CBLINN_PHONG_MATERIAL m_Copper; + CBLINN_PHONG_MATERIAL m_Floor; + }m_materials; + + bool m_isPreview; + + SFVEC3F shadeHit( const SFVEC3F &aBgColor, + const RAY &aRay, + HITINFO &aHitInfo, + bool aIsInsideObject, + unsigned int aRecursiveLevel ) const; + + /// State used on quality render + RT_RENDER_STATE m_rt_render_state; + + /// Time that the render starts + unsigned long int m_stats_start_rendering_time; + + /// Save the number of blocks progress of the render + unsigned int m_nrBlocksRenderProgress; + + CPOSTSHADER_SSAO m_postshader_ssao; + + CLIGHTCONTAINER m_lights; + + CDIRECTIONALLIGHT *m_camera_light; + + bool m_opengl_support_vertex_buffer_object; + GLuint m_pboId; + GLuint m_pboDataSize; + + CCONTAINER m_object_container; + + /// This will store the list of created objects special for RT, + /// that will be clear in the end + CCONTAINER2D m_containerWithObjectsToDelete; + + CCONTAINER2D *m_outlineBoard2dObjects; + + CGENERICACCELERATOR *m_accelerator; + + + // Morton codes + + /// used to see if the windows size changed + wxSize m_oldWindowsSize; + + /// this encodes the Morton code positions + std::vector< SFVEC2UI > m_blockPositions; + + /// this flags if a position was already processed (cleared each new render) + std::vector< bool > m_blockPositionsWasProcessed; + + /// this encodes the Morton code positions (on fast preview mode) + std::vector< SFVEC2UI > m_blockPositionsFast; + + SFVEC2UI m_realBufferSize; + SFVEC2UI m_fastPreviewModeSize; + + HITINFO_PACKET *m_firstHitinfo; + + SFVEC3F *m_shaderBuffer; + + // Display Offset + unsigned int m_xoffset; + unsigned int m_yoffset; + + // Statistics + unsigned int m_stats_converted_dummy_to_plane; + unsigned int m_stats_converted_roundsegment2d_to_roundsegment; + + void create_3d_object_from( CCONTAINER &aDstContainer, + const COBJECT2D *aObject2D, + float aZMin, float aZMax, + const CMATERIAL *aMaterial, + const SFVEC3F &aObjColor ); + + void add_3D_vias_and_pads_to_container(); + void insert3DViaHole( const VIA* aVia ); + void insert3DPadHole( const D_PAD* aPad ); + void load_3D_models(); + void add_3D_models( const S3DMODEL *a3DModel, + const glm::mat4 &aModelMatrix ); + + /// Stores materials of the 3D models + MAP_MODEL_MATERIALS m_model_materials; + + void initialize_block_positions(); + + void render( GLubyte *ptrPBO, REPORTER *aStatusTextReporter ); + void render_preview( GLubyte *ptrPBO ); +}; + +#endif // C3D_RENDER_RAYTRACING_H diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/cfrustum.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/cfrustum.cpp new file mode 100644 index 0000000000..b4f69cc9d2 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/cfrustum.cpp @@ -0,0 +1,115 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cfrustum.cpp + * @brief + */ + + +#include "cfrustum.h" + +// !TODO: optimize wih SSE +//#if(GLM_ARCH != GLM_ARCH_PURE) +#if 0 +#error not implemented +#else + + +#endif + +void CFRUSTUM::GenerateFrustum( const RAY &topLeft, + const RAY &topRight, + const RAY &bottomLeft, + const RAY &bottomRight ) +{ + m_point[0] = topLeft.m_Origin; + m_point[1] = topRight.m_Origin; + m_point[2] = bottomLeft.m_Origin; + m_point[3] = topLeft.m_Origin; + + if( topRight.m_Dir == topLeft.m_Dir ) + { + // This will be the case if the camera is Ortho projection + + m_normals[0] = glm::normalize( bottomLeft.m_Origin - topLeft.m_Origin ); // TOP + m_normals[1] = glm::normalize( topLeft.m_Origin - topRight.m_Origin ); // RIGHT + m_normals[2] = -m_normals[0]; // BOTTOM + m_normals[3] = -m_normals[1]; // LEFT + } + else + { + m_normals[0] = glm::cross( topRight.m_Dir, topLeft.m_Dir ); // TOP + m_normals[1] = glm::cross( bottomRight.m_Dir, topRight.m_Dir ); // RIGHT + m_normals[2] = glm::cross( bottomLeft.m_Dir, bottomRight.m_Dir ); // BOTTOM + m_normals[3] = glm::cross( topLeft.m_Dir, bottomLeft.m_Dir ); // LEFT + } +} + + +// There are multiple implementation of this algorithm on the web, +// this one was based on the one find in: +// https://github.com/nslo/raytracer/blob/2c2e0ff4bbb6082e07804ec7cf0b92673b98dcb1/src/raytracer/geom_utils.cpp#L66 +// by Nathan Slobody and Adam Wright +// The frustum test is not exllude all the boxes, +// when a box is behind and if it is intersecting the planes it will not be discardly but should. +bool CFRUSTUM::Intersect( const CBBOX &aBBox ) const +{ + const SFVEC3F box[8] = { aBBox.Min(), + aBBox.Max(), + SFVEC3F(aBBox.Min().x, aBBox.Min().y, aBBox.Max().z), + SFVEC3F(aBBox.Min().x, aBBox.Max().y, aBBox.Min().z), + SFVEC3F(aBBox.Min().x, aBBox.Max().y, aBBox.Max().z), + SFVEC3F(aBBox.Max().x, aBBox.Min().y, aBBox.Min().z), + SFVEC3F(aBBox.Max().x, aBBox.Min().y, aBBox.Max().z), + SFVEC3F(aBBox.Max().x, aBBox.Max().y, aBBox.Min().z) }; + + // test each plane of frustum individually; if the point is on the wrong + // side of the plane, the box is outside the frustum and we can exit + int out_side = 0; + + for( unsigned int i = 0; i < 4; ++i ) + { + const SFVEC3F &pointPlane = m_point[i]; + const SFVEC3F &normalPlane = m_normals[i]; + + for( unsigned int j = 0; j < 8; ++j ) + { + const SFVEC3F OP = pointPlane - box[j]; + const float dot = glm::dot( OP, normalPlane ); + + if( dot < 0.0f ) + { + out_side++; + + break; + } + } + } + + if( out_side == 4 ) + return true; + + return false; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/cfrustum.h b/3d-viewer/3d_rendering/3d_render_raytracing/cfrustum.h index a8d994785c..db20870edc 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/cfrustum.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/cfrustum.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,13 +24,12 @@ /** * @file cfrustum.h - * @brief + * @brief implements a frustum that is used to test ray pack tests */ #ifndef _CFRUSTUM_H_ #define _CFRUSTUM_H_ -#include "plugins/3dapi/xv3d_types.h" #include "shapes3D/cbbox.h" #include "ray.h" @@ -39,13 +38,32 @@ #if 0 #error not implemented #else -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) CFRUSTUM +struct CFRUSTUM { - SFVEC3F m_normals[4]; - SFVEC3F m_point[4]; - void GenerateFrustum( const RAY &topLeft, const RAY &topRight, const RAY &bottomLeft, const RAY &bottomRight ); +public: + /** + * @brief GenerateFrustum + * @param topLeft + * @param topRight + * @param bottomLeft + * @param bottomRight + */ + void GenerateFrustum( const RAY &topLeft, + const RAY &topRight, + const RAY &bottomLeft, + const RAY &bottomRight ); + + /** + * @brief Intersect - Intersects a bbox with this frustum + * @param aBBox: a bbox to test + * @return true if the bbox intersects this frustum + */ bool Intersect( const CBBOX &aBBox ) const; + +private: + SFVEC3F m_normals[4]; + SFVEC3F m_point[4]; }; #endif diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/clight.h b/3d-viewer/3d_rendering/3d_render_raytracing/clight.h new file mode 100644 index 0000000000..40c0a128b0 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/clight.h @@ -0,0 +1,203 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file clight.h + * @brief declare and implement light types classes + */ + +#ifndef _CLIGHT_H_ +#define _CLIGHT_H_ + +#include "ray.h" +#include "hitinfo.h" + +/// A base light class to derive to implement other light classes +class CLIGHT +{ +public: + CLIGHT() { m_castShadow = true; } + + virtual ~CLIGHT() {} + + /** + * @brief GetLightParameters - Get parameters from this light + * @param aHitPoint: input hit position + * @param aOutVectorToLight: a vector that points from the hit + * position in direction to the light + * @param aOutLightColor: the color of this light + * @param aOutDistance: the distance from the point to the light + */ + virtual void GetLightParameters( const SFVEC3F &aHitPoint, + SFVEC3F &aOutVectorToLight, + SFVEC3F &aOutLightColor, + float &aOutDistance ) const = 0; + + void SetCastShadows( bool aCastShadow ) { m_castShadow = aCastShadow; } + bool GetCastShadows() const { return m_castShadow; } + +protected: + bool m_castShadow; +}; + + +/// Point light based on: +/// http://ogldev.atspace.co.uk/www/tutorial20/tutorial20.html +class CPOINTLIGHT : public CLIGHT +{ + +public: + CPOINTLIGHT( const SFVEC3F &aPos, const SFVEC3F &aColor ) + { + m_position = aPos; + m_color = aColor; + m_att_constant = 1.0f; + m_att_linear = 0.5f; + m_att_exp = 0.25f; + m_castShadow = true; + } + + // Imported functions from CLIGHT + + void GetLightParameters( const SFVEC3F &aHitPoint, + SFVEC3F &aOutVectorToLight, + SFVEC3F &aOutLightColor, + float &aOutDistance ) const + { + const SFVEC3F vectorLight = m_position - aHitPoint; + + aOutDistance = glm::length( vectorLight ); + aOutVectorToLight = vectorLight / aOutDistance; // normalize + + + float att = 1.0f / ( m_att_constant + + m_att_linear * aOutDistance + + m_att_exp * aOutDistance * aOutDistance ); + + if( att <= 0.0f ) + aOutLightColor = SFVEC3F( 0.0f, 0.0f, 0.0f ); + else + aOutLightColor = m_color * att; + } + +private: + SFVEC3F m_position; + SFVEC3F m_color; + + float m_att_constant; + float m_att_linear; + float m_att_exp; +}; + + +/// Directional light - a light based only on a direction vector +class CDIRECTIONALLIGHT : public CLIGHT +{ +public: + CDIRECTIONALLIGHT( const SFVEC3F &aDir, const SFVEC3F &aColor ) + { + // Invert light direction and make sure it is normalized + m_inv_direction = glm::normalize( -aDir ); + m_color = aColor; + m_castShadow = true; // Set as default to cast shadows + } + + /** + * @brief SetDirection - Set directional light orientation + * @param aDir: vector from the light + */ + void SetDirection( const SFVEC3F &aDir ) { m_inv_direction = -aDir; } + + // Imported functions from CLIGHT + + void GetLightParameters( const SFVEC3F &aHitPoint, + SFVEC3F &aOutVectorToLight, + SFVEC3F &aOutLightColor, + float &aOutDistance ) const + { + (void)aHitPoint; // unused + + aOutVectorToLight = m_inv_direction; + aOutDistance = std::numeric_limits::infinity(); + aOutLightColor = m_color; + } + +private: + SFVEC3F m_inv_direction; ///< oposite direction of the light + SFVEC3F m_color; ///< light color +}; + + +typedef std::list< CLIGHT * > LIST_LIGHT; + + +/// A light contariner. It will add lights and remove it in the end +class CLIGHTCONTAINER +{ +public: + CLIGHTCONTAINER() {} + + ~CLIGHTCONTAINER() { Clear(); } + + /** + * @brief Clear - Remove all lights from the container + */ + void Clear() + { + if( !m_lights.empty() ) + { + for( LIST_LIGHT::iterator ii = m_lights.begin(); + ii != m_lights.end(); + --ii ) + { + delete *ii; + *ii = NULL; + } + + m_lights.clear(); + } + } + + + /** + * @brief Add - Add a light to the container + * @param aLight + */ + void Add( CLIGHT *aLight ) + { + if( aLight ) + m_lights.push_back( aLight ); + } + + /** + * @brief GetList - get light list of this container + * @return a list of lights + */ + const LIST_LIGHT &GetList() const { return m_lights; } + +private: + LIST_LIGHT m_lights; ///< list of lights +}; + +#endif // _CLIGHT_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/cmaterial.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/cmaterial.cpp new file mode 100644 index 0000000000..cf3890e894 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/cmaterial.cpp @@ -0,0 +1,119 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cmaterial.cpp + * @brief + */ + +#include "cmaterial.h" +#include + + +CMATERIAL::CMATERIAL() +{ + m_ambientColor = SFVEC3F( 0.2f, 0.2f, 0.2f ); + m_emissiveColor = SFVEC3F( 0.0f, 0.0f, 0.0f ); + m_specularColor = SFVEC3F( 1.0f, 1.0f, 1.0f ); + m_shinness = 50.2f; + m_transparency = 0.0f; // completely opaque + m_cast_shadows = true; + m_reflection = 0.0f; +} + + +CMATERIAL::CMATERIAL( const SFVEC3F &aAmbient, + const SFVEC3F &aEmissive, + const SFVEC3F &aSpecular, + float aShinness, + float aTransparency, + float aReflection ) +{ + wxASSERT( aReflection >= 0.0f ); + wxASSERT( aReflection <= 1.0f ); + + wxASSERT( aTransparency >= 0.0f ); + wxASSERT( aTransparency <= 1.0f ); + + wxASSERT( aShinness >= 0.0f ); + wxASSERT( aShinness <= 180.0f ); + + m_ambientColor = aAmbient; + m_emissiveColor = aEmissive; + m_specularColor = aSpecular; + m_shinness = aShinness; + m_transparency = aTransparency; + m_reflection = aReflection; + m_cast_shadows = true; +} + + +// This may be a good value if based on nr of lights +// that contribute to the illumination of that point +#define AMBIENT_FACTOR 0.160f +#define SPECULAR_FACTOR 1.000f + +// https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model +SFVEC3F CBLINN_PHONG_MATERIAL::Shade( const RAY &aRay, + const HITINFO &aHitInfo, + float NdotL, + const SFVEC3F &aDiffuseObjColor, + const SFVEC3F &aDirToLight, + const SFVEC3F &aLightColor, + float aShadowAttenuationFactor ) const +{ + wxASSERT( NdotL >= FLT_EPSILON ); + + //const float ambientFactor = AMBIENT_FACTOR; + + // This is a hack to get some kind of fake ambient illumination + // There is no logic behind this, just pure artistic experimentation + const float ambientFactor = glm::max( ( (1.0f - NdotL) /** (1.0f - NdotL)*/ ) * + ( AMBIENT_FACTOR + AMBIENT_FACTOR ), + AMBIENT_FACTOR ); + + if( aShadowAttenuationFactor > FLT_EPSILON ) + { + // Calculate the diffuse light factoring in light color, + // power and the attenuation + const SFVEC3F diffuse = NdotL * aLightColor; + + // Calculate the half vector between the light vector and the view vector. + const SFVEC3F H = glm::normalize( aDirToLight - aRay.m_Dir ); + + //Intensity of the specular light + const float NdotH = glm::dot( H, aHitInfo.m_HitNormal ); + const float intensitySpecular = glm::pow( glm::max( NdotH, 0.0f ), + m_shinness ); + + return m_ambientColor * ambientFactor + + aShadowAttenuationFactor * ( diffuse * aDiffuseObjColor + + SPECULAR_FACTOR * + aLightColor * + intensitySpecular * + m_specularColor ); + } + + return m_ambientColor * ambientFactor; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/cmaterial.h b/3d-viewer/3d_rendering/3d_render_raytracing/cmaterial.h index b207ae28b2..877b3ea31b 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/cmaterial.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/cmaterial.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -33,37 +33,57 @@ #include "ray.h" #include "hitinfo.h" -/// -class GLM_ALIGN(CLASS_ALIGNMENT) CMATERIAL + +/// A base material class that can be used to derive a material implementation +class CMATERIAL { public: CMATERIAL(); - CMATERIAL( const SFVEC3F &aAmbient, const SFVEC3F &aEmissive, const SFVEC3F &aSpecular, float aShinness, float aTransparency ); + CMATERIAL( const SFVEC3F &aAmbient, + const SFVEC3F &aEmissive, + const SFVEC3F &aSpecular, + float aShinness, + float aTransparency, + float aReflection ); - const SFVEC3F &GetAmbientColor() const { return m_ambientColor; } + const SFVEC3F &GetAmbientColor() const { return m_ambientColor; } const SFVEC3F &GetEmissiveColor() const { return m_emissiveColor; } const SFVEC3F &GetSpecularColor() const { return m_specularColor; } - float GetShinness() const { return m_shinness; } + float GetShinness() const { return m_shinness; } float GetTransparency() const { return m_transparency; } + float GetReflection() const { return m_reflection; } + + /** + * @brief SetCastShadows - Set if the material can receive shadows + * @param aCastShadows - true yes it can, false not it cannot + */ + void SetCastShadows( bool aCastShadows ) { m_cast_shadows = aCastShadows; } - void SetCastShadows( bool aCastShadows ) { m_cast_shadows = aCastShadows; } bool GetCastShadows() const { return m_cast_shadows; } - virtual SFVEC3F Shade( const RAY &aRay, const HITINFO &aHitInfo, float NdotL, const SFVEC3F &aDiffuseObjColor, const SFVEC3F &aDirToLight, const SFVEC3F &aLightColor, bool aIsInShadow ) const = 0; + /** + * @brief Shade - Shades an intersection point + * @param aRay: the camera ray that hits the object + * @param aHitInfo: the hit information + * @param NdotL: the dot product between Normal and Light + * @param aDiffuseObjColor: diffuse object color + * @param aDirToLight: a vector of the incident light direction + * @param aLightColor: the light color + * @param aShadowAttenuationFactor 0.0f total in shadow, 1.0f completely not in shadow + * @return the resultant color + */ + virtual SFVEC3F Shade( const RAY &aRay, + const HITINFO &aHitInfo, + float NdotL, + const SFVEC3F &aDiffuseObjColor, + const SFVEC3F &aDirToLight, + const SFVEC3F &aLightColor, + float aShadowAttenuationFactor ) const = 0; -/* - - - virtual SFVEC3F GetReflect(Vec3f point){ return reflectiveColor;} - virtual Vec3f GetTrans(Vec3f point){ return transparentColor;} - virtual float getIndexOfRefrac(Vec3f point){ return indexOfRefraction;} - - virtual bool isReflect(Vec3f point){return ((reflectiveColor.x()>0)||(reflectiveColor.y()>0)||(reflectiveColor.z()>0));} - virtual bool isTransparent(Vec3f point){return ((transparentColor.x()>0)||(transparentColor.y()>0)||(transparentColor.z()>0));} -*/ protected: SFVEC3F m_ambientColor; + // NOTE: we will not use diffuse color material here, // because it will be stored in object, since there are objects (i.e: triangles) // that can have per vertex color @@ -72,12 +92,14 @@ protected: SFVEC3F m_specularColor; float m_shinness; float m_transparency; ///< 1.0 is completely transparent, 0.0 completely opaque + float m_reflection; ///< 1.0 completely reflective, 0.0 no reflective bool m_cast_shadows; ///< true if this object will block the light }; +/// Blinn Phong based material /// https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model -class GLM_ALIGN(CLASS_ALIGNMENT) CBLINN_PHONG_MATERIAL : public CMATERIAL +class CBLINN_PHONG_MATERIAL : public CMATERIAL { public: CBLINN_PHONG_MATERIAL() : CMATERIAL() {} @@ -86,10 +108,22 @@ public: const SFVEC3F &aEmissive, const SFVEC3F &aSpecular, float aShinness, - float aTransparency ) : - CMATERIAL( aAmbient, aEmissive, aSpecular, aShinness, aTransparency ) {} + float aTransparency, + float aReflection ) : CMATERIAL( aAmbient, + aEmissive, + aSpecular, + aShinness, + aTransparency, + aReflection ) {} - SFVEC3F Shade( const RAY &aRay, const HITINFO &aHitInfo, float NdotL, const SFVEC3F &aDiffuseObjColor, const SFVEC3F &aDirToLight, const SFVEC3F &aLightColor, bool aIsInShadow ) const; + // Imported from CMATERIAL + SFVEC3F Shade( const RAY &aRay, + const HITINFO &aHitInfo, + float NdotL, + const SFVEC3F &aDiffuseObjColor, + const SFVEC3F &aDirToLight, + const SFVEC3F &aLightColor, + float aShadowAttenuationFactor ) const; }; #endif // _CMATERIAL_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/hitinfo.h b/3d-viewer/3d_rendering/3d_render_raytracing/hitinfo.h index c1f6c7c9e8..7c4d75130f 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/hitinfo.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/hitinfo.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,7 +30,6 @@ #ifndef _HITINFO_H_ #define _HITINFO_H_ -#include "plugins/3dapi/xv3d_types.h" #include "raypacket.h" //#define RAYTRACING_RAY_STATISTICS @@ -38,15 +37,16 @@ class COBJECT; /// Stores the hit information of a ray with a point on the surface of a object -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) HITINFO +struct HITINFO { SFVEC3F m_HitNormal; ///< (12) normal at the hit point float m_tHit; ///< ( 4) distance const COBJECT *pHitObject; ///< ( 4) Object that was hitted SFVEC2F m_UV; ///< ( 8) 2-D texture coordinates + unsigned int m_acc_node_info; ///< ( 4) The acc stores here the node that it hits - unsigned int m_acc_node_info; ///< ( 4) The acc should store here information (Ex: the node that it hits) + float m_ShadowFactor; ///< ( 4) Shadow attenuation (1.0 no shadow, 0.0f darkness) #ifdef RAYTRACING_RAY_STATISTICS // Statistics @@ -55,11 +55,11 @@ GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) HITINFO #endif }; -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) HITINFO_PACKET + +struct HITINFO_PACKET { bool m_hitresult; HITINFO m_HitInfo; - }; #endif // _HITINFO_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/mortoncodes.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/mortoncodes.cpp new file mode 100644 index 0000000000..05520b47c5 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/mortoncodes.cpp @@ -0,0 +1,125 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file mortoncodes.cpp + * @brief Implementes Morton Codes base on the implementation of Fabian “ryg” Giesen + * https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ + */ + +#include "mortoncodes.h" + + +// "Insert" a 0 bit after each of the 16 low bits of x +uint32_t Part1By1( uint32_t x ) +{ + x &= 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210 + x = (x ^ (x << 8)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210 + x = (x ^ (x << 4)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210 + x = (x ^ (x << 2)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10 + x = (x ^ (x << 1)) & 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + + return x; +} + + +// "Insert" two 0 bits after each of the 10 low bits of x +uint32_t Part1By2( uint32_t x ) +{ + x &= 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210 + x = (x ^ (x << 16)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210 + x = (x ^ (x << 8)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210 + x = (x ^ (x << 4)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10 + x = (x ^ (x << 2)) & 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 + + return x; +} + + +// Inverse of Part1By1 - "delete" all odd-indexed bits +uint32_t Compact1By1( uint32_t x ) +{ + x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10 + x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210 + x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210 + x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210 + + return x; +} + + +// Inverse of Part1By2 - "delete" all bits not at positions divisible by 3 +uint32_t Compact1By2( uint32_t x ) +{ + x &= 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 + x = (x ^ (x >> 2)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10 + x = (x ^ (x >> 4)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210 + x = (x ^ (x >> 8)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210 + x = (x ^ (x >> 16)) & 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210 + + return x; +} + + +uint32_t EncodeMorton2( uint32_t x, uint32_t y ) +{ + return ( Part1By1( y ) << 1 ) + Part1By1( x ); +} + + +uint32_t EncodeMorton3( uint32_t x, uint32_t y, uint32_t z ) +{ + return ( Part1By2( z ) << 2 ) + ( Part1By2( y ) << 1 ) + Part1By2( x ); +} + + +uint32_t DecodeMorton2X( uint32_t code ) +{ + return Compact1By1( code >> 0 ); +} + + +uint32_t DecodeMorton2Y( uint32_t code ) +{ + return Compact1By1( code >> 1 ); +} + + +uint32_t DecodeMorton3X( uint32_t code ) +{ + return Compact1By2( code >> 0 ); +} + + +uint32_t DecodeMorton3Y( uint32_t code ) +{ + return Compact1By2( code >> 1 ); +} + + +uint32_t DecodeMorton3Z( uint32_t code ) +{ + return Compact1By2( code >> 2 ); +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/mortoncodes.h b/3d-viewer/3d_rendering/3d_render_raytracing/mortoncodes.h new file mode 100644 index 0000000000..d8c845c96b --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/mortoncodes.h @@ -0,0 +1,47 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file mortoncodes.h + * @brief Implementes Morton Codes + * https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ + * http://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ + */ + +#ifndef _MORTONCODES_H_ +#define _MORTONCODES_H_ + +#include + +uint32_t EncodeMorton2( uint32_t x, uint32_t y ); +uint32_t EncodeMorton3( uint32_t x, uint32_t y, uint32_t z ); + +uint32_t DecodeMorton2X( uint32_t code ); +uint32_t DecodeMorton2Y( uint32_t code ); + +uint32_t DecodeMorton3X( uint32_t code ); +uint32_t DecodeMorton3Y( uint32_t code ); +uint32_t DecodeMorton3Z( uint32_t code ); + +#endif // _MORTONCODES_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/ray.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/ray.cpp index e8f11a8e95..27d5af5b42 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/ray.cpp +++ b/3d-viewer/3d_rendering/3d_render_raytracing/ray.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,7 +29,7 @@ #include "ray.h" -#include "3d_math/3d_fastmath.h" +#include "../../3d_fastmath.h" #include #include @@ -189,7 +189,9 @@ void RAY::Init( const SFVEC3F& o, const SFVEC3F& d ) bool IntersectSegment( const SFVEC2F &aStartA, const SFVEC2F &aEnd_minus_startA, const SFVEC2F &aStartB, const SFVEC2F &aEnd_minus_startB ) { - float rxs = aEnd_minus_startA.x * aEnd_minus_startB.y - aEnd_minus_startA.y * aEnd_minus_startB.x; + float rxs = aEnd_minus_startA.x * + aEnd_minus_startB.y - aEnd_minus_startA.y * + aEnd_minus_startB.x; if( fabs(rxs) > glm::epsilon() ) { @@ -213,6 +215,7 @@ bool IntersectSegment( const SFVEC2F &aStartA, const SFVEC2F &aEnd_minus_startA, return false; } + // !TODO: not tested bool RAY::IntersectSphere( const SFVEC3F &aCenter, float aRadius, float &aOutT0, float &aOutT1 ) const { @@ -271,6 +274,7 @@ bool RAY::IntersectSphere( const SFVEC3F &aCenter, float aRadius, float &aOutT0, return true; } + RAYSEG2D::RAYSEG2D( const SFVEC2F& s, const SFVEC2F& e ) { m_Start = s; @@ -290,17 +294,21 @@ RAYSEG2D::RAYSEG2D( const SFVEC2F& s, const SFVEC2F& e ) } -bool RAYSEG2D::IntersectSegment( const SFVEC2F &aStart, const SFVEC2F &aEnd_minus_start, float *aOutT ) const +bool RAYSEG2D::IntersectSegment( const SFVEC2F &aStart, + const SFVEC2F &aEnd_minus_start, + float *aOutT ) const { - float rxs = m_End_minus_start.x * aEnd_minus_start.y - m_End_minus_start.y * aEnd_minus_start.x; + float rxs = m_End_minus_start.x * + aEnd_minus_start.y - m_End_minus_start.y * + aEnd_minus_start.x; - if( fabs(rxs) > glm::epsilon() ) + if( fabs( rxs ) > glm::epsilon() ) { - float inv_rxs = 1.0f / rxs; + const float inv_rxs = 1.0f / rxs; - SFVEC2F pq = aStart - m_Start; + const SFVEC2F pq = aStart - m_Start; - float t = (pq.x * aEnd_minus_start.y - pq.y * aEnd_minus_start.x) * inv_rxs; + const float t = (pq.x * aEnd_minus_start.y - pq.y * aEnd_minus_start.x) * inv_rxs; if( (t < 0.0f) || (t > 1.0f) ) return false; @@ -324,7 +332,7 @@ float RAYSEG2D::DistanceToPointSquared( const SFVEC2F &aPoint ) const { SFVEC2F p = aPoint - m_Start; - float c1 = glm::dot( p, m_End_minus_start ); + const float c1 = glm::dot( p, m_End_minus_start ); if( c1 < FLT_EPSILON ) return glm::dot( p, p ); @@ -333,8 +341,9 @@ float RAYSEG2D::DistanceToPointSquared( const SFVEC2F &aPoint ) const p = aPoint - m_End; else { - float b = c1 / m_DOT_End_minus_start; - SFVEC2F pb = m_Start + m_End_minus_start * b; + const float b = c1 / m_DOT_End_minus_start; + const SFVEC2F pb = m_Start + m_End_minus_start * b; + p = aPoint - pb; } @@ -342,21 +351,26 @@ float RAYSEG2D::DistanceToPointSquared( const SFVEC2F &aPoint ) const } -bool RAYSEG2D::IntersectCircle( const SFVEC2F &aCenter, float aRadius, float *aOutT0, float *aOutT1, SFVEC2F *aOutNormalT0, SFVEC2F *aOutNormalT1 ) const +bool RAYSEG2D::IntersectCircle( const SFVEC2F &aCenter, + float aRadius, + float *aOutT0, + float *aOutT1, + SFVEC2F *aOutNormalT0, + SFVEC2F *aOutNormalT1 ) const { // This code used directly from Steve Marschner's CS667 framework // http://cs665pd.googlecode.com/svn/trunk/photon/sphere.cpp // Compute some factors used in computation - float qx = m_Start.x - aCenter.x; - float qy = m_Start.y - aCenter.y; + const float qx = m_Start.x - aCenter.x; + const float qy = m_Start.y - aCenter.y; - float qd = qx * m_Dir.x + qy * m_Dir.y; - float qq = qx * qx + qy * qy; + const float qd = qx * m_Dir.x + qy * m_Dir.y; + const float qq = qx * qx + qy * qy; // solving the quadratic equation for t at the pts of intersection // dd*t^2 + (2*qd)*t + (qq-r^2) = 0 - float discriminantsqr = (qd * qd - (qq - aRadius * aRadius)); + const float discriminantsqr = (qd * qd - (qq - aRadius * aRadius)); // If the discriminant is less than zero, there is no intersection if( discriminantsqr < FLT_EPSILON ) @@ -365,9 +379,9 @@ bool RAYSEG2D::IntersectCircle( const SFVEC2F &aCenter, float aRadius, float *aO // Otherwise check and make sure that the intersections occur on the ray (t // > 0) and return the closer one - float discriminant = sqrt(discriminantsqr); - float t1 = (-qd - discriminant); - float t2 = (-qd + discriminant); + const float discriminant = sqrt( discriminantsqr ); + const float t1 = (-qd - discriminant); + const float t2 = (-qd + discriminant); if( (( t1 < 0.0f ) || ( t1 > m_Length ) ) && (( t2 < 0.0f ) || ( t2 > m_Length ) ) ) diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/ray.h b/3d-viewer/3d_rendering/3d_render_raytracing/ray.h index 2091ff2dfe..8497df9d28 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/ray.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/ray.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,7 +30,7 @@ #ifndef _RAY_H_ #define _RAY_H_ -#include "plugins/3dapi/xv3d_types.h" +#include enum RAY_CLASSIFICATION @@ -40,7 +40,7 @@ enum RAY_CLASSIFICATION }; -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) RAY +struct RAY { SFVEC3F m_Origin; unsigned int rayID; ///< unique ray ID @@ -57,16 +57,21 @@ GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) RAY void Init( const SFVEC3F& o, const SFVEC3F& d ); - bool IntersectSphere( const SFVEC3F &aCenter, float aRadius, float &aOutT0, float &aOutT1 ) const; + bool IntersectSphere( const SFVEC3F &aCenter, + float aRadius, + float &aOutT0, + float &aOutT1 ) const; SFVEC3F at( float t ) const { return m_Origin + m_Dir * t; } - SFVEC2F at2D( float t ) const { return SFVEC2F( m_Origin.x + m_Dir.x * t, m_Origin.y + m_Dir.y * t ); } + + SFVEC2F at2D( float t ) const { + return SFVEC2F( m_Origin.x + m_Dir.x * t, m_Origin.y + m_Dir.y * t ); } void debug() const; }; -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) RAY2D +struct RAY2D { SFVEC2F m_Origin; SFVEC2F m_Dir; @@ -78,7 +83,7 @@ GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) RAY2D }; -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) RAYSEG2D +struct RAYSEG2D { SFVEC2F m_Start; SFVEC2F m_End; @@ -90,10 +95,19 @@ GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) RAYSEG2D RAYSEG2D( const SFVEC2F& s, const SFVEC2F& e ); - bool IntersectSegment( const SFVEC2F &aStart, const SFVEC2F &aEnd_minus_start, float *aOutT ) const; - bool IntersectCircle( const SFVEC2F &aCenter, float aRadius, float *aOutT0, float *aOutT1, SFVEC2F *aOutNormalT0, SFVEC2F *aOutNormalT1 ) const; + bool IntersectSegment( const SFVEC2F &aStart, + const SFVEC2F &aEnd_minus_start, + float *aOutT ) const; + + bool IntersectCircle( const SFVEC2F &aCenter, + float aRadius, + float *aOutT0, + float *aOutT1, + SFVEC2F *aOutNormalT0, + SFVEC2F *aOutNormalT1 ) const; float DistanceToPointSquared( const SFVEC2F &aPoint ) const; + /** * Function atNormalized - returns the position at t * t - value 0.0 ... 1.0 @@ -109,7 +123,7 @@ bool IntersectSegment( const SFVEC2F &aStartA, const SFVEC2F &aEnd_minus_startA, #if(GLM_ARCH != GLM_ARCH_PURE) /* -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) RAY4 +struct RAY4 { glm::simdVec4 m_orgX; ///< x coordinate of ray origin glm::simdVec4 m_orgy; ///< y coordinate of ray origin diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/raypacket.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/raypacket.cpp new file mode 100644 index 0000000000..cf7f66e07c --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/raypacket.cpp @@ -0,0 +1,131 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file raypacket.cpp + * @brief + */ + +#include "raypacket.h" +#include "../3d_fastmath.h" +#include + + +RAYPACKET::RAYPACKET( const CCAMERA &aCamera, const SFVEC2I &aWindowsPosition ) +{ + unsigned int i = 0; + + for( unsigned int y = 0; y < RAYPACKET_DIM; ++y ) + { + for( unsigned int x = 0; x < RAYPACKET_DIM; ++x ) + { + SFVEC3F rayOrigin; + SFVEC3F rayDir; + + aCamera.MakeRay( SFVEC2I( aWindowsPosition.x + x, + aWindowsPosition.y + y ), + rayOrigin, rayDir ); + + m_ray[i].Init( rayOrigin, rayDir ); + + i++; + } + } + + wxASSERT( i == RAYPACKET_RAYS_PER_PACKET ); + + m_Frustum.GenerateFrustum( + m_ray[ 0 * RAYPACKET_DIM + 0 ], + m_ray[ 0 * RAYPACKET_DIM + (RAYPACKET_DIM - 1) ], + m_ray[ (RAYPACKET_DIM - 1) * RAYPACKET_DIM + 0 ], + m_ray[ (RAYPACKET_DIM - 1) * RAYPACKET_DIM + (RAYPACKET_DIM - 1) ] ); + +} + + +RAYPACKET::RAYPACKET( const CCAMERA &aCamera, + const SFVEC2I &aWindowsPosition, + const SFVEC3F &aDirectionDisplacementFactor ) +{ + unsigned int i = 0; + for( unsigned int y = 0; y < RAYPACKET_DIM; ++y ) + for( unsigned int x = 0; x < RAYPACKET_DIM; ++x ) + { + SFVEC3F rayOrigin; + SFVEC3F rayDir; + + aCamera.MakeRay( SFVEC2I( aWindowsPosition.x + x, + aWindowsPosition.y + y ), + rayOrigin, rayDir ); + + const SFVEC3F randVector = SFVEC3F( Fast_RandFloat() * aDirectionDisplacementFactor.x, + Fast_RandFloat() * aDirectionDisplacementFactor.y, + Fast_RandFloat() * aDirectionDisplacementFactor.z ); + + m_ray[i].Init( rayOrigin, + glm::normalize( rayDir + randVector ) ); + + i++; + } + + wxASSERT( i == RAYPACKET_RAYS_PER_PACKET ); + + m_Frustum.GenerateFrustum( m_ray[ 0 * RAYPACKET_DIM + 0 ], + m_ray[ 0 * RAYPACKET_DIM + (RAYPACKET_DIM - 1) ], + m_ray[ (RAYPACKET_DIM - 1) * RAYPACKET_DIM + 0 ], + m_ray[ (RAYPACKET_DIM - 1) * RAYPACKET_DIM + (RAYPACKET_DIM - 1) ] ); + +} + + +RAYPACKET::RAYPACKET( const CCAMERA &aCamera, + const SFVEC2I &aWindowsPosition, + unsigned int aPixelMultiple ) +{ + unsigned int i = 0; + + for( unsigned int y = 0; y < RAYPACKET_DIM; y++ ) + { + for( unsigned int x = 0; x < RAYPACKET_DIM; x++ ) + { + SFVEC3F rayOrigin; + SFVEC3F rayDir; + + aCamera.MakeRay( SFVEC2I( aWindowsPosition.x + x * aPixelMultiple, + aWindowsPosition.y + y * aPixelMultiple), + rayOrigin, rayDir ); + + m_ray[i].Init( rayOrigin, rayDir ); + + i++; + } + } + + wxASSERT( i == RAYPACKET_RAYS_PER_PACKET ); + + m_Frustum.GenerateFrustum( m_ray[ 0 * RAYPACKET_DIM + 0 ], + m_ray[ 0 * RAYPACKET_DIM + (RAYPACKET_DIM - 1) ], + m_ray[ (RAYPACKET_DIM - 1) * RAYPACKET_DIM + 0 ], + m_ray[ (RAYPACKET_DIM - 1) * RAYPACKET_DIM + (RAYPACKET_DIM - 1) ] ); +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/raypacket.h b/3d-viewer/3d_rendering/3d_render_raytracing/raypacket.h index bc9b2d9e43..410a76adec 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/raypacket.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/raypacket.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,13 +40,21 @@ #define RAYPACKET_RAYS_PER_PACKET (RAYPACKET_DIM * RAYPACKET_DIM) -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) RAYPACKET +struct RAYPACKET { CFRUSTUM m_Frustum; RAY m_ray[RAYPACKET_RAYS_PER_PACKET]; - RAYPACKET( const CCAMERA &aCamera, const SFVEC2I &aWindowsPosition ); - RAYPACKET( const CCAMERA &aCamera, const SFVEC2I &aWindowsPosition, unsigned int aPixelMultiple ); + RAYPACKET( const CCAMERA &aCamera, + const SFVEC2I &aWindowsPosition ); + + RAYPACKET( const CCAMERA &aCamera, + const SFVEC2I &aWindowsPosition, + const SFVEC3F &aDirectionDisplacementFactor ); + + RAYPACKET( const CCAMERA &aCamera, + const SFVEC2I &aWindowsPosition, + unsigned int aPixelMultiple ); }; diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cbbox2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cbbox2d.cpp index 01fa466214..43736b83e6 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cbbox2d.cpp +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cbbox2d.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,7 +27,7 @@ * @brief Bounding Box class implementation */ -#include "3d_math/3d_fastmath.h" +#include "3d_fastmath.h" #include "cbbox2d.h" #include @@ -106,11 +106,13 @@ void CBBOX2D::Union( const SFVEC2F &aPoint ) void CBBOX2D::Union( const CBBOX2D &aBBox ) { - // get the minimun value between the added bounding box and the existent bounding box + // get the minimun value between the added bounding box and + // the existent bounding box m_min.x = fminf( m_min.x, aBBox.m_min.x ); m_min.y = fminf( m_min.y, aBBox.m_min.y ); - // get the maximun value between the added bounding box and the existent bounding box + // get the maximun value between the added bounding box and + // the existent bounding box m_max.x = fmaxf( m_max.x, aBBox.m_max.x ); m_max.y = fmaxf( m_max.y, aBBox.m_max.y ); } @@ -131,7 +133,7 @@ SFVEC2F CBBOX2D::GetExtent() const unsigned int CBBOX2D::MaxDimension() const { unsigned int result = 0; - SFVEC2F extent = GetExtent(); + const SFVEC2F extent = GetExtent(); if( extent.y > extent.x ) result = 1; @@ -141,7 +143,8 @@ unsigned int CBBOX2D::MaxDimension() const float CBBOX2D::Perimeter() const { - SFVEC2F extent = GetExtent(); + const SFVEC2F extent = GetExtent(); + return 2.0f * ( extent.x + extent.y ); } @@ -150,8 +153,8 @@ void CBBOX2D::Scale( float aScale ) { wxASSERT( IsInitialized() ); - SFVEC2F scaleV( aScale, aScale ); - SFVEC2F centerV = GetCenter(); + const SFVEC2F scaleV( aScale, aScale ); + const SFVEC2F centerV = GetCenter(); m_min = (m_min - centerV) * scaleV + centerV; m_max = (m_max - centerV) * scaleV + centerV; @@ -184,18 +187,20 @@ bool CBBOX2D::Intersects( const SFVEC2F &aCenter, float aRadiusSquared ) const { float fDistSq = 0.0f; - for ( unsigned int i = 0; i < 2; i++ ) + for( unsigned int i = 0; i < 2; i++ ) { if( aCenter[i] < m_min[i] ) { - float fDist = aCenter[i] - m_min[i]; + const float fDist = aCenter[i] - m_min[i]; + fDistSq += fDist * fDist; } else { if( aCenter[i] > m_max[i] ) { - float fDist = aCenter[i] - m_max[i]; + const float fDist = aCenter[i] - m_max[i]; + fDistSq += fDist * fDist; } } @@ -210,8 +215,8 @@ bool CBBOX2D::Intersects( const CBBOX2D &aBBox ) const wxASSERT( IsInitialized() ); wxASSERT( aBBox.IsInitialized() ); - bool x = ( m_max.x >= aBBox.m_min.x ) && ( m_min.x <= aBBox.m_max.x ); - bool y = ( m_max.y >= aBBox.m_min.y ) && ( m_min.y <= aBBox.m_max.y ); + const bool x = ( m_max.x >= aBBox.m_min.x ) && ( m_min.x <= aBBox.m_max.x ); + const bool y = ( m_max.y >= aBBox.m_min.y ) && ( m_min.y <= aBBox.m_max.y ); return ( x && y ); } @@ -238,14 +243,14 @@ bool CBBOX2D::Intersect( const RAY2D &aRay, float *t ) const { wxASSERT( t ); - float tx1 = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; - float tx2 = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; + const float tx1 = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; + const float tx2 = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; float tmin = glm::min( tx1, tx2 ); float tmax = glm::max( tx1, tx2 ); - float ty1 = (m_min.y - aRay.m_Origin.y) * aRay.m_InvDir.y; - float ty2 = (m_max.y - aRay.m_Origin.y) * aRay.m_InvDir.y; + const float ty1 = (m_min.y - aRay.m_Origin.y) * aRay.m_InvDir.y; + const float ty2 = (m_max.y - aRay.m_Origin.y) * aRay.m_InvDir.y; tmin = glm::max( tmin, glm::min( ty1, ty2 ) ); tmax = glm::min( tmax, glm::max( ty1, ty2 ) ); @@ -261,21 +266,22 @@ bool CBBOX2D::Intersect( const RAY2D &aRay, float *t ) const bool CBBOX2D::Intersect( const RAYSEG2D &aRaySeg ) const { - float tx1 = (m_min.x - aRaySeg.m_Start.x) * aRaySeg.m_InvDir.x; - float tx2 = (m_max.x - aRaySeg.m_Start.x) * aRaySeg.m_InvDir.x; + const float tx1 = (m_min.x - aRaySeg.m_Start.x) * aRaySeg.m_InvDir.x; + const float tx2 = (m_max.x - aRaySeg.m_Start.x) * aRaySeg.m_InvDir.x; float tmin = glm::min( tx1, tx2 ); float tmax = glm::max( tx1, tx2 ); - float ty1 = (m_min.y - aRaySeg.m_Start.y) * aRaySeg.m_InvDir.y; - float ty2 = (m_max.y - aRaySeg.m_Start.y) * aRaySeg.m_InvDir.y; + const float ty1 = (m_min.y - aRaySeg.m_Start.y) * aRaySeg.m_InvDir.y; + const float ty2 = (m_max.y - aRaySeg.m_Start.y) * aRaySeg.m_InvDir.y; tmin = glm::max( tmin, glm::min( ty1, ty2 ) ); tmax = glm::min( tmax, glm::max( ty1, ty2 ) ); if( (tmax >= 0.0f) && (tmax >= tmin) ) { - float t = (tmin > 0.0f)?tmin:tmax; + const float t = (tmin > 0.0f)?tmin:tmax; + return ( t < aRaySeg.m_Length ); } @@ -288,14 +294,14 @@ bool CBBOX2D::Intersect( const RAY2D &aRay, float *aOutHitT0, float *aOutHitT1 ) wxASSERT( aOutHitT0 ); wxASSERT( aOutHitT1 ); - float tx1 = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; - float tx2 = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; + const float tx1 = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; + const float tx2 = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; float tmin = glm::min( tx1, tx2 ); float tmax = glm::max( tx1, tx2 ); - float ty1 = (m_min.y - aRay.m_Origin.y) * aRay.m_InvDir.y; - float ty2 = (m_max.y - aRay.m_Origin.y) * aRay.m_InvDir.y; + const float ty1 = (m_min.y - aRay.m_Origin.y) * aRay.m_InvDir.y; + const float ty2 = (m_max.y - aRay.m_Origin.y) * aRay.m_InvDir.y; tmin = glm::max( tmin, glm::min( ty1, ty2 ) ); tmax = glm::min( tmax, glm::max( ty1, ty2 ) ); diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cbbox2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cbbox2d.h index aa83e1973c..93656db510 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cbbox2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cbbox2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,15 +30,14 @@ #ifndef _CBBOX2D_H_ #define _CBBOX2D_H_ -#include "plugins/3dapi/xv3d_types.h" -#include "3d_rendering/3d_render_raytracing/ray.h" +#include "../ray.h" /** * Class CBBOX * manages a bounding box defined by two SFVEC2F min max points. */ -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) CBBOX2D +struct CBBOX2D { public: @@ -54,7 +53,7 @@ public: * Initialize a bounding box with a given point * @param aPbInit a point for the bounding box initialization */ - CBBOX2D( const SFVEC2F &aPbInit ); + explicit CBBOX2D( const SFVEC2F &aPbInit ); /** * Constructor CBBOX2D @@ -209,8 +208,8 @@ public: bool Intersect( const RAYSEG2D &aRaySeg ) const; private: - SFVEC2F m_min; ///< point of the lower position of the bounding box - SFVEC2F m_max; ///< point of the higher position of the bounding box + SFVEC2F m_min; ///< point of the lower position of the bounding box + SFVEC2F m_max; ///< point of the higher position of the bounding box }; #endif // CBBox2d_h diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.cpp new file mode 100644 index 0000000000..f5adbb4b80 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.cpp @@ -0,0 +1,177 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cfilledcircle2d.cpp + * @brief + */ + +#include "cfilledcircle2d.h" +#include + + +CFILLEDCIRCLE2D::CFILLEDCIRCLE2D( const SFVEC2F &aCenter, + float aRadius, + const BOARD_ITEM &aBoardItem ) : + COBJECT2D( OBJ2D_FILLED_CIRCLE, aBoardItem ) +{ + wxASSERT( aRadius > 0.0f ); // If that happens, it should be handled before create this circle + + m_center = aCenter; + m_radius = aRadius; + m_radius_squared = aRadius * aRadius; + + m_bbox.Reset(); + m_bbox.Set( m_center - SFVEC2F( aRadius, aRadius ), + m_center + SFVEC2F( aRadius, aRadius ) ); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); + + wxASSERT( m_bbox.IsInitialized() ); +} + + +bool CFILLEDCIRCLE2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // NOT IMPLEMENTED + return false; +} + + +bool CFILLEDCIRCLE2D::Intersects( const CBBOX2D &aBBox ) const +{ + return aBBox.Intersects( m_center, m_radius_squared ); +} + + +bool CFILLEDCIRCLE2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const +{ + wxASSERT( aOutT ); + wxASSERT( aNormalOut ); + + // This code used directly from Steve Marschner's CS667 framework + // http://cs665pd.googlecode.com/svn/trunk/photon/sphere.cpp + + // Compute some factors used in computation + const float qx = aSegRay.m_Start.x - m_center.x; + const float qy = aSegRay.m_Start.y - m_center.y; + + const float qd = qx * aSegRay.m_Dir.x + qy * aSegRay.m_Dir.y; + const float qq = qx * qx + qy * qy; + + // solving the quadratic equation for t at the pts of intersection + // dd*t^2 + (2*qd)*t + (qq-r^2) = 0 + const float discriminantsqr = (qd * qd - (qq - m_radius_squared)); + + // If the discriminant is less than zero, there is no intersection + if( discriminantsqr < FLT_EPSILON ) + return false; + + + // Otherwise check and make sure that the intersections occur on the ray (t + // > 0) and return the closer one + const float discriminant = sqrt(discriminantsqr); + const float t1 = (-qd - discriminant); + const float t2 = (-qd + discriminant); + float t; + + + if( (t1 > 0.0f) && (t1 < aSegRay.m_Length) ) + t = t1; + else + { + if( (t2 > 0.0f) && (t2 < aSegRay.m_Length) ) + t = t2; + else + return false; // Neither intersection was in the ray's half line. + } + + wxASSERT( (t > 0.0f) && (t <= aSegRay.m_Length) ); + + // Convert the intersection to a normalized 0.0 .. 1.0 + *aOutT = t / aSegRay.m_Length; + + const SFVEC2F hitPoint = aSegRay.at( t ); + + *aNormalOut = (hitPoint - m_center) / m_radius; + + return true; +} + + +INTERSECTION_RESULT CFILLEDCIRCLE2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + if( !m_bbox.Intersects( aBBox ) ) + return INTR_MISSES; + + SFVEC2F v[4]; + + v[0] = aBBox.Min() - m_center; + v[1] = aBBox.Max() - m_center; + v[2] = SFVEC2F( aBBox.Min().x, aBBox.Max().y ) - m_center; + v[3] = SFVEC2F( aBBox.Max().x, aBBox.Min().y ) - m_center; + + float s[4]; + + s[0] = v[0].x * v[0].x + v[0].y * v[0].y; + s[1] = v[1].x * v[1].x + v[1].y * v[1].y; + s[2] = v[2].x * v[2].x + v[2].y * v[2].y; + s[3] = v[3].x * v[3].x + v[3].y * v[3].y; + + bool isInside[4]; + + isInside[0] = s[0] <= m_radius_squared; + isInside[1] = s[1] <= m_radius_squared; + isInside[2] = s[2] <= m_radius_squared; + isInside[3] = s[3] <= m_radius_squared; + + // Check if all points are inside the circle + if( isInside[0] && + isInside[1] && + isInside[2] && + isInside[3] ) + return INTR_FULL_INSIDE; + + // Check if any point is inside the circle + if( isInside[0] || + isInside[1] || + isInside[2] || + isInside[3] ) + return INTR_INTERSECTS; + + return INTR_MISSES; +} + + +bool CFILLEDCIRCLE2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + const SFVEC2F v = m_center - aPoint; + + if( (v.x * v.x + v.y * v.y) <= m_radius_squared ) + return true; + + return false; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h index fc1d7ec020..d97d2489bd 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,7 +32,7 @@ #include "cobject2d.h" -class GLM_ALIGN(CLASS_ALIGNMENT) CFILLEDCIRCLE2D : public COBJECT2D +class CFILLEDCIRCLE2D : public COBJECT2D { public: float GetRadius() const { return m_radius; } diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/citemlayercsg2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/citemlayercsg2d.cpp new file mode 100644 index 0000000000..caab7c5c3b --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/citemlayercsg2d.cpp @@ -0,0 +1,217 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file citemlayercsg2d.cpp + * @brief + */ + +#include "citemlayercsg2d.h" +#include "3d_fastmath.h" +#include + +CITEMLAYERCSG2D::CITEMLAYERCSG2D( const COBJECT2D *aObjectA, + std::vector *aObjectB, + const COBJECT2D *aObjectC, + const BOARD_ITEM &aBoardItem ): + COBJECT2D( OBJ2D_CSG, aBoardItem ), + m_objectA(aObjectA), + m_objectB(aObjectB), + m_objectC(aObjectC) +{ + wxASSERT( aObjectA ); + + m_bbox.Reset(); + m_bbox.Set( aObjectA->GetBBox() ); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); + + wxASSERT( m_bbox.IsInitialized() ); +} + + +CITEMLAYERCSG2D::~CITEMLAYERCSG2D() +{ + if( ((void*)m_objectB != CSGITEM_EMPTY) && + ((void*)m_objectB != CSGITEM_FULL) ) + { + delete m_objectB; + m_objectB = NULL; + } +} + + +bool CITEMLAYERCSG2D::Intersects( const CBBOX2D &aBBox ) const +{ + return m_bbox.Intersects( aBBox ); + // !TODO: improove this implementation + //return false; +} + + +bool CITEMLAYERCSG2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // NOT IMPLEMENTED + return false; +} + +// Based on ideas and implementation by Nick Chapman +// http://homepages.paradise.net.nz/nickamy/raytracer/raytracer.htm +bool CITEMLAYERCSG2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const +{ + wxASSERT( aOutT ); + wxASSERT( aNormalOut ); + + if( m_objectA->GetObjectType() == OBJ2D_DUMMYBLOCK ) + return false; + + float currentRayDist; + SFVEC2F currentRayPos; + SFVEC2F currentNormal; + + if( m_objectA->IsPointInside( aSegRay.m_Start ) ) + { + // start ray point off where it is now (at the origin) + currentRayDist = 0.0f; + currentRayPos = aSegRay.m_Start; + } + else + { + //move ray point to start of main object + if( !m_objectA->Intersect( aSegRay, ¤tRayDist, ¤tNormal ) ) + return false; + + currentRayPos = aSegRay.atNormalized( NextFloatDown( currentRayDist ) ); + } + + //wxASSERT( (currentRayDist >= 0.0f) && (currentRayDist <= 1.0f) ); + + + // move through the union of subtracted regions + bool hitSubRegion = false; + + if( m_objectB ) + { + while(1) + { + bool wasInsideSubVol = false; + + //check against all subbed objects + for( unsigned int i = 0; i < m_objectB->size(); ++i ) + { + if( ((const COBJECT2D *)(*m_objectB)[i])->IsPointInside( currentRayPos ) ) + { + hitSubRegion = true; + + // ray point is inside a subtracted region, so move it to the end of the + // subtracted region + float hitDist; + if( !((const COBJECT2D *)(*m_objectB)[i])->Intersect( aSegRay, + &hitDist, + ¤tNormal ) ) + return false; // ray hit main object but did not leave subtracted volume + + wxASSERT( hitDist <= 1.0f ); + + if( hitDist > currentRayDist ) + currentRayDist = hitDist; + + currentRayDist += 0.0001f; + + // ray has left this specific subtracted object volume + currentRayPos = aSegRay.atNormalized( currentRayDist ); + + if( m_objectA->IsPointInside( currentRayPos ) ) + //if(1) + { + wasInsideSubVol = true; + + break; + } + } + } + + if( !wasInsideSubVol ) + break; // ray has succesfully passed through all subtracted regions + + if( currentRayDist >= 1.0f ) + break; + } + } + + //ray is not inside any of the specific subtracted regions + + if( hitSubRegion ) + { + //if( !m_objectA->IsPointInside( currentRayPos ) ) + // return false; // ray got right through the hole in the object! + + currentNormal *= -1.0f; + } + else + { + //ray just hit the main object without hitting any holes + } + + *aNormalOut = currentNormal; + *aOutT = currentRayDist; + + return true; +} + + +INTERSECTION_RESULT CITEMLAYERCSG2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + + // !TODO: + + return INTR_MISSES; +} + + +bool CITEMLAYERCSG2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + // Perform the operation (A - B) /\ C + + if( m_objectA->IsPointInside( aPoint ) ) + { + + if( m_objectB != CSGITEM_EMPTY) + for( unsigned int i = 0; i< m_objectB->size(); i++ ) + { + if( (*m_objectB)[i]->IsPointInside( aPoint ) ) + return false; + } + + // !TODO: not yet implemented + //if( m_objectC && m_objectC != CSGITEM_FULL ) + // return m_objectC->IsPointInside( aPoint ); + + return true; + } + + return false; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/citemlayercsg2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/citemlayercsg2d.h new file mode 100644 index 0000000000..5318332327 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/citemlayercsg2d.h @@ -0,0 +1,102 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file citemlayercsg2d.h + * @brief + */ + +#ifndef _CITEMLAYERCSG2D_H_ +#define _CITEMLAYERCSG2D_H_ + +#include "cobject2d.h" +#include + +/** + * This class is used to make constructive solig geometry for items + * objects on layers. + * The operation is in the form (A - B) /\ C + * For almost all of the layers it translate something like: + * A (a via, a track, pad, polygon), B (a via hole, a THT hole, .. ), + * C the board (epoxy) + * For layers like Solder mask that are negative: + * A ( epoxy ), B( pads, polygons, ..), C=1 + * + * Some notes: + * BODY = PCB_outlines - PCB_holes - (Modules_THT_holes + VIA_THT_holes) + * + * Plated_vias_and_holes = Tracks.Vias + Modules.PlatedHoles + * + * Layer.cu = ( Tracks.cu + Modules_Pads.cu + Modules_Graphics.cu + + * Layer_zones.cu + PCB_drawings.cu - Layer_VIA_holes ) & BODY + * + * Layer.Mask = BODY - + * (PCB_drawing.Mask + Modules_Graphics.Mask + + * Modules_Pads.Mask + Layer_zones.Mask ) + * Layer.Paste = (PCB_drawing.Paste + Modules_Graphics.Paste + + * Modules_Pads.Paste + Layer_zones.Paste) & BODY + * Layer.Silk = (PCB_drawing.Silk + Modules_Graphics.Silk + + * Modules_Pads.Silk + Layer_zones.Paste) & BODY + * + * BODY = A - B /\ 1 + * Layer.cu = A - B /\ C + * Layer.mask = A - B /\ 1 + * Layers.Paste = A - 0 /\ C + * Layers.Silk = A - 0 /\ C + * + * BODY = P - T /\ 1 + * Layer.cu = T - H /\ BODY + * Layer.mask = BODY - M /\ 1 + * Layers.Paste = P - 0 /\ BODY + * Layers.Silk = S - 0 /\ BODY + */ + +#define CSGITEM_EMPTY 0 +#define CSGITEM_FULL (COBJECT2D *)((size_t)(-1)) + +class CITEMLAYERCSG2D : public COBJECT2D +{ +private: + const COBJECT2D *m_objectA; + std::vector *m_objectB; + const COBJECT2D *m_objectC; + +public: + CITEMLAYERCSG2D( const COBJECT2D *aObjectA, + std::vector *aObjectB, + const COBJECT2D *aObjectC, + const BOARD_ITEM &aBoardItem ); + + ~CITEMLAYERCSG2D(); + + // Imported from COBJECT2D + bool Overlaps( const CBBOX2D &aBBox ) const; + bool Intersects( const CBBOX2D &aBBox ) const; + bool Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut ) const; + INTERSECTION_RESULT IsBBoxInside( const CBBOX2D &aBBox ) const; + bool IsPointInside( const SFVEC2F &aPoint ) const; +}; + + +#endif // _CITEMLAYERCSG2D_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cobject2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cobject2d.cpp index b6418c8e1d..e8c9c759d8 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cobject2d.cpp +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cobject2d.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cobject2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cobject2d.h index 8458870325..0bcc769acc 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cobject2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cobject2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,7 +30,6 @@ #ifndef _COBJECT2D_H_ #define _COBJECT2D_H_ -#include "plugins/3dapi/xv3d_types.h" #include "cbbox2d.h" #include @@ -60,7 +59,7 @@ enum OBJECT2D_TYPE }; -class GLM_ALIGN(CLASS_ALIGNMENT) COBJECT2D +class COBJECT2D { protected: CBBOX2D m_bbox; @@ -78,12 +77,13 @@ public: /** Function Overlaps * @brief Test if the box overlaps the object * Conformance - * The function overlaps implements function Overlaps from the OGC Simple Feature Specification. + * The function overlaps implements function Overlaps from the OGC + * Simple Feature Specification. * http://www.opengeospatial.org/standards/sfa * a.Overlaps(b) ⇔ ( dim(I(a)) = dim(I(b)) = dim(I(a) ∩ I(b))) ∧ (a ∩ b ≠ a) ∧ (a ∩ b ≠ b) - * It means that the result dimension of an overlap is the same dimentions of the bounding box - * (so the overlap cannot be a point or a line) and one of the boxes cannot full contain the other - * box. + * It means that the result dimension of an overlap is the same dimentions + * of the bounding box (so the overlap cannot be a point or a line) and one + * of the boxes cannot full contain the other box. * @param aBBox - The bounding box to test * @return true if the BBox intersects the object or is inside it */ @@ -100,11 +100,14 @@ public: /** Function Intersect * @brief Intersect * @param aSegRay - * @param aOutT a value between 0.0 and 1.0 in relation to the time of the hit of the segment + * @param aOutT a value between 0.0 and 1.0 in relation to the time of the + * hit of the segment * @param aNormalOut * @return */ - virtual bool Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut ) const = 0; + virtual bool Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const = 0; /** Function IsBBoxInside * @brief Tests if the bouding is out, intersects or is complety inside @@ -119,7 +122,7 @@ public: const SFVEC2F &GetCentroid() const { return m_centroid; } - const OBJECT2D_TYPE GetObjectType() const { return m_obj_type; } + OBJECT2D_TYPE GetObjectType() const { return m_obj_type; } }; @@ -129,9 +132,12 @@ public: class COBJECT2D_STATS { public: - void ResetStats() { memset( m_counter, 0, sizeof(unsigned int) * OBJ2D_MAX ); } + void ResetStats() { memset( m_counter, 0, sizeof( unsigned int ) * OBJ2D_MAX ); } - unsigned int GetCountOf( OBJECT2D_TYPE aObjType ) const { return m_counter[aObjType]; } + unsigned int GetCountOf( OBJECT2D_TYPE aObjType ) const + { + return m_counter[aObjType]; + } void AddOne( OBJECT2D_TYPE aObjType ) { m_counter[aObjType]++; } diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.cpp new file mode 100644 index 0000000000..e5cc037999 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.cpp @@ -0,0 +1,831 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cpolygon2d.cpp + * @brief + */ + +#include "cpolygon2d.h" +#include +#include + +#ifdef PRINT_STATISTICS_3D_VIEWER +#include +#endif + +// CPOLYGONBLOCK2D +// ///////////////////////////////////////////////////////////////////////////// + +static bool polygon_IsPointInside( const SEGMENTS &aSegments, const SFVEC2F &aPoint ) +{ + wxASSERT( aSegments.size() >= 3 ); + + unsigned int i; + unsigned int j = aSegments.size() - 1; + bool oddNodes = false; + + for( i = 0; i < aSegments.size(); j = i++ ) + { + const float polyJY = aSegments[j].m_Start.y; + const float polyIY = aSegments[i].m_Start.y; + + if( ((polyIY <= aPoint.y) && (polyJY >= aPoint.y)) || + ((polyJY <= aPoint.y) && (polyIY >= aPoint.y)) + ) + { + const float polyJX = aSegments[j].m_Start.x; + const float polyIX = aSegments[i].m_Start.x; + + if( (polyIX <= aPoint.x) || (polyJX <= aPoint.x) ) + oddNodes ^= ( ( polyIX + + ( ( aPoint.y - polyIY ) * + aSegments[i].m_inv_JY_minus_IY ) * + aSegments[i].m_JX_minus_IX ) < aPoint.x ); + } + } + + return oddNodes; +} + + +CPOLYGONBLOCK2D::CPOLYGONBLOCK2D( const SEGMENTS_WIDTH_NORMALS &aOpenSegmentList, + const OUTERS_AND_HOLES &aOuter_and_holes, + const BOARD_ITEM &aBoardItem ) : + COBJECT2D( OBJ2D_POLYGON, aBoardItem ) +{ + m_open_segments.resize( aOpenSegmentList.size() ); + + // Copy vectors and structures + for( unsigned int i = 0; i < aOpenSegmentList.size(); i++ ) + m_open_segments[i] = aOpenSegmentList[i]; + + m_outers_and_holes = aOuter_and_holes; + + // Compute bounding box with the points of the polygon + m_bbox.Reset(); + + for( unsigned int i = 0; i < m_outers_and_holes.m_Outers.size(); i++ ) + { + for( unsigned int j = 0; j < m_outers_and_holes.m_Outers[i].size(); j++ ) + m_bbox.Union( ((SEGMENTS)m_outers_and_holes.m_Outers[i])[j].m_Start ); + } + + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); + + // Some checks + wxASSERT( m_open_segments.size() == aOpenSegmentList.size() ); + wxASSERT( m_open_segments.size() > 0 ); + + wxASSERT( m_outers_and_holes.m_Outers.size() > 0 ); + wxASSERT( m_outers_and_holes.m_Outers.size() == aOuter_and_holes.m_Outers.size() ); + wxASSERT( m_outers_and_holes.m_Holes.size() == aOuter_and_holes.m_Holes.size() ); + + wxASSERT( m_outers_and_holes.m_Outers[0].size() >= 3 ); + wxASSERT( m_outers_and_holes.m_Outers[0].size() == + aOuter_and_holes.m_Outers[0].size() ); + + wxASSERT( m_bbox.IsInitialized() ); +} + + +bool CPOLYGONBLOCK2D::Intersects( const CBBOX2D &aBBox ) const +{ + return m_bbox.Intersects( aBBox ); + + // !TODO: this is a quick not perfect implementation + // in order to make it perfect the box must be checked against all the + // polygons in the outers and not inside the holes +} + + +bool CPOLYGONBLOCK2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // NOT IMPLEMENTED + return false; +} + + +bool CPOLYGONBLOCK2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const +{ + int hitIndex = -1; + float hitU = 0.0f; + float tMin = 0.0f; + + for( unsigned int i = 0; i < m_open_segments.size(); i++ ) + { + const SFVEC2F &s = m_open_segments[i].m_Precalc_slope; + const SFVEC2F &q = m_open_segments[i].m_Start; + + float rxs = aSegRay.m_End_minus_start.x * s.y - + aSegRay.m_End_minus_start.y * s.x; + + if( fabs(rxs) > FLT_EPSILON ) + { + const float inv_rxs = 1.0f / rxs; + + const SFVEC2F pq = q - aSegRay.m_Start; + + const float t = (pq.x * s.y - pq.y * s.x) * inv_rxs; + + if( (t < 0.0f) || (t > 1.0f) ) + continue; + + const float u = ( pq.x * aSegRay.m_End_minus_start.y - + pq.y * aSegRay.m_End_minus_start.x ) * inv_rxs; + + if( (u < 0.0f) || (u > 1.0f) ) + continue; + + if( ( hitIndex == -1 ) || ( t <= tMin ) ) + { + tMin = t; + hitIndex = i; + hitU = u; + } + } + } + + if( hitIndex >= 0 ) + { + wxASSERT( (tMin >= 0.0f) && (tMin <= 1.0f) ); + + *aOutT = tMin; + *aNormalOut = glm::normalize( + m_open_segments[hitIndex].m_Normals.m_Start * hitU + + m_open_segments[hitIndex].m_Normals.m_End * + (1.0f - hitU) ); + + return true; + } + + return false; +} + + +INTERSECTION_RESULT CPOLYGONBLOCK2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + + return INTR_MISSES; +} + + +bool CPOLYGONBLOCK2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + // NOTE: we could add here a test for the bounding box, but because in the + // 3d object it already checked for a 3d bbox. + + // First test if point is inside a hole. + // If true it can early exit + for( unsigned int i = 0; i < m_outers_and_holes.m_Holes.size(); i++ ) + if( !m_outers_and_holes.m_Holes[i].empty() ) + if( polygon_IsPointInside( m_outers_and_holes.m_Holes[i], aPoint ) ) + return false; + + // At this moment, the point is not inside a hole, so check if it is + // inside the polygon + for( unsigned int i = 0; i < m_outers_and_holes.m_Outers.size(); i++ ) + if( !m_outers_and_holes.m_Outers.empty() ) + if( polygon_IsPointInside( m_outers_and_holes.m_Outers[i], aPoint ) ) + return true; + + // Miss the polygon + return false; +} + + +// CDUMMYBLOCK2D +// ///////////////////////////////////////////////////////////////////////////// + +CDUMMYBLOCK2D::CDUMMYBLOCK2D( const SFVEC2F &aPbMin, const SFVEC2F &aPbMax, + const BOARD_ITEM &aBoardItem ) : + COBJECT2D( OBJ2D_DUMMYBLOCK, aBoardItem ) +{ + m_bbox.Set( aPbMin, aPbMax ); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); +} + + +CDUMMYBLOCK2D::CDUMMYBLOCK2D( const CBBOX2D &aBBox, + const BOARD_ITEM &aBoardItem ) : + COBJECT2D( OBJ2D_DUMMYBLOCK, aBoardItem ) +{ + m_bbox.Set( aBBox ); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); +} + + +bool CDUMMYBLOCK2D::Intersects( const CBBOX2D &aBBox ) const +{ + return m_bbox.Intersects( aBBox ); +} + + +bool CDUMMYBLOCK2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // Not implemented + return false; +} + + +bool CDUMMYBLOCK2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const +{ + // The dummy block will be never intersected because it have no edges, + // only it have a plan surface of the size of the bounding box + return false; +} + + +INTERSECTION_RESULT CDUMMYBLOCK2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + //!TODO: + return INTR_MISSES; +} + + +bool CDUMMYBLOCK2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + // The dummy is filled in all his bounding box, so if it hit the bbox + // it will hit this dummy + if( m_bbox.Inside( aPoint ) ) + return true; + + return false; +} + + +// Polygon process and conversion +// //////////////////////////////////////////////////////////////////////////// + +typedef std::vector KF_POINTS; + +#define MAX_NR_DIVISIONS 96 + + +static bool intersect( const SEGMENT_WITH_NORMALS &aSeg, + const SFVEC2F &aStart, + const SFVEC2F &aEnd ) +{ + const SFVEC2F r = aEnd - aStart; + const SFVEC2F s = aSeg.m_Precalc_slope; + const SFVEC2F q = aSeg.m_Start; + + const float rxs = r.x * s.y - r.y * s.x; + + if( fabs(rxs) > glm::epsilon() ) + { + const float inv_rxs = 1.0f / rxs; + + const SFVEC2F pq = q - aStart; + + const float t = (pq.x * s.y - pq.y * s.x) * inv_rxs; + + if( (t < 0.0f) || (t > 1.0f) ) + return false; + + const float u = (pq.x * r.y - pq.y * r.x) * inv_rxs; + + if( (u < 0.0f) || (u > 1.0f) ) + return false; + + return true; + } + + return false; +} + + +static void extractPathsFrom( const SEGMENTS_WIDTH_NORMALS &aSegList, + const CBBOX2D &aBBox, + SEGMENTS_WIDTH_NORMALS &aOutSegThatIntersect ) +{ + wxASSERT( aSegList.size () >= 3 ); + + unsigned int i; + unsigned int j = aSegList.size() - 1; + + const SFVEC2F p1( aBBox.Min().x, aBBox.Min().y ); + const SFVEC2F p2( aBBox.Max().x, aBBox.Min().y ); + const SFVEC2F p3( aBBox.Max().x, aBBox.Max().y ); + const SFVEC2F p4( aBBox.Min().x, aBBox.Max().y ); + + aOutSegThatIntersect.clear(); + + for( i = 0; i < aSegList.size(); j = i++ ) + { + if( aBBox.Inside( aSegList[i].m_Start ) || + aBBox.Inside( aSegList[j].m_Start ) ) + { + // if the segment points are inside the bounding box then this + // segment is touching the bbox. + aOutSegThatIntersect.push_back( aSegList[i] ); + } + else + { + // Check if a segment intersects the bounding box + + // Make a bounding box based on the segments start and end + CBBOX2D segmentBBox( aSegList[i].m_Start, + aSegList[j].m_Start ); + + if( aBBox.Intersects( segmentBBox ) ) + { + + const SEGMENT_WITH_NORMALS &seg = aSegList[i]; + + if( intersect( seg, p1, p2 ) || + intersect( seg, p2, p3 ) || + intersect( seg, p3, p4 ) || + intersect( seg, p4, p1 ) ) + { + aOutSegThatIntersect.push_back( seg ); + } + } + } + } +} + + +static void polygon_Convert( const SHAPE_LINE_CHAIN &aPath, + SEGMENTS &aOutSegment, + float aBiuTo3DunitsScale ) +{ + aOutSegment.resize( aPath.PointCount() ); + + for( int j = 0; j < aPath.PointCount(); j++ ) + { + const VECTOR2I &a = aPath.CPoint( j ); + + aOutSegment[j].m_Start = SFVEC2F( (float) a.x * aBiuTo3DunitsScale, + (float)-a.y * aBiuTo3DunitsScale ); + } + + unsigned int i; + unsigned int j = aOutSegment.size () - 1; + + for( i = 0; i < aOutSegment.size (); j = i++ ) + { + // Calculate constants for each segment + aOutSegment[i].m_inv_JY_minus_IY = 1.0f / ( aOutSegment[j].m_Start.y - + aOutSegment[i].m_Start.y ); + + aOutSegment[i].m_JX_minus_IX = (aOutSegment[j].m_Start.x - + aOutSegment[i].m_Start.x); + } +} + + +void Convert_path_polygon_to_polygon_blocks_and_dummy_blocks( + const SHAPE_POLY_SET &aMainPath, + CGENERICCONTAINER2D &aDstContainer, + float aBiuTo3DunitsScale, + float aDivFactor, + const BOARD_ITEM &aBoardItem ) +{ + BOX2I pathBounds = aMainPath.BBox(); + + // Get the path + + wxASSERT( aMainPath.OutlineCount() == 1 ); + const SHAPE_POLY_SET::POLYGON& curr_polywithholes = aMainPath.CPolygon( 0 ); + + wxASSERT( curr_polywithholes.size() == 1 ); + const SHAPE_LINE_CHAIN& path = curr_polywithholes[0]; // a simple polygon + + // Convert the points to segments class + CBBOX2D bbox; + bbox.Reset(); + + // Contains the main list of segments and each segment normal interpolated + SEGMENTS_WIDTH_NORMALS segments_and_normals; + + // Contains a closed polygon used to calc if points are inside + SEGMENTS segments; + + segments_and_normals.resize( path.PointCount() ); + segments.resize( path.PointCount() ); + + for( int i = 0; i < path.PointCount(); i++ ) + { + const VECTOR2I& a = path.CPoint( i ); + + const SFVEC2F point ( (float)( a.x) * aBiuTo3DunitsScale, + (float)(-a.y) * aBiuTo3DunitsScale ); + + bbox.Union( point ); + segments_and_normals[i].m_Start = point; + segments[i].m_Start = point; + } + + bbox.ScaleNextUp(); + + + // Calc the slopes, normals and some statistics about this polygon + unsigned int i; + unsigned int j = segments_and_normals.size() - 1; + + // Temporary normal to the segment, it will later be used for interpolation + std::vector< SFVEC2F > tmpSegmentNormals; + tmpSegmentNormals.resize( segments_and_normals.size() ); + + float medOfTheSquaresSegmentLength = 0.0f; +#ifdef PRINT_STATISTICS_3D_VIEWER + float minLength = FLT_MAX; +#endif + + for( i = 0; i < segments_and_normals.size(); j = i++ ) + { + const SFVEC2F slope = segments_and_normals[j].m_Start - + segments_and_normals[i].m_Start; + + segments_and_normals[i].m_Precalc_slope = slope; + + // Calculate constants for each segment + segments[i].m_inv_JY_minus_IY = 1.0f / ( segments_and_normals[j].m_Start.y - + segments_and_normals[i].m_Start.y ); + + segments[i].m_JX_minus_IX = ( segments_and_normals[j].m_Start.x - + segments_and_normals[i].m_Start.x ); + + // The normal orientation expect a fixed polygon orientation (!TODO: which one?) + //tmpSegmentNormals[i] = glm::normalize( SFVEC2F( -slope.y, +slope.x ) ); + tmpSegmentNormals[i] = glm::normalize( SFVEC2F( slope.y, -slope.x ) ); + + const float length = slope.x * slope.x + slope.y * slope.y; + +#ifdef PRINT_STATISTICS_3D_VIEWER + if( length < minLength ) + minLength = length; +#endif + + medOfTheSquaresSegmentLength += length; + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + float minSegmentLength = sqrt( minLength ); +#endif + + // This calc an approximation of medium lengths, that will be used to calc + // the size of the division. + medOfTheSquaresSegmentLength /= segments_and_normals.size(); + medOfTheSquaresSegmentLength = sqrt( medOfTheSquaresSegmentLength ); + + + // Compute the normal interpolation + // If calculate the dot between the segments, if they are above/below some + // threshould it will not interpolated it (ex: if you are in a edge corner + // or in a smooth transaction) + j = segments_and_normals.size() - 1; + for( i = 0; i < segments_and_normals.size(); j = i++ ) + { + const SFVEC2F normalBeforeSeg = tmpSegmentNormals[j]; + const SFVEC2F normalSeg = tmpSegmentNormals[i]; + const SFVEC2F normalAfterSeg = tmpSegmentNormals[ (i + 1) % + segments_and_normals.size() ]; + + const float dotBefore = glm::dot( normalBeforeSeg, normalSeg ); + const float dotAfter = glm::dot( normalAfterSeg, normalSeg ); + + if( dotBefore < 0.7f ) + segments_and_normals[i].m_Normals.m_Start = normalSeg; + else + segments_and_normals[i].m_Normals.m_Start = + glm::normalize( (((normalBeforeSeg * dotBefore ) + normalSeg) * 0.5f) ); + + if( dotAfter < 0.7f ) + segments_and_normals[i].m_Normals.m_End = normalSeg; + else + segments_and_normals[i].m_Normals.m_End = + glm::normalize( (((normalAfterSeg * dotAfter ) + normalSeg) * 0.5f) ); + } + + if( aDivFactor == 0.0f ) + aDivFactor = medOfTheSquaresSegmentLength; + + SFVEC2UI grid_divisions; + grid_divisions.x = (unsigned int)( (bbox.GetExtent().x / aDivFactor) ); + grid_divisions.y = (unsigned int)( (bbox.GetExtent().y / aDivFactor) ); + + grid_divisions = glm::clamp( grid_divisions , + SFVEC2UI( 1, 1 ), + SFVEC2UI( MAX_NR_DIVISIONS, MAX_NR_DIVISIONS ) ); + + // Calculate the steps advance of the grid + SFVEC2F blockAdvance; + + blockAdvance.x = bbox.GetExtent().x / (float)grid_divisions.x; + blockAdvance.y = bbox.GetExtent().y / (float)grid_divisions.y; + + wxASSERT( blockAdvance.x > 0.0f ); + wxASSERT( blockAdvance.y > 0.0f ); + + const int leftToRight_inc = (pathBounds.GetRight() - pathBounds.GetLeft()) / + grid_divisions.x; + + const int topToBottom_inc = (pathBounds.GetBottom() - pathBounds.GetTop()) / + grid_divisions.y; + + // Statistics + unsigned int stats_n_empty_blocks = 0; + unsigned int stats_n_dummy_blocks = 0; + unsigned int stats_n_poly_blocks = 0; + unsigned int stats_sum_size_of_polygons = 0; + + + // Step by each block of a grid trying to extract segments and create + // polygon blocks + + int topToBottom = pathBounds.GetTop(); + float blockY = bbox.Max().y; + + for( unsigned int iy = 0; iy < grid_divisions.y; iy++ ) + { + + int leftToRight = pathBounds.GetLeft(); + float blockX = bbox.Min().x; + + for( unsigned int ix = 0; ix < grid_divisions.x; ix++ ) + { + CBBOX2D blockBox( SFVEC2F( blockX, + blockY - blockAdvance.y ), + SFVEC2F( blockX + blockAdvance.x, + blockY ) ); + + // Make the box large to it will catch (intersect) the edges + blockBox.ScaleNextUp(); + blockBox.ScaleNextUp(); + blockBox.ScaleNextUp(); + + SEGMENTS_WIDTH_NORMALS extractedSegments; + + extractPathsFrom( segments_and_normals, blockBox, extractedSegments ); + + + if( extractedSegments.empty() ) + { + + SFVEC2F p1( blockBox.Min().x, blockBox.Min().y ); + SFVEC2F p2( blockBox.Max().x, blockBox.Min().y ); + SFVEC2F p3( blockBox.Max().x, blockBox.Max().y ); + SFVEC2F p4( blockBox.Min().x, blockBox.Max().y ); + + if( polygon_IsPointInside( segments, p1 ) || + polygon_IsPointInside( segments, p2 ) || + polygon_IsPointInside( segments, p3 ) || + polygon_IsPointInside( segments, p4 ) ) + { + // In this case, the segments are not intersecting the + // polygon, so it means that if any point is inside it, + // then all other are inside the polygon. + // This is a full bbox inside, so add a dummy box + + aDstContainer.Add( new CDUMMYBLOCK2D( blockBox, aBoardItem ) ); + stats_n_dummy_blocks++; + } + else + { + // Points are outside, so this block complety missed the polygon + // In this case, no objects need to be added + stats_n_empty_blocks++; + } + } + else + { + // At this point, the borders of polygon were intersected by the + // bounding box, so we must calculate a new polygon that will + // close that small block. + // This block will be used to calculate if points are inside + // the (sub block) polygon. + + SHAPE_POLY_SET subBlockPoly; + + SHAPE_LINE_CHAIN sb = SHAPE_LINE_CHAIN( + VECTOR2I( leftToRight, + topToBottom ), + VECTOR2I( leftToRight + leftToRight_inc, + topToBottom ), + VECTOR2I( leftToRight + leftToRight_inc, + topToBottom + topToBottom_inc ), + VECTOR2I( leftToRight, + topToBottom + topToBottom_inc ) ); + + //sb.Append( leftToRight, topToBottom ); + sb.SetClosed( true ); + + subBlockPoly.AddOutline( sb ); + + // We need here a strictly simple polygon with outlines and holes + SHAPE_POLY_SET solution; + solution.BooleanIntersection( aMainPath, + subBlockPoly, + SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + OUTERS_AND_HOLES outersAndHoles; + + outersAndHoles.m_Holes.clear(); + outersAndHoles.m_Outers.clear(); + + for( int idx = 0; idx < solution.OutlineCount(); idx++ ) + { + const SHAPE_LINE_CHAIN & outline = solution.Outline( idx ); + + SEGMENTS solutionSegment; + + polygon_Convert( outline, solutionSegment, aBiuTo3DunitsScale ); + outersAndHoles.m_Outers.push_back( solutionSegment ); + + stats_sum_size_of_polygons += solutionSegment.size(); + + for( int holeIdx = 0; + holeIdx < solution.HoleCount( idx ); + holeIdx++ ) + { + const SHAPE_LINE_CHAIN & hole = solution.Hole( idx, holeIdx ); + + polygon_Convert( hole, solutionSegment, aBiuTo3DunitsScale ); + outersAndHoles.m_Holes.push_back( solutionSegment ); + stats_sum_size_of_polygons += solutionSegment.size(); + } + + } + + if( !outersAndHoles.m_Outers.empty() ) + { + aDstContainer.Add( new CPOLYGONBLOCK2D( extractedSegments, + outersAndHoles, + aBoardItem ) ); + stats_n_poly_blocks++; + } + } + + blockX += blockAdvance.x; + leftToRight += leftToRight_inc; + } + + blockY -= blockAdvance.y; + topToBottom += topToBottom_inc; + } + +#ifdef PRINT_STATISTICS_3D_VIEWER + printf( "////////////////////////////////////////////////////////////////////////////////\n" ); + printf( "Convert_path_polygon_to_polygon_blocks_and_dummy_blocks\n" ); + printf( " grid_divisions (%u, %u)\n", grid_divisions.x, grid_divisions.y ); + printf( " N Total Blocks %u\n", grid_divisions.x * grid_divisions.y ); + printf( " N Empty Blocks %u\n", stats_n_empty_blocks ); + printf( " N Dummy Blocks %u\n", stats_n_dummy_blocks ); + printf( " N Polyg Blocks %u\n", stats_n_poly_blocks ); + printf( " Med N Seg Poly %u\n", stats_sum_size_of_polygons / stats_n_poly_blocks ); + printf( " medOfTheSquaresSegmentLength %f\n", medOfTheSquaresSegmentLength ); + printf( " minSegmentLength %f\n", minSegmentLength ); + printf( " aDivFactor %f\n", aDivFactor ); + printf( "////////////////////////////////////////////////////////////////////////////////\n" ); +#endif +} + + +void Polygon_Calc_BBox_3DU( const SHAPE_POLY_SET &aPolysList, + CBBOX2D &aOutBBox , + float aBiuTo3DunitsScale ) +{ + aOutBBox.Reset(); + + for( int idx = 0; idx < aPolysList.OutlineCount(); ++idx ) + { + // Each polygon in aPolysList is a polygon with holes + const SHAPE_POLY_SET::POLYGON& curr_polywithholes = aPolysList.CPolygon( idx ); + + for( unsigned ipoly = 0; ipoly < curr_polywithholes.size(); ++ipoly ) + { + const SHAPE_LINE_CHAIN& path = curr_polywithholes[ipoly]; // a simple polygon + + for( int jj = 0; jj < path.PointCount(); jj++ ) + { + const VECTOR2I& a = path.CPoint( jj ); + + aOutBBox.Union( SFVEC2F( (float) a.x * aBiuTo3DunitsScale, + (float)-a.y * aBiuTo3DunitsScale ) ); + } + } + } + + aOutBBox.ScaleNextUp(); +} + + +void Polygon_Convert( const KI_POLYGON &aPolygon, + ClipperLib::Path &aOutPath, + CBBOX2D &aOutBBox, + float aBiuTo3DunitsScale ) +{ + aOutPath.resize( aPolygon.size() ); + aOutBBox.Reset(); + + for( unsigned i = 0; i < aPolygon.size(); i++ ) + { + const KI_POLY_POINT point = *(aPolygon.begin() + i); + + aOutPath[i] = ClipperLib::IntPoint( (ClipperLib::cInt)point.x(), + (ClipperLib::cInt)point.y() ); + + aOutBBox.Union( SFVEC2F( (float) point.x() * aBiuTo3DunitsScale, + (float)-point.y() * aBiuTo3DunitsScale ) ); + } + + aOutBBox.ScaleNextUp(); + + ClipperLib::CleanPolygon( aOutPath ); +} + + +#ifdef DEBUG +static void polygon_Convert( const ClipperLib::Path &aPath, + SEGMENTS &aOutSegment, + float aBiuTo3DunitsScale ) +{ + aOutSegment.resize( aPath.size() ); + + for( unsigned i = 0; i < aPath.size(); i++ ) + { + aOutSegment[i].m_Start = SFVEC2F( (float) aPath[i].X * aBiuTo3DunitsScale, + (float)-aPath[i].Y * aBiuTo3DunitsScale ); + } + + unsigned int i; + unsigned int j = aOutSegment.size () - 1; + + for( i = 0; i < aOutSegment.size (); j = i++ ) + { + // Calculate constants for each segment + aOutSegment[i].m_inv_JY_minus_IY = 1.0f / ( aOutSegment[j].m_Start.y - + aOutSegment[i].m_Start.y ); + aOutSegment[i].m_JX_minus_IX = (aOutSegment[j].m_Start.x - aOutSegment[i].m_Start.x); + } +} + +void Polygon2d_TestModule() +{ + // "This structure contains a sequence of IntPoint vertices defining a + // single contour" + ClipperLib::Path aPath; + + SEGMENTS aSegments; + + aPath.resize( 4 ); + + aPath[0] = ClipperLib::IntPoint( -2, -2 ); + aPath[1] = ClipperLib::IntPoint( 2, -2 ); + aPath[2] = ClipperLib::IntPoint( 2, 2 ); + aPath[3] = ClipperLib::IntPoint( -2, 2 ); + + // It must be an outter polygon + wxASSERT( ClipperLib::Orientation( aPath ) ); + + polygon_Convert( aPath, aSegments, 1.0f ); + + wxASSERT( aPath.size() == aSegments.size() ); + + wxASSERT( aSegments[0].m_Start == SFVEC2F( -2.0f, 2.0f ) ); + wxASSERT( aSegments[1].m_Start == SFVEC2F( 2.0f, 2.0f ) ); + wxASSERT( aSegments[2].m_Start == SFVEC2F( 2.0f, -2.0f ) ); + wxASSERT( aSegments[3].m_Start == SFVEC2F( -2.0f, -2.0f ) ); + + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 0.0f, 0.0f ) ) ); + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -1.9f, -1.9f ) ) ); + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -1.9f, 1.9f ) ) ); + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 1.9f, 1.9f ) ) ); + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 1.9f, -1.9f ) ) ); + + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -2.1f, -2.0f ) ) == false ); + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -2.1f, 2.0f ) ) == false ); + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 2.1f, 2.0f ) ) == false ); + wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 2.1f, -2.0f ) ) == false ); +} +#endif diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h index e4a7d8cd3e..88ee0283df 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -84,7 +84,7 @@ typedef struct /// polygons. This polygon class represent a sub part of that main polygon. /// There is information for the contours (used to test the ray2d intersection) /// and a close definition of the block polygon to test if a point is inside. -class GLM_ALIGN(CLASS_ALIGNMENT) CPOLYGONBLOCK2D : public COBJECT2D +class CPOLYGONBLOCK2D : public COBJECT2D { private: /// This is the outter part of the polygon. This list is used to test a ray @@ -115,11 +115,14 @@ public: /// a ray will return allways false. /// This is used as a sub block extrated from polygon (pcb polygon areas) and /// represents an area that is full filled. -class GLM_ALIGN(CLASS_ALIGNMENT) CDUMMYBLOCK2D : public COBJECT2D +class CDUMMYBLOCK2D : public COBJECT2D { public: - CDUMMYBLOCK2D(const SFVEC2F &aPbMin, const SFVEC2F &aPbMax , const BOARD_ITEM &aBoardItem ); + CDUMMYBLOCK2D( const SFVEC2F &aPbMin, + const SFVEC2F &aPbMax, + const BOARD_ITEM &aBoardItem ); + CDUMMYBLOCK2D( const CBBOX2D &aBBox, const BOARD_ITEM &aBoardItem ); // Imported from COBJECT2D @@ -138,16 +141,24 @@ public: * @param aMainPath - the polygon are that was converted from the pcb board * @param aDstContainer - the destination container to put the created sub blocks * @param aBiuTo3DunitsScale - the rendering target 3d scale - * @param aDivFactor - a division factor (in 3Dunits) to divide the polygon plane, 0.0f will use the internal polygon segm statistics + * @param aDivFactor - a division factor (in 3Dunits) to divide the polygon plane, + * 0.0f will use the internal polygon segm statistics */ -void Convert_path_polygon_to_polygon_blocks_and_dummy_blocks(const SHAPE_POLY_SET &aMainPath, +void Convert_path_polygon_to_polygon_blocks_and_dummy_blocks( + const SHAPE_POLY_SET &aMainPath, CGENERICCONTAINER2D &aDstContainer, float aBiuTo3DunitsScale, float aDivFactor, const BOARD_ITEM &aBoardItem ); -void Polygon_Calc_BBox_3DU(const SHAPE_POLY_SET &aPolysList, CBBOX2D &aOutBBox , float aBiuTo3DunitsScale ); -void Polygon_Convert( const KI_POLYGON &aPolygon, ClipperLib::Path &aOutPath, CBBOX2D &aOutBBox, float aBiuTo3DunitsScale ); +void Polygon_Calc_BBox_3DU( const SHAPE_POLY_SET &aPolysList, + CBBOX2D &aOutBBox, + float aBiuTo3DunitsScale ); + +void Polygon_Convert( const KI_POLYGON &aPolygon, + ClipperLib::Path &aOutPath, + CBBOX2D &aOutBBox, + float aBiuTo3DunitsScale ); void Polygon2d_TestModule(); diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.cpp new file mode 100644 index 0000000000..6d507971fb --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.cpp @@ -0,0 +1,212 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cpolygon4pts2d.cpp + * @brief + */ + +#include "cpolygon4pts2d.h" +#include + + +CPOLYGON4PTS2D::CPOLYGON4PTS2D( const SFVEC2F &v1, + const SFVEC2F &v2, + const SFVEC2F &v3, + const SFVEC2F &v4, + const BOARD_ITEM &aBoardItem ) : + COBJECT2D( OBJ2D_POLYGON4PT, aBoardItem ) +{/* + if( (v1.x > v2.x) || (v1.y < v2.y) ) + { + m_segments[0] = v4; + m_segments[1] = v3; + m_segments[2] = v2; + m_segments[3] = v1; + } + else + {*/ + m_segments[0] = v1; + m_segments[1] = v4; + m_segments[2] = v3; + m_segments[3] = v2; + // } + + unsigned int i; + unsigned int j = 4 - 1; + + for( i = 0; i < 4; j = i++ ) + { + SFVEC2F slope = m_segments[j] - m_segments[i]; + m_precalc_slope[i] = slope; + m_seg_normal[i] = glm::normalize( SFVEC2F( -slope.y, +slope.x ) ); + } + + m_bbox.Reset(); + m_bbox.Union( v1 ); + m_bbox.Union( v2 ); + m_bbox.Union( v3 ); + m_bbox.Union( v4 ); + m_bbox.ScaleNextUp(); + m_bbox.ScaleNextUp(); + m_bbox.ScaleNextUp(); + m_bbox.ScaleNextUp(); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); + + wxASSERT( m_bbox.IsInitialized() ); +} + + +bool CPOLYGON4PTS2D::Intersects( const CBBOX2D &aBBox ) const +{ + return m_bbox.Intersects( aBBox ); + + // This source code is not working OK. + /* + if( !m_bbox.Intersects( aBBox ) ) + return false; + + // Check if the bouding box complety have inside the small bouding box + if( (aBBox.Max().x > m_bbox.Max().x) && + (aBBox.Max().y > m_bbox.Max().x) && + (aBBox.Min().x < m_bbox.Min().x) && + (aBBox.Min().y < m_bbox.Min().y) + ) + return true; + + SFVEC2F v[4]; + + v[0] = aBBox.Min(); + v[1] = SFVEC2F( aBBox.Min().x, aBBox.Max().y ); + v[2] = aBBox.Max(); + v[3] = SFVEC2F( aBBox.Max().x, aBBox.Min().y ); + + for( unsigned int i = 0; i < 4; i++ ) + { + if( IntersectSegment( m_segments[i], m_precalc_slope[i], v[0], v[1] - v[0] ) ) + return true; + if( IntersectSegment( m_segments[i], m_precalc_slope[i], v[1], v[2] - v[1] ) ) + return true; + if( IntersectSegment( m_segments[i], m_precalc_slope[i], v[2], v[3] - v[2] ) ) + return true; + if( IntersectSegment( m_segments[i], m_precalc_slope[i], v[3], v[0] - v[3] ) ) + return true; + } + + if( IsPointInside( v[0] ) ) + return true; + if( IsPointInside( v[1] ) ) + return true; + if( IsPointInside( v[2] ) ) + return true; + if( IsPointInside( v[3] ) ) + return true; + + return false;*/ +} + + +bool CPOLYGON4PTS2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // NOT IMPLEMENTED + return true; +} + + +bool CPOLYGON4PTS2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const +{ + wxASSERT( aOutT ); + wxASSERT( aNormalOut ); + + bool hited = false; + unsigned int hitIndex; + float bestHitT; + + for( unsigned int i = 0; i < 4; i++ ) + { + float t; + + if( aSegRay.IntersectSegment( m_segments[i], m_precalc_slope[i], &t ) ) + if( (hited == false) || ( t < bestHitT) ) + { + hited = true; + hitIndex = i; + bestHitT = t; + } + } + + if( hited ) + { + wxASSERT( (bestHitT >= 0.0f) && (bestHitT <= 1.0f) ); + + *aOutT = bestHitT; + *aNormalOut = m_seg_normal[hitIndex]; + + return true; + } + + return false; +} + + +INTERSECTION_RESULT CPOLYGON4PTS2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + // !TODO: + + return INTR_MISSES; +} + + +bool CPOLYGON4PTS2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + unsigned int i; + unsigned int j = 4 - 1; + bool oddNodes = false; + + for( i = 0; i < 4; j = i++ ) + { + const float polyJY = m_segments[j].y; + const float polyIY = m_segments[i].y; + + if( ((polyIY <= aPoint.y) && (polyJY >= aPoint.y)) || + ((polyJY <= aPoint.y) && (polyIY >= aPoint.y)) + ) + { + const float polyJX = m_segments[j].x; + const float polyIX = m_segments[i].x; + + if( (polyIX <= aPoint.x) || (polyJX <= aPoint.x) ) + { + oddNodes ^= ( ( polyIX + + ( ( aPoint.y - polyIY ) / ( polyJY - polyIY ) ) * + ( polyJX - polyIX ) ) < aPoint.x ); + } + } + } + + return oddNodes; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h index 3ef3396101..fc2e2abf0b 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -37,7 +37,7 @@ * (rectangles, trapezoids, with rotation.etc) * This is a simplified version of the cpolygon2d class */ -class GLM_ALIGN(CLASS_ALIGNMENT) CPOLYGON4PTS2D : public COBJECT2D +class CPOLYGON4PTS2D : public COBJECT2D { private: SFVEC2F m_segments[4]; diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cring2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cring2d.cpp new file mode 100644 index 0000000000..5f90a2f53f --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cring2d.cpp @@ -0,0 +1,196 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cring2d.cpp + * @brief + */ + +#include "cring2d.h" +#include "../../../3d_fastmath.h" +#include + + +CRING2D::CRING2D( const SFVEC2F &aCenter, float aInnerRadius, float aOuterRadius, + const BOARD_ITEM &aBoardItem ) : COBJECT2D( OBJ2D_RING, aBoardItem ) +{ + wxASSERT( aInnerRadius < aOuterRadius ); + + m_center = aCenter; + m_inner_radius = aInnerRadius; + m_outer_radius = aOuterRadius; + + m_inner_radius_squared = aInnerRadius * aInnerRadius; + m_outer_radius_squared = aOuterRadius * aOuterRadius; + + m_bbox.Reset(); + m_bbox.Set( m_center - SFVEC2F( aOuterRadius, aOuterRadius ), + m_center + SFVEC2F( aOuterRadius, aOuterRadius ) ); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); + + + wxASSERT( m_bbox.IsInitialized() ); +} + + +bool CRING2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // NOT IMPLEMENTED + return false; +} + + +bool CRING2D::Intersects( const CBBOX2D &aBBox ) const +{ + // !TODO: check the inside for a great improovment + return aBBox.Intersects( m_center, m_outer_radius_squared ); +} + + +bool CRING2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const +{ + // This code used directly from Steve Marschner's CS667 framework + // http://cs665pd.googlecode.com/svn/trunk/photon/sphere.cpp + + // Compute some factors used in computation + const float qx = (aSegRay.m_Start.x - m_center.x); + const float qy = (aSegRay.m_Start.y - m_center.y); + + const float qd = qx * aSegRay.m_Dir.x + qy * aSegRay.m_Dir.y; + const float qq = qx * qx + qy * qy; + + // solving the quadratic equation for t at the pts of intersection + // dd*t^2 + (2*qd)*t + (qq-r^2) = 0 + + const float discriminantsqr = qd * qd - qq; + const float discriminantsqr_outter = discriminantsqr + m_outer_radius_squared; + + // If the discriminant is less than zero, there is no intersection + if( discriminantsqr_outter < FLT_EPSILON ) + return false; + + // Otherwise check and make sure that the intersections occur on the ray (t + // > 0) and return the closer one + const float discriminant = sqrt( discriminantsqr_outter ); + float t = (-qd - discriminant); + + if( (t > FLT_EPSILON) && (t < aSegRay.m_Length) ) + { + SFVEC2F hitPoint = aSegRay.at( t ); + *aNormalOut = (hitPoint - m_center) / m_outer_radius; + } + else + { + const float discriminantsqr_inter = discriminantsqr + m_inner_radius_squared; + + if( discriminantsqr_inter > FLT_EPSILON ) + { + const float discriminant_inner = sqrt( discriminantsqr_inter ); + + const float t2_inner = (-qd + discriminant_inner); + + if( (t2_inner > FLT_EPSILON) && (t2_inner < aSegRay.m_Length) ) + { + t = t2_inner; + + const SFVEC2F hitPoint = aSegRay.at( t2_inner ); + + *aNormalOut = (m_center - hitPoint) / m_inner_radius; + } + else + return false; + } + else + return false; + } + + wxASSERT( (t > 0.0f) && (t <= aSegRay.m_Length) ); + + // Convert the intersection to a normalized 0.0 .. 1.0 + *aOutT = t / aSegRay.m_Length; + + return true; +} + + +INTERSECTION_RESULT CRING2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + /* + if( !m_bbox.Overlaps( aBBox ) ) + return INTR_MISSES; + + SFVEC2F v[4]; + + v[0] = aBBox.Min() - m_center; + v[1] = aBBox.Max() - m_center; + v[2] = SFVEC2F( aBBox.Min().x, aBBox.Max().y ) - m_center; + v[3] = SFVEC2F( aBBox.Max().x, aBBox.Min().y ) - m_center; + + float s[4]; + + s[0] = v[0].x * v[0].x + v[0].y * v[0].y; + s[1] = v[1].x * v[1].x + v[1].y * v[1].y; + s[2] = v[2].x * v[2].x + v[2].y * v[2].y; + s[3] = v[3].x * v[3].x + v[3].y * v[3].y; + + bool isInside[4]; + + isInside[0] = s[0] <= m_radius_squared; + isInside[1] = s[1] <= m_radius_squared; + isInside[2] = s[2] <= m_radius_squared; + isInside[3] = s[3] <= m_radius_squared; + + // Check if all points are inside the circle + if( isInside[0] && + isInside[1] && + isInside[2] && + isInside[3] ) + return INTR_FULL_INSIDE; + + // Check if any point is inside the circle + if( isInside[0] || + isInside[1] || + isInside[2] || + isInside[3] ) + return INTR_INTERSECTS; +*/ + return INTR_MISSES; +} + + +bool CRING2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + const SFVEC2F v = m_center - aPoint; + + const float dot = glm::dot( v, v ); + + if( (dot <= m_outer_radius_squared) && + (dot >= m_inner_radius_squared) ) + return true; + + return false; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cring2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cring2d.h index 7a414b597a..13f2155c68 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cring2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/cring2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,7 +32,7 @@ #include "cobject2d.h" -class GLM_ALIGN(CLASS_ALIGNMENT) CRING2D : public COBJECT2D +class CRING2D : public COBJECT2D { public: const SFVEC2F &GetCenter() const { return m_center; } diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.cpp index c78a746f05..f8aad7228b 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.cpp +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,30 +31,38 @@ #include -CROUNDSEGMENT2D::CROUNDSEGMENT2D( const SFVEC2F &aStart, const SFVEC2F &aEnd, float aWidth, +CROUNDSEGMENT2D::CROUNDSEGMENT2D( const SFVEC2F &aStart, + const SFVEC2F &aEnd, + float aWidth, const BOARD_ITEM &aBoardItem ) : COBJECT2D( OBJ2D_ROUNDSEG, aBoardItem ), m_segment( aStart, aEnd ) { + wxASSERT( aStart != aEnd ); + m_radius = (aWidth / 2.0f); m_radius_squared = m_radius * m_radius; m_width = aWidth; - SFVEC2F leftRadiusOffset( -m_segment.m_Dir.y * m_radius, m_segment.m_Dir.x * m_radius); + SFVEC2F leftRadiusOffset( -m_segment.m_Dir.y * m_radius, + m_segment.m_Dir.x * m_radius ); + m_leftStart = aStart + leftRadiusOffset; m_leftEnd = aEnd + leftRadiusOffset; - m_leftEnd_minus_start = m_leftEnd - m_leftStart; - m_leftDir = glm::normalize( m_leftEnd_minus_start ); + m_leftEndMinusStart = m_leftEnd - m_leftStart; + m_leftDir = glm::normalize( m_leftEndMinusStart ); - SFVEC2F rightRadiusOffset( -leftRadiusOffset.x, -leftRadiusOffset.y ); + SFVEC2F rightRadiusOffset( -leftRadiusOffset.x, + -leftRadiusOffset.y ); m_rightStart = aEnd + rightRadiusOffset; m_rightEnd = aStart + rightRadiusOffset; - m_rightEnd_minus_start = m_rightEnd - m_rightStart; - m_rightDir = glm::normalize( m_rightEnd_minus_start ); + m_rightEndMinusStart = m_rightEnd - m_rightStart; + m_rightDir = glm::normalize( m_rightEndMinusStart ); m_bbox.Reset(); m_bbox.Set( aStart, aEnd ); - m_bbox.Set( m_bbox.Min() - SFVEC2F( m_radius, m_radius ), m_bbox.Max() + SFVEC2F( m_radius, m_radius ) ); + m_bbox.Set( m_bbox.Min() - SFVEC2F( m_radius, m_radius ), + m_bbox.Max() + SFVEC2F( m_radius, m_radius ) ); m_bbox.ScaleNextUp(); m_centroid = m_bbox.GetCenter(); @@ -82,19 +90,37 @@ bool CROUNDSEGMENT2D::Intersects( const CBBOX2D &aBBox ) const v[3] = SFVEC2F( aBBox.Max().x, aBBox.Min().y ); // Test against the main rectangle segment - if( IntersectSegment( m_leftStart, m_leftEnd_minus_start, v[0], v[1] - v[0] ) ) return true; - if( IntersectSegment( m_leftStart, m_leftEnd_minus_start, v[1], v[2] - v[1] ) ) return true; - if( IntersectSegment( m_leftStart, m_leftEnd_minus_start, v[2], v[3] - v[2] ) ) return true; - if( IntersectSegment( m_leftStart, m_leftEnd_minus_start, v[3], v[0] - v[3] ) ) return true; + if( IntersectSegment( m_leftStart, m_leftEndMinusStart, v[0], v[1] - v[0] ) ) + return true; - if( IntersectSegment( m_rightStart, m_rightEnd_minus_start, v[0], v[1] - v[0] ) ) return true; - if( IntersectSegment( m_rightStart, m_rightEnd_minus_start, v[1], v[2] - v[1] ) ) return true; - if( IntersectSegment( m_rightStart, m_rightEnd_minus_start, v[2], v[3] - v[2] ) ) return true; - if( IntersectSegment( m_rightStart, m_rightEnd_minus_start, v[3], v[0] - v[3] ) ) return true; + if( IntersectSegment( m_leftStart, m_leftEndMinusStart, v[1], v[2] - v[1] ) ) + return true; + + if( IntersectSegment( m_leftStart, m_leftEndMinusStart, v[2], v[3] - v[2] ) ) + return true; + + if( IntersectSegment( m_leftStart, m_leftEndMinusStart, v[3], v[0] - v[3] ) ) + return true; + + + if( IntersectSegment( m_rightStart, m_rightEndMinusStart, v[0], v[1] - v[0] ) ) + return true; + + if( IntersectSegment( m_rightStart, m_rightEndMinusStart, v[1], v[2] - v[1] ) ) + return true; + + if( IntersectSegment( m_rightStart, m_rightEndMinusStart, v[2], v[3] - v[2] ) ) + return true; + + if( IntersectSegment( m_rightStart, m_rightEndMinusStart, v[3], v[0] - v[3] ) ) + return true; // Test the two circles - if( aBBox.Intersects( m_segment.m_Start, m_radius_squared ) ) return true; - if( aBBox.Intersects( m_segment.m_End, m_radius_squared ) ) return true; + if( aBBox.Intersects( m_segment.m_Start, m_radius_squared ) ) + return true; + + if( aBBox.Intersects( m_segment.m_End, m_radius_squared ) ) + return true; return false; } @@ -107,13 +133,15 @@ bool CROUNDSEGMENT2D::Overlaps( const CBBOX2D &aBBox ) const } -bool CROUNDSEGMENT2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut ) const +bool CROUNDSEGMENT2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const { wxASSERT( aOutT ); wxASSERT( aNormalOut ); - bool start_is_inside = IsPointInside( aSegRay.m_Start ); - bool end_is_inside = IsPointInside( aSegRay.m_End ); + const bool start_is_inside = IsPointInside( aSegRay.m_Start ); + const bool end_is_inside = IsPointInside( aSegRay.m_End ); // If segment if inside there are no hits if( start_is_inside && end_is_inside ) @@ -128,7 +156,9 @@ bool CROUNDSEGMENT2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F SFVEC2F farHitNormal; float leftSegT; - bool leftSegmentHit = aSegRay.IntersectSegment( m_leftStart, m_leftEnd_minus_start, &leftSegT ); + const bool leftSegmentHit = aSegRay.IntersectSegment( m_leftStart, + m_leftEndMinusStart, + &leftSegT ); if( leftSegmentHit ) { @@ -141,7 +171,9 @@ bool CROUNDSEGMENT2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F } float rightSegT; - bool rightSegmentHit = aSegRay.IntersectSegment( m_rightStart, m_rightEnd_minus_start, &rightSegT ); + const bool rightSegmentHit = aSegRay.IntersectSegment( m_rightStart, + m_rightEndMinusStart, + &rightSegT ); if( rightSegmentHit ) { @@ -167,9 +199,9 @@ bool CROUNDSEGMENT2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F SFVEC2F circleStart_N0; SFVEC2F circleStart_N1; - bool startCircleHit = aSegRay.IntersectCircle( m_segment.m_Start, m_radius, - &circleStart_T0, &circleStart_T1, - &circleStart_N0, &circleStart_N1 ); + const bool startCircleHit = aSegRay.IntersectCircle( m_segment.m_Start, m_radius, + &circleStart_T0, &circleStart_T1, + &circleStart_N0, &circleStart_N1 ); if( startCircleHit ) { @@ -207,9 +239,9 @@ bool CROUNDSEGMENT2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F SFVEC2F circleEnd_N0; SFVEC2F circleEnd_N1; - bool rightCircleHit = aSegRay.IntersectCircle( m_segment.m_End, m_radius, - &circleEnd_T0, &circleEnd_T1, - &circleEnd_N0, &circleEnd_N1 ); + const bool rightCircleHit = aSegRay.IntersectCircle( m_segment.m_End, m_radius, + &circleEnd_T0, &circleEnd_T1, + &circleEnd_N0, &circleEnd_N1 ); if( rightCircleHit ) { if( circleEnd_T0 > 0.0f ) @@ -251,9 +283,10 @@ bool CROUNDSEGMENT2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F } else { - wxASSERT( (farHitT > 0.0f) && (farHitT <= 1.0f) ); + wxASSERT( (farHitT >= 0.0f) && (farHitT <= 1.0f) ); + *aOutT = farHitT; - *aNormalOut = farHitNormal; + *aNormalOut = -farHitNormal; // the normal started inside, so invert it } } diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h index 0b4f54ae3b..8524bc456c 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,7 +32,7 @@ #include "cobject2d.h" -class GLM_ALIGN(CLASS_ALIGNMENT) CROUNDSEGMENT2D : public COBJECT2D +class CROUNDSEGMENT2D : public COBJECT2D { friend class CROUNDSEG; @@ -42,12 +42,12 @@ private: SFVEC2F m_leftStart; SFVEC2F m_leftEnd; - SFVEC2F m_leftEnd_minus_start; + SFVEC2F m_leftEndMinusStart; SFVEC2F m_leftDir; SFVEC2F m_rightStart; SFVEC2F m_rightEnd; - SFVEC2F m_rightEnd_minus_start; + SFVEC2F m_rightEndMinusStart; SFVEC2F m_rightDir; float m_radius; @@ -69,12 +69,12 @@ public: const SFVEC2F &GetLeftStar() const { return m_leftStart; } const SFVEC2F &GetLeftEnd() const { return m_leftEnd; } - const SFVEC2F &GetLeftEnd_minus_Start() const { return m_leftEnd_minus_start; } + const SFVEC2F &GetLeftEnd_minus_Start() const { return m_leftEndMinusStart; } const SFVEC2F &GetLeftDir() const { return m_leftDir; } const SFVEC2F &GetRightStar() const { return m_rightStart; } const SFVEC2F &GetRightEnd() const { return m_rightEnd; } - const SFVEC2F &GetRightEnd_minus_Start() const { return m_rightEnd_minus_start; } + const SFVEC2F &GetRightEnd_minus_Start() const { return m_rightEndMinusStart; } const SFVEC2F &GetRightDir() const { return m_rightDir; } // Imported from COBJECT2D @@ -85,5 +85,23 @@ public: bool IsPointInside( const SFVEC2F &aPoint ) const; }; +static const float s_min_dot = (FLT_EPSILON * 4.0f) ; + +/** + * @brief Segment_is_a_circle - check if segment start and end is very close to each other + * should used to check if the segment should be converted to a circle instead + * @param aStart + * @param aEnd + * @return true is it is better to convert the segment to circle + */ +inline bool Is_segment_a_circle( const SFVEC2F &aStart, const SFVEC2F &aEnd ) +{ + const SFVEC2F vec = aEnd - aStart; + + return (aStart == aEnd) || + // This is the same as calc the lenght squared (without the sqrt) + // and compare with a small value + ( glm::dot( vec, vec ) <= s_min_dot ); +} #endif // _CROUNDSEGMENT2D_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.cpp new file mode 100644 index 0000000000..0ed9d61581 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.cpp @@ -0,0 +1,316 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ctriangle2d.cpp + * @brief + */ + +#include "ctriangle2d.h" +#include +#include +#include +#include + +#include // CALLBACK definition, needed on Windows + // alse needed on OSX to define __DARWIN__ + +#include "../../../3d_fastmath.h" +#include + + +CTRIANGLE2D::CTRIANGLE2D ( const SFVEC2F &aV1, + const SFVEC2F &aV2, + const SFVEC2F &aV3, + const BOARD_ITEM &aBoardItem ) : COBJECT2D( OBJ2D_TRIANGLE, + aBoardItem ) +{ + p1 = aV1; + p2 = aV2; + p3 = aV3; + + // Pre-Calc values + m_inv_denominator = 1.0f / ( (p2.y - p3.y) * (p1.x - p3.x) + + (p3.x - p2.x) * (p1.y - p3.y)); + m_p2y_minus_p3y = (p2.y - p3.y); + m_p3x_minus_p2x = (p3.x - p2.x); + m_p3y_minus_p1y = (p3.y - p1.y); + m_p1x_minus_p3x = (p1.x - p3.x); + + m_bbox.Reset(); + m_bbox.Union( aV1 ); + m_bbox.Union( aV2 ); + m_bbox.Union( aV3 ); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); + + wxASSERT( m_bbox.IsInitialized() ); +} + + +bool CTRIANGLE2D::Intersects( const CBBOX2D &aBBox ) const +{ + if( !m_bbox.Intersects( aBBox ) ) + return false; + //!TODO: Optimize + return true; +} + + +bool CTRIANGLE2D::Overlaps( const CBBOX2D &aBBox ) const +{ + // NOT IMPLEMENTED + return false; +} + + +bool CTRIANGLE2D::Intersect( const RAYSEG2D &aSegRay, + float *aOutT, + SFVEC2F *aNormalOut ) const +{ + wxASSERT( aOutT ); + wxASSERT( aNormalOut ); + return false; +} + + +INTERSECTION_RESULT CTRIANGLE2D::IsBBoxInside( const CBBOX2D &aBBox ) const +{ + if( !m_bbox.Intersects( aBBox ) ) + return INTR_MISSES; + // !TODO: + return INTR_MISSES; +} + + +bool CTRIANGLE2D::IsPointInside( const SFVEC2F &aPoint ) const +{ + // http://totologic.blogspot.co.uk/2014/01/accurate-point-in-triangle-test.html + + SFVEC2F point_minus_p3 = aPoint - p3; + + // barycentric coordinate system + const float a = ( m_p2y_minus_p3y * point_minus_p3.x + + m_p3x_minus_p2x * point_minus_p3.y ) * m_inv_denominator; + + if( 0.0f > a || a > 1.0f ) + return false; + + const float b = ( m_p3y_minus_p1y * point_minus_p3.x + + m_p1x_minus_p3x * point_minus_p3.y ) * m_inv_denominator; + + if( 0.0f > b || b > 1.0f ) + return false; + + const float c = 1.0f - a - b; + + return 0.0f <= c && c <= 1.0f; +/* + return 0.0f <= a && a <= 1.0f && + 0.0f <= b && b <= 1.0f && + 0.0f <= c && c <= 1.0f;*/ +} + + +template void FreeClear( C & cntr ) +{ + for( typename C::iterator it = cntr.begin(); + it != cntr.end(); + ++it ) + { + delete * it; + } + + cntr.clear(); +} + +// Note: Please check edgeshrink.cpp in order to learn the EdgeShrink propose + +#define APPLY_EDGE_SHRINK + +#ifdef APPLY_EDGE_SHRINK +extern void EdgeShrink( std::vector &aPath ); + +#define POLY_SCALE_FACT 256 +#define POLY_SCALE_FACT_INVERSE (1.0 / (double)(POLY_SCALE_FACT)) +#endif + +void Convert_shape_line_polygon_to_triangles( const SHAPE_POLY_SET &aPolyList, + CGENERICCONTAINER2D &aDstContainer, + float aBiuTo3DunitsScale , + const BOARD_ITEM &aBoardItem ) +{ + unsigned int nOutlines = aPolyList.OutlineCount(); + + + for( unsigned int idx = 0; idx < nOutlines; ++idx ) + { + const SHAPE_LINE_CHAIN &outlinePath = aPolyList.COutline( idx ); + + wxASSERT( outlinePath.PointCount() >= 3 ); + + std::vector scaledOutline; + scaledOutline.resize( outlinePath.PointCount() ); + + // printf("\nidx: %u\n", idx); + + // Apply a scale to the points + for( unsigned int i = 0; + i < (unsigned int)outlinePath.PointCount(); + ++i ) + { + const VECTOR2I& a = outlinePath.CPoint( i ); + +#ifdef APPLY_EDGE_SHRINK + scaledOutline[i] = SFVEC2I64( (glm::int64)a.x * POLY_SCALE_FACT, + (glm::int64)a.y * POLY_SCALE_FACT ); +#else + scaledOutline[i] = SFVEC2I64( (glm::int64)a.x, + (glm::int64)a.y ); +#endif + } + +#ifdef APPLY_EDGE_SHRINK + // Apply a modification to the points + EdgeShrink( scaledOutline ); +#endif + // Copy to a array of pointers + std::vector polyline; + polyline.resize( outlinePath.PointCount() ); + + for( unsigned int i = 0; + i < (unsigned int)scaledOutline.size(); + ++i ) + { + const SFVEC2I64 &a = scaledOutline[i]; + + //printf("%lu %lu\n", a.x, a.y); + + polyline[i] = new p2t::Point( (double)a.x, + (double)a.y ); + } + + // Start creating the structured to be triangulated + p2t::CDT* cdt = new p2t::CDT( polyline ); + + // Add holes for this outline + unsigned int nHoles = aPolyList.HoleCount( idx ); + + std::vector< std::vector > polylineHoles; + + polylineHoles.resize( nHoles ); + + for( unsigned int idxHole = 0; idxHole < nHoles; ++idxHole ) + { + const SHAPE_LINE_CHAIN &outlineHoles = aPolyList.CHole( idx, + idxHole ); + + wxASSERT( outlineHoles.PointCount() >= 3 ); + + std::vector scaledHole; + scaledHole.resize( outlineHoles.PointCount() ); + + // Apply a scale to the points + for( unsigned int i = 0; + i < (unsigned int)outlineHoles.PointCount(); + ++i ) + { + const VECTOR2I &h = outlineHoles.CPoint( i ); +#ifdef APPLY_EDGE_SHRINK + scaledHole[i] = SFVEC2I64( (glm::int64)h.x * POLY_SCALE_FACT, + (glm::int64)h.y * POLY_SCALE_FACT ); +#else + scaledHole[i] = SFVEC2I64( (glm::int64)h.x, + (glm::int64)h.y ); +#endif + } + +#ifdef APPLY_EDGE_SHRINK + // Apply a modification to the points + EdgeShrink( scaledHole ); +#endif + + // Resize and reserve space + polylineHoles[idxHole].resize( outlineHoles.PointCount() ); + + for( unsigned int i = 0; + i < (unsigned int)outlineHoles.PointCount(); + ++i ) + { + const SFVEC2I64 &h = scaledHole[i]; + + polylineHoles[idxHole][i] = new p2t::Point( h.x, h.y ); + } + + cdt->AddHole( polylineHoles[idxHole] ); + } + + // Triangulate + cdt->Triangulate(); + + // Hint: if you find any crashes on the triangulation poly2tri library, + // you can use the following site to debug the points and it will mark + // the errors in the polygon: + // http://r3mi.github.io/poly2tri.js/ + + + // Get and add triangles + std::vector triangles; + triangles = cdt->GetTriangles(); + +#ifdef APPLY_EDGE_SHRINK + const double conver_d = (double)aBiuTo3DunitsScale * + POLY_SCALE_FACT_INVERSE; +#else + const double conver_d = (double)aBiuTo3DunitsScale; +#endif + for( unsigned int i = 0; i < triangles.size(); ++i ) + { + p2t::Triangle& t = *triangles[i]; + + p2t::Point& a = *t.GetPoint( 0 ); + p2t::Point& b = *t.GetPoint( 1 ); + p2t::Point& c = *t.GetPoint( 2 ); + + aDstContainer.Add( new CTRIANGLE2D( SFVEC2F( a.x * conver_d, + -a.y * conver_d ), + SFVEC2F( b.x * conver_d, + -b.y * conver_d ), + SFVEC2F( c.x * conver_d, + -c.y * conver_d ), + aBoardItem ) ); + } + + // Delete created data + delete cdt; + + // Free points + FreeClear(polyline); + + for( unsigned int idxHole = 0; idxHole < nHoles; ++idxHole ) + { + FreeClear( polylineHoles[idxHole] ); + } + } +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h index 34769fc020..71e361fa33 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,7 +36,7 @@ #include #include -class GLM_ALIGN(CLASS_ALIGNMENT) CTRIANGLE2D : public COBJECT2D +class CTRIANGLE2D : public COBJECT2D { private: SFVEC2F p1; diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/edgeshrink.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/edgeshrink.cpp new file mode 100644 index 0000000000..1027ec3122 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes2D/edgeshrink.cpp @@ -0,0 +1,89 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file edgeshrink.cpp + * @brief The edgeShrink function was found in the project clip2tri by the: + * Bitfighter project (http://bitfighter.org) + * https://github.com/raptor/clip2tri + * https://github.com/raptor/clip2tri/blob/f62a734d22733814b8a970ed8a68a4d94c24fa5f/clip2tri/clip2tri.cpp#L150 + */ + +#include +#include + +// clip2tri is Licenced under: + +// The MIT License (MIT) + +// Copyright (c) 2014 Bitfighter developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +// Shrink large polygons by reducing each coordinate by 1 in the +// general direction of the last point as we wind around +// +// This normally wouldn't work in every case, but our upscaled-by-1000 polygons +// have little chance to create new duplicate points with this method. +// +// For information on why this was needed, see: +// +// https://github.com/greenm01/poly2tri/issues/90 +// + +#define S_INC 1 + +void EdgeShrink( std::vector &aPath ) +{ + unsigned int prev = aPath.size() - 1; + + for( unsigned int i = 0; i < aPath.size(); i++ ) + { + // Adjust coordinate by 1 depending on the direction + (aPath[i].x - aPath[prev].x) > 0 ? aPath[i].x -= S_INC : + aPath[i].x += S_INC; + + (aPath[i].y - aPath[prev].y) > 0 ? aPath[i].y -= S_INC : + aPath[i].y += S_INC; + + prev = i; + } +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox.cpp index fa661db1fb..1101b21cf4 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox.cpp +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,11 +26,11 @@ * @file cbbox.cpp * @brief Bounding Box class implementation */ -#include "3d_math/3d_fastmath.h" +#include "3d_fastmath.h" #include "cbbox.h" #include -#include // For the wxASSERT +#include // For the wxASSERT CBBOX::CBBOX() @@ -57,6 +57,13 @@ CBBOX::~CBBOX() } +void CBBOX::Set( const SFVEC3F &aPoint ) +{ + m_min = aPoint; + m_max = aPoint; +} + + void CBBOX::Set( const SFVEC3F &aPbMin, const SFVEC3F &aPbMax ) { m_min.x = fminf( aPbMin.x, aPbMax.x ); @@ -111,8 +118,7 @@ void CBBOX::Union( const SFVEC3F &aPoint ) void CBBOX::Union( const CBBOX &aBBox ) { - if( !aBBox.IsInitialized() ) - return; + wxASSERT( aBBox.IsInitialized() ); // get the minimun value between the added bounding box and the existent bounding box m_min.x = fmin( m_min.x, aBBox.m_min.x ); @@ -151,8 +157,10 @@ unsigned int CBBOX::MaxDimension() const SFVEC3F extent = GetExtent(); - if( extent.y > extent.x ) result = 1; - if( extent.z > extent.y ) result = 2; + if( extent.y > extent.x ) + result = 1; + if( extent.z > extent.y ) + result = 2; return result; } @@ -164,8 +172,10 @@ float CBBOX::GetMaxDimension() const SFVEC3F extent = GetExtent(); - if( extent.y > extent.x ) max_dimensions_idx = 1; - if( extent.z > extent.y ) max_dimensions_idx = 2; + if( extent.y > extent.x ) + max_dimensions_idx = 1; + if( extent.z > extent.y ) + max_dimensions_idx = 2; return extent[max_dimensions_idx]; } @@ -288,14 +298,18 @@ bool CBBOX::Intersect( const RAY &aRay, float *aOutHitt0, float *aOutHitt1 ) return false; } - if( aOutHitt0 ) *aOutHitt0 = t0; - if( aOutHitt1 ) *aOutHitt1 = t1; + if( aOutHitt0 ) + *aOutHitt0 = t0; + if( aOutHitt1 ) + *aOutHitt1 = t1; return true; } #else // https://github.com/mmp/pbrt-v2/blob/master/src/accelerators/bvh.cpp#L126 -bool CBBOX::Intersect( const RAY &aRay, float *aOutHitt0, float *aOutHitt1 ) const +bool CBBOX::Intersect( const RAY &aRay, + float *aOutHitt0, + float *aOutHitt1 ) const { wxASSERT( aOutHitt0 ); wxASSERT( aOutHitt1 ); @@ -303,8 +317,8 @@ bool CBBOX::Intersect( const RAY &aRay, float *aOutHitt0, float *aOutHitt1 ) con const SFVEC3F bounds[2] = {m_min, m_max}; // Check for ray intersection against x and y slabs - float tmin = (bounds[ aRay.m_dirIsNeg[0]].x - aRay.m_Origin.x) * aRay.m_InvDir.x; - float tmax = (bounds[1 - aRay.m_dirIsNeg[0]].x - aRay.m_Origin.x) * aRay.m_InvDir.x; + float tmin = (bounds[ aRay.m_dirIsNeg[0]].x - aRay.m_Origin.x) * aRay.m_InvDir.x; + float tmax = (bounds[1 - aRay.m_dirIsNeg[0]].x - aRay.m_Origin.x) * aRay.m_InvDir.x; float tymin = (bounds[ aRay.m_dirIsNeg[1]].y - aRay.m_Origin.y) * aRay.m_InvDir.y; float tymax = (bounds[1 - aRay.m_dirIsNeg[1]].y - aRay.m_Origin.y) * aRay.m_InvDir.y; @@ -315,8 +329,8 @@ bool CBBOX::Intersect( const RAY &aRay, float *aOutHitt0, float *aOutHitt1 ) con tmax = (tymax < tmax)? tymax : tmax; // Check for ray intersection against z slab - float tzmin = (bounds[ aRay.m_dirIsNeg[2]].z - aRay.m_Origin.z) * aRay.m_InvDir.z; - float tzmax = (bounds[1 - aRay.m_dirIsNeg[2]].z - aRay.m_Origin.z) * aRay.m_InvDir.z; + const float tzmin = (bounds[ aRay.m_dirIsNeg[2]].z - aRay.m_Origin.z) * aRay.m_InvDir.z; + const float tzmax = (bounds[1 - aRay.m_dirIsNeg[2]].z - aRay.m_Origin.z) * aRay.m_InvDir.z; if( (tmin > tzmax) || (tzmin > tmax) ) return false; @@ -338,8 +352,11 @@ void CBBOX::ApplyTransformation( glm::mat4 aTransformMatrix ) { wxASSERT( IsInitialized() ); - SFVEC3F v1 = SFVEC3F( aTransformMatrix * glm::vec4( m_min.x, m_min.y, m_min.z, 1.0f ) ); - SFVEC3F v2 = SFVEC3F( aTransformMatrix * glm::vec4( m_max.x, m_max.y, m_max.z, 1.0f ) ); + const SFVEC3F v1 = SFVEC3F( aTransformMatrix * + glm::vec4( m_min.x, m_min.y, m_min.z, 1.0f ) ); + + const SFVEC3F v2 = SFVEC3F( aTransformMatrix * + glm::vec4( m_max.x, m_max.y, m_max.z, 1.0f ) ); Reset(); Union( v1 ); @@ -349,19 +366,27 @@ void CBBOX::ApplyTransformation( glm::mat4 aTransformMatrix ) void CBBOX::ApplyTransformationAA( glm::mat4 aTransformMatrix ) { - // XXX - CB - commented out because it spams the legacy renderer - // wxASSERT( IsInitialized() ); + wxASSERT( IsInitialized() ); // apply the transformation matrix for each of vertices of the bounding box // and make a union with all vertices - CBBOX tmpBBox = CBBOX( SFVEC3F( aTransformMatrix * glm::vec4( m_min.x, m_min.y, m_min.z, 1.0f ) ) ); - tmpBBox.Union( SFVEC3F( aTransformMatrix * glm::vec4( m_max.x, m_min.y, m_min.z, 1.0f ) ) ); - tmpBBox.Union( SFVEC3F( aTransformMatrix * glm::vec4( m_min.x, m_max.y, m_min.z, 1.0f ) ) ); - tmpBBox.Union( SFVEC3F( aTransformMatrix * glm::vec4( m_min.x, m_min.y, m_max.z, 1.0f ) ) ); - tmpBBox.Union( SFVEC3F( aTransformMatrix * glm::vec4( m_min.x, m_max.y, m_max.z, 1.0f ) ) ); - tmpBBox.Union( SFVEC3F( aTransformMatrix * glm::vec4( m_max.x, m_max.y, m_min.z, 1.0f ) ) ); - tmpBBox.Union( SFVEC3F( aTransformMatrix * glm::vec4( m_max.x, m_min.y, m_max.z, 1.0f ) ) ); - tmpBBox.Union( SFVEC3F( aTransformMatrix * glm::vec4( m_max.x, m_max.y, m_max.z, 1.0f ) ) ); + CBBOX tmpBBox = CBBOX( + SFVEC3F( aTransformMatrix * + glm::vec4( m_min.x, m_min.y, m_min.z, 1.0f ) ) ); + tmpBBox.Union( SFVEC3F( aTransformMatrix * + glm::vec4( m_max.x, m_min.y, m_min.z, 1.0f ) ) ); + tmpBBox.Union( SFVEC3F( aTransformMatrix * + glm::vec4( m_min.x, m_max.y, m_min.z, 1.0f ) ) ); + tmpBBox.Union( SFVEC3F( aTransformMatrix * + glm::vec4( m_min.x, m_min.y, m_max.z, 1.0f ) ) ); + tmpBBox.Union( SFVEC3F( aTransformMatrix * + glm::vec4( m_min.x, m_max.y, m_max.z, 1.0f ) ) ); + tmpBBox.Union( SFVEC3F( aTransformMatrix * + glm::vec4( m_max.x, m_max.y, m_min.z, 1.0f ) ) ); + tmpBBox.Union( SFVEC3F( aTransformMatrix * + glm::vec4( m_max.x, m_min.y, m_max.z, 1.0f ) ) ); + tmpBBox.Union( SFVEC3F( aTransformMatrix * + glm::vec4( m_max.x, m_max.y, m_max.z, 1.0f ) ) ); m_min = tmpBBox.m_min; m_max = tmpBBox.m_max; diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox.h index c0d56d5f48..38dd52794e 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,15 +30,14 @@ #ifndef _CBBOX_H_ #define _CBBOX_H_ -#include "plugins/3dapi/xv3d_types.h" -#include "3d_rendering/3d_render_raytracing/ray.h" -#include // For the DBG( +#include "../ray.h" +#include // For the DBG( /** * Class CBBOX * manages a bounding box defined by two SFVEC3F min max points. */ -GLM_ALIGNED_STRUCT(CLASS_ALIGNMENT) CBBOX +struct CBBOX { public: @@ -54,7 +53,7 @@ public: * Initialize a bounding box with a given point * @param aPbInit a point for the bounding box initialization */ - CBBOX( const SFVEC3F &aPbInit ); + explicit CBBOX( const SFVEC3F &aPbInit ); /** * Constructor CBBOX @@ -77,6 +76,13 @@ public: void Set( const CBBOX &aBBox ); + /** + * @brief Set + * @param aPbMin + * @param aPbMax + */ + void Set( const SFVEC3F &aPoint ); + /** * Function Union * recalculate the bounding box adding a point @@ -246,8 +252,8 @@ public: private: - SFVEC3F m_min; ///< (12) point of the lower position of the bounding box - SFVEC3F m_max; ///< (12) point of the higher position of the bounding box + SFVEC3F m_min; ///< (12) point of the lower position of the bounding box + SFVEC3F m_max; ///< (12) point of the higher position of the bounding box }; #endif // CBBox_h diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox_ray.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox_ray.cpp index 5d1dc2b588..a2244d1aed 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox_ray.cpp +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cbbox_ray.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,19 +45,21 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const { - switch ( aRay.m_Classification ) + switch( aRay.m_Classification ) { case MMM: { - if (( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) - || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) - || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) - || ( aRay.kbyj * m_min.y - m_max.z + aRay.c_yz > 0) - || ( aRay.kbyi * m_min.x - m_max.z + aRay.c_xz > 0) - || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) - ) - return false; + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.y < m_min.y ) || + ( aRay.m_Origin.z < m_min.z ) + || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) + || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) + || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) + || ( aRay.kbyj * m_min.y - m_max.z + aRay.c_yz > 0) + || ( aRay.kbyi * m_min.x - m_max.z + aRay.c_xz > 0) + || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) + ) + return false; // compute the intersection distance @@ -79,15 +81,17 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case MMP: { - if (( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) - || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) - || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) - || ( aRay.kbyj * m_min.y - m_min.z + aRay.c_yz < 0) - || ( aRay.kbyi * m_min.x - m_min.z + aRay.c_xz < 0) - || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) - ) - return false; + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.y < m_min.y ) || + ( aRay.m_Origin.z > m_max.z ) + || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) + || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) + || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) + || ( aRay.kbyj * m_min.y - m_min.z + aRay.c_yz < 0) + || ( aRay.kbyi * m_min.x - m_min.z + aRay.c_xz < 0) + || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) + ) + return false; *t = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -106,15 +110,17 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case MPM: { - if (( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) - || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) - || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) - || ( aRay.kbyj * m_max.y - m_max.z + aRay.c_yz > 0) - || ( aRay.kbyi * m_min.x - m_max.z + aRay.c_xz > 0) - || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) - ) - return false; + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.y > m_max.y ) || + ( aRay.m_Origin.z < m_min.z ) + || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) + || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) + || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) + || ( aRay.kbyj * m_max.y - m_max.z + aRay.c_yz > 0) + || ( aRay.kbyi * m_min.x - m_max.z + aRay.c_xz > 0) + || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) + ) + return false; *t = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -133,15 +139,17 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case MPP: { - if (( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) - || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) - || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) - || ( aRay.kbyj * m_max.y - m_min.z + aRay.c_yz < 0) - || ( aRay.kbyi * m_min.x - m_min.z + aRay.c_xz < 0) - || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) - ) - return false; + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.y > m_max.y ) || + ( aRay.m_Origin.z > m_max.z ) + || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) + || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) + || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) + || ( aRay.kbyj * m_max.y - m_min.z + aRay.c_yz < 0) + || ( aRay.kbyi * m_min.x - m_min.z + aRay.c_xz < 0) + || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) + ) + return false; *t = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -160,15 +168,17 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case PMM: { - if (( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) - || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) - || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) - || ( aRay.kbyj * m_min.y - m_max.z + aRay.c_yz > 0) - || ( aRay.kbyi * m_max.x - m_max.z + aRay.c_xz > 0) - || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) - ) - return false; + if( ( aRay.m_Origin.x > m_max.x ) || + ( aRay.m_Origin.y < m_min.y ) || + ( aRay.m_Origin.z < m_min.z ) + || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) + || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) + || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) + || ( aRay.kbyj * m_min.y - m_max.z + aRay.c_yz > 0) + || ( aRay.kbyi * m_max.x - m_max.z + aRay.c_xz > 0) + || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -188,15 +198,17 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case PMP: { - if (( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) - || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) - || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) - || ( aRay.kbyj * m_min.y - m_min.z + aRay.c_yz < 0) - || ( aRay.kbyi * m_max.x - m_min.z + aRay.c_xz < 0) - || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) - ) - return false; + if( ( aRay.m_Origin.x > m_max.x ) || + ( aRay.m_Origin.y < m_min.y ) || + ( aRay.m_Origin.z > m_max.z ) + || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) + || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) + || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) + || ( aRay.kbyj * m_min.y - m_min.z + aRay.c_yz < 0) + || ( aRay.kbyi * m_max.x - m_min.z + aRay.c_xz < 0) + || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -215,15 +227,17 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case PPM: { - if (( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) - || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) - || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) - || ( aRay.kbyj * m_max.y - m_max.z + aRay.c_yz > 0) - || ( aRay.kbyi * m_max.x - m_max.z + aRay.c_xz > 0) - || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) - ) - return false; + if( ( aRay.m_Origin.x > m_max.x ) || + ( aRay.m_Origin.y > m_max.y ) || + ( aRay.m_Origin.z < m_min.z ) + || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) + || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) + || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) + || ( aRay.kbyj * m_max.y - m_max.z + aRay.c_yz > 0) + || ( aRay.kbyi * m_max.x - m_max.z + aRay.c_xz > 0) + || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -242,15 +256,17 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case PPP: { - if (( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) - || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) - || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) - || ( aRay.kbyj * m_max.y - m_min.z + aRay.c_yz < 0) - || ( aRay.kbyi * m_max.x - m_min.z + aRay.c_xz < 0) - || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) - ) - return false; + if( ( aRay.m_Origin.x > m_max.x ) || + ( aRay.m_Origin.y > m_max.y ) || + ( aRay.m_Origin.z > m_max.z ) + || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) + || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) + || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) + || ( aRay.kbyj * m_max.y - m_min.z + aRay.c_yz < 0) + || ( aRay.kbyi * m_max.x - m_min.z + aRay.c_xz < 0) + || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -269,12 +285,13 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case OMM: { - if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) - || ( aRay.kbyj * m_min.y - m_max.z + aRay.c_yz > 0) - ) - return false; + if(( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.x > m_max.x ) + || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z < m_min.z) + || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) + || ( aRay.kbyj * m_min.y - m_max.z + aRay.c_yz > 0) + ) + return false; *t = (m_max.y - aRay.m_Origin.y) * aRay.m_InvDir.y; @@ -288,12 +305,13 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case OMP: { - if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) - || ( aRay.kbyj * m_min.y - m_min.z + aRay.c_yz < 0) - ) - return false; + if(( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.x > m_max.x ) + || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z > m_max.z) + || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) + || ( aRay.kbyj * m_min.y - m_min.z + aRay.c_yz < 0) + ) + return false; *t = (m_max.y - aRay.m_Origin.y) * aRay.m_InvDir.y; @@ -307,12 +325,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case OPM: { - if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) - || ( aRay.kbyj * m_max.y - m_max.z + aRay.c_yz > 0) - ) - return false; + if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) + || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) + || ( aRay.kbyj * m_max.y - m_max.z + aRay.c_yz > 0) + ) + return false; *t = (m_min.y - aRay.m_Origin.y) * aRay.m_InvDir.y; @@ -326,12 +344,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case OPP: { - if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) - || ( aRay.kbyj * m_max.y - m_min.z + aRay.c_yz < 0) - ) - return false; + if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z > m_max.z) + || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) + || ( aRay.kbyj * m_max.y - m_min.z + aRay.c_yz < 0) + ) + return false; *t = (m_min.y - aRay.m_Origin.y) * aRay.m_InvDir.y; @@ -346,12 +364,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case MOM: { - if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) - || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.kbyi * m_min.x - m_max.z + aRay.c_xz > 0) - || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) - ) - return false; + if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) + || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.z < m_min.z) + || ( aRay.kbyi * m_min.x - m_max.z + aRay.c_xz > 0) + || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) + ) + return false; *t = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -366,12 +384,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case MOP: { - if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) - || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.kbyi * m_min.x - m_min.z + aRay.c_xz < 0) - || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) - ) - return false; + if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) + || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.z > m_max.z) + || ( aRay.kbyi * m_min.x - m_min.z + aRay.c_xz < 0) + || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) + ) + return false; *t = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -385,12 +403,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case POM: { - if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) - || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.kbyi * m_max.x - m_max.z + aRay.c_xz > 0) - || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) - ) - return false; + if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) + || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.z < m_min.z) + || ( aRay.kbyi * m_max.x - m_max.z + aRay.c_xz > 0) + || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -405,12 +423,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case POP: { - if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) - || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.kbyi * m_max.x - m_min.z + aRay.c_xz < 0) - || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) - ) - return false; + if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) + || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.z > m_max.z) + || ( aRay.kbyi * m_max.x - m_min.z + aRay.c_xz < 0) + || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -424,12 +442,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case MMO: { - if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y < m_min.y) - || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) - || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) - ) - return false; + if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) + || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y < m_min.y) + || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) + || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) + ) + return false; *t = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -443,12 +461,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case MPO: { - if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y > m_max.y) - || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) - || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) - ) - return false; + if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) + || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y > m_max.y) + || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) + || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) + ) + return false; *t = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -463,12 +481,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case PMO: { - if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) - || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) - || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) - ) - return false; + if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) + || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) + || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) + || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -482,12 +500,12 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case PPO: { - if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) - || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y > m_max.y) - || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) - || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) - ) - return false; + if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) + || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y > m_max.y) + || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) + || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -502,11 +520,11 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case MOO: { - if(( aRay.m_Origin.x < m_min.x) - || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) - || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) - ) - return false; + if(( aRay.m_Origin.x < m_min.x) + || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) + || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) + ) + return false; *t = (m_max.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -515,11 +533,11 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case POO: { - if(( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) - || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) - ) - return false; + if(( aRay.m_Origin.x > m_max.x) + || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) + || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) + ) + return false; *t = (m_min.x - aRay.m_Origin.x) * aRay.m_InvDir.x; @@ -528,11 +546,11 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case OMO: { - if(( aRay.m_Origin.y < m_min.y) - || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) - ) - return false; + if(( aRay.m_Origin.y < m_min.y) + || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) + ) + return false; *t = (m_max.y - aRay.m_Origin.y) * aRay.m_InvDir.y; @@ -541,11 +559,11 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case OPO: { - if(( aRay.m_Origin.y > m_max.y) - || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) - ) - return false; + if(( aRay.m_Origin.y > m_max.y) + || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) + ) + return false; *t = (m_min.y - aRay.m_Origin.y) * aRay.m_InvDir.y; @@ -555,11 +573,11 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case OOM: { - if(( aRay.m_Origin.z < m_min.z) - || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) - ) - return false; + if(( aRay.m_Origin.z < m_min.z) + || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) + ) + return false; *t = (m_max.z - aRay.m_Origin.z) * aRay.m_InvDir.z; @@ -568,11 +586,11 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const case OOP: { - if(( aRay.m_Origin.z > m_max.z) - || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) - ) - return false; + if(( aRay.m_Origin.z > m_max.z) + || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) + ) + return false; *t = (m_min.z - aRay.m_Origin.z) * aRay.m_InvDir.z; @@ -586,11 +604,13 @@ bool CBBOX::Intersect( const RAY &aRay, float *t ) const bool CBBOX::Intersect( const RAY &aRay ) const { - switch ( aRay.m_Classification ) + switch( aRay.m_Classification ) { case MMM: - { - if (( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z < m_min.z) + { + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.y < m_min.y ) || + ( aRay.m_Origin.z < m_min.z ) || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) @@ -599,14 +619,15 @@ bool CBBOX::Intersect( const RAY &aRay ) const || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) ) return false; - - return true; - } + return true; + } case MMP: - { - if (( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z > m_max.z) + { + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.y < m_min.y ) || + ( aRay.m_Origin.z > m_max.z ) || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) @@ -615,13 +636,14 @@ bool CBBOX::Intersect( const RAY &aRay ) const || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) ) return false; - - return true; - } + return true; + } case MPM: - { - if (( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) + { + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.y > m_max.y ) || + ( aRay.m_Origin.z < m_min.z ) || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) @@ -630,13 +652,14 @@ bool CBBOX::Intersect( const RAY &aRay ) const || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) ) return false; - - return true; - } + return true; + } case MPP: - { - if (( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z > m_max.z) + { + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.y > m_max.y ) || + ( aRay.m_Origin.z > m_max.z ) || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) @@ -645,13 +668,14 @@ bool CBBOX::Intersect( const RAY &aRay ) const || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) ) return false; - - return true; - } + return true; + } case PMM: - { - if (( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z < m_min.z) + { + if( ( aRay.m_Origin.x > m_max.x ) || + ( aRay.m_Origin.y < m_min.y ) || + ( aRay.m_Origin.z < m_min.z ) || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) @@ -660,14 +684,15 @@ bool CBBOX::Intersect( const RAY &aRay ) const || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) ) return false; - - return true; - } + return true; + } case PMP: - { - if (( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z > m_max.z) + { + if( ( aRay.m_Origin.x > m_max.x ) || + ( aRay.m_Origin.y < m_min.y ) || + ( aRay.m_Origin.z > m_max.z ) || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) @@ -676,13 +701,14 @@ bool CBBOX::Intersect( const RAY &aRay ) const || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) ) return false; - - return true; - } + return true; + } case PPM: - { - if (( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) + { + if( ( aRay.m_Origin.x > m_max.x ) || + ( aRay.m_Origin.y > m_max.y ) || + ( aRay.m_Origin.z < m_min.z ) || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) @@ -691,13 +717,14 @@ bool CBBOX::Intersect( const RAY &aRay ) const || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) ) return false; - - return true; - } + return true; + } case PPP: - { - if (( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z > m_max.z) + { + if( ( aRay.m_Origin.x > m_max.x ) || + ( aRay.m_Origin.y > m_max.y ) || + ( aRay.m_Origin.z > m_max.z ) || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) @@ -706,225 +733,209 @@ bool CBBOX::Intersect( const RAY &aRay ) const || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) ) return false; - - return true; - } + return true; + } case OMM: - { - if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) - || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z < m_min.z) - || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) - || ( aRay.kbyj * m_min.y - m_max.z + aRay.c_yz > 0) - ) + { + if( ( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.x > m_max.x ) + || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z < m_min.z) + || ( aRay.jbyk * m_min.z - m_max.y + aRay.c_zy > 0) + || ( aRay.kbyj * m_min.y - m_max.z + aRay.c_yz > 0) ) return false; - - return true; - } + return true; + } case OMP: - { - if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + { + if(( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.x > m_max.x ) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.z > m_max.z) || ( aRay.jbyk * m_max.z - m_max.y + aRay.c_zy > 0) || ( aRay.kbyj * m_min.y - m_min.z + aRay.c_yz < 0) ) return false; - - return true; - } + return true; + } case OPM: - { - if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + { + if(( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.x > m_max.x ) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) || ( aRay.jbyk * m_min.z - m_min.y + aRay.c_zy < 0) || ( aRay.kbyj * m_max.y - m_max.z + aRay.c_yz > 0) ) return false; - - return true; - } + return true; + } case OPP: - { - if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) + { + if(( aRay.m_Origin.x < m_min.x ) || + ( aRay.m_Origin.x > m_max.x ) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z > m_max.z) || ( aRay.jbyk * m_max.z - m_min.y + aRay.c_zy < 0) || ( aRay.kbyj * m_max.y - m_min.z + aRay.c_yz < 0) ) return false; - - return true; - } + return true; + } case MOM: - { + { if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.z < m_min.z) || ( aRay.kbyi * m_min.x - m_max.z + aRay.c_xz > 0) || ( aRay.ibyk * m_min.z - m_max.x + aRay.c_zx > 0) ) return false; - - return true; - } + return true; + } case MOP: - { + { if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.z > m_max.z) || ( aRay.kbyi * m_min.x - m_min.z + aRay.c_xz < 0) || ( aRay.ibyk * m_max.z - m_max.x + aRay.c_zx > 0) ) return false; - - return true; - } + return true; + } case POM: - { + { if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.z < m_min.z) || ( aRay.kbyi * m_max.x - m_max.z + aRay.c_xz > 0) || ( aRay.ibyk * m_min.z - m_min.x + aRay.c_zx < 0) ) return false; - - return true; - } + return true; + } case POP: - { + { if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.z > m_max.z) || ( aRay.kbyi * m_max.x - m_min.z + aRay.c_xz < 0) || ( aRay.ibyk * m_max.z - m_min.x + aRay.c_zx < 0) ) return false; - - return true; - } + return true; + } case MMO: - { + { if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.jbyi * m_min.x - m_max.y + aRay.c_xy > 0) || ( aRay.ibyj * m_min.y - m_max.x + aRay.c_yx > 0) ) return false; - - return true; - } + return true; + } case MPO: - { + { if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.jbyi * m_min.x - m_min.y + aRay.c_xy < 0) || ( aRay.ibyj * m_max.y - m_max.x + aRay.c_yx > 0) ) return false; - - return true; - } + return true; + } case PMO: - { + { if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.jbyi * m_max.x - m_max.y + aRay.c_xy > 0) || ( aRay.ibyj * m_min.y - m_min.x + aRay.c_yx < 0) ) return false; - - return true; - } + return true; + } case PPO: - { + { if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y > m_max.y) || ( aRay.jbyi * m_max.x - m_min.y + aRay.c_xy < 0) || ( aRay.ibyj * m_max.y - m_min.x + aRay.c_yx < 0) ) return false; - - return true; - } + return true; + } case MOO: - { + { if(( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) ) return false; - - return true; - } + return true; + } case POO: - { + { if(( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) ) return false; - - return true; - } + return true; + } case OMO: - { + { if(( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) ) return false; - - return true; - } + return true; + } case OPO: - { + { if(( aRay.m_Origin.y > m_max.y) || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.z > m_max.z) ) return false; - - return true; - } + return true; + } case OOM: - { + { if(( aRay.m_Origin.z < m_min.z) || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) ) return false; - - return true; - } + return true; + } case OOP: - { + { if(( aRay.m_Origin.z > m_max.z) || ( aRay.m_Origin.x < m_min.x) || ( aRay.m_Origin.x > m_max.x) || ( aRay.m_Origin.y < m_min.y) || ( aRay.m_Origin.y > m_max.y) ) return false; - - return true; - } + return true; + } } return false; diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ccylinder.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ccylinder.cpp new file mode 100644 index 0000000000..eac6411d57 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ccylinder.cpp @@ -0,0 +1,184 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ccylinder.cpp + * @brief + */ + +#include "3d_fastmath.h" +#include "ccylinder.h" + + +CVCYLINDER::CVCYLINDER( SFVEC2F aCenterPoint, + float aZmin, + float aZmax, + float aRadius ) : COBJECT( OBJ3D_CYLINDER ) +{ + m_center = aCenterPoint; + m_radius_squared = aRadius * aRadius; + m_inv_radius = 1.0f / aRadius; + + m_bbox.Set( SFVEC3F( aCenterPoint.x - aRadius, + aCenterPoint.y - aRadius, + aZmin ), + SFVEC3F( aCenterPoint.x + aRadius, + aCenterPoint.y + aRadius, + aZmax ) ); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); +} + + +bool CVCYLINDER::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +{ + // Based on: + // http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp + // Ray-sphere intersection: geometric + // ///////////////////////////////////////////////////////////////////////// + const double OCx_Start = aRay.m_Origin.x - m_center.x; + const double OCy_Start = aRay.m_Origin.y - m_center.y; + + const double p_dot_p = OCx_Start * OCx_Start + OCy_Start * OCy_Start; + + const double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x + + (double)aRay.m_Dir.y * (double)aRay.m_Dir.y; + const double b = (double)aRay.m_Dir.x * (double)OCx_Start + + (double)aRay.m_Dir.y * (double)OCy_Start; + const double c = p_dot_p - m_radius_squared; + + const float delta = (float)(b * b - a * c); + + bool hitResult = false; + + if( delta > FLT_EPSILON ) + { + const float inv_a = 1.0 / a; + + const float sdelta = sqrtf( delta ); + const float t = (-b - sdelta) * inv_a; + const float z = aRay.m_Origin.z + t * aRay.m_Dir.z; + + if( (z >= m_bbox.Min().z) && + (z <= m_bbox.Max().z) ) + { + if( t < aHitInfo.m_tHit ) + { + hitResult = true; + aHitInfo.m_tHit = t; + } + } + + if( !hitResult ) + { + const float t1 = (-b + sdelta) * inv_a; + const float z1 = aRay.m_Origin.z + t1 * aRay.m_Dir.z; + + if( (z1 > m_bbox.Min().z ) && + (z1 < m_bbox.Max().z ) ) + { + if( t1 < aHitInfo.m_tHit ) + { + hitResult = true; + aHitInfo.m_tHit = t1; + } + } + } + } + + if( hitResult ) + { + const SFVEC2F hitPoint2D = aRay.at2D( aHitInfo.m_tHit ); + //aHitInfo.m_HitPoint = + aHitInfo.m_HitNormal = SFVEC3F( -(hitPoint2D.x - m_center.x) * m_inv_radius, + -(hitPoint2D.y - m_center.y) * m_inv_radius, + 0.0f ); + aHitInfo.pHitObject = this; + } + + return hitResult; +} + + +bool CVCYLINDER::IntersectP(const RAY &aRay , float aMaxDistance ) const +{ + // Based on: + // http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp + // Ray-sphere intersection: geometric + // ///////////////////////////////////////////////////////////////////////// + const double OCx_Start = aRay.m_Origin.x - m_center.x; + const double OCy_Start = aRay.m_Origin.y - m_center.y; + + const double p_dot_p = OCx_Start * OCx_Start + OCy_Start * OCy_Start; + + const double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x + + (double)aRay.m_Dir.y * (double)aRay.m_Dir.y; + const double b = (double)aRay.m_Dir.x * (double)OCx_Start + + (double)aRay.m_Dir.y * (double)OCy_Start; + const double c = p_dot_p - m_radius_squared; + + const float delta = (float)(b * b - a * c); + + if( delta > FLT_EPSILON ) + { + const float inv_a = 1.0 / a; + + const float sdelta = sqrtf( delta ); + const float t = (-b - sdelta) * inv_a; + const float z = aRay.m_Origin.z + t * aRay.m_Dir.z; + + if( (z >= m_bbox.Min().z) && + (z <= m_bbox.Max().z) ) + { + if( t < aMaxDistance ) + return true; + } + + const float t1 = (-b + sdelta) * inv_a; + const float z1 = aRay.m_Origin.z + t1 * aRay.m_Dir.z; + + if( (z1 > m_bbox.Min().z ) && + (z1 < m_bbox.Max().z ) ) + { + if( t1 < aMaxDistance ) + return true; + } + } + + return false; +} + + +bool CVCYLINDER::Intersects( const CBBOX &aBBox ) const +{ + // !TODO: improove + return m_bbox.Intersects( aBBox ); +} + +SFVEC3F CVCYLINDER::GetDiffuseColor( const HITINFO &aHitInfo ) const +{ + (void)aHitInfo; // unused + + return m_diffusecolor; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ccylinder.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ccylinder.h new file mode 100644 index 0000000000..c6099fe88a --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ccylinder.h @@ -0,0 +1,66 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ccylinder.h + * @brief + */ + +#ifndef _CCYLINDER_H_ +#define _CCYLINDER_H_ + +#include "cobject.h" + +/** + * A vertical cylinder + */ +class CVCYLINDER : public COBJECT +{ + +public: + /** + * Constructor CVCYLINDER + * @param aCenterPoint = position of the center of the plane + * @param aXSize = size by X axis + * @param aYSize = size by Y axis + */ + CVCYLINDER( SFVEC2F aCenterPoint, float aZmin, float aZmax, float aRadius ); + + void SetColor( SFVEC3F aObjColor ) { m_diffusecolor = aObjColor; } + + // Imported from COBJECT + bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; + bool IntersectP(const RAY &aRay , float aMaxDistance ) const; + bool Intersects( const CBBOX &aBBox ) const; + SFVEC3F GetDiffuseColor( const HITINFO &aHitInfo ) const; + +private: + SFVEC2F m_center; + float m_radius_squared; + float m_inv_radius; + SFVEC3F m_diffusecolor; +}; + + +#endif // _CCYLINDER_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cdummyblock.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cdummyblock.cpp new file mode 100644 index 0000000000..d1392f3ba0 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cdummyblock.cpp @@ -0,0 +1,90 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cdummyblock.cpp + * @brief + */ + +#include "cdummyblock.h" + + +CDUMMYBLOCK::CDUMMYBLOCK( const CBBOX &aBBox ) : COBJECT( OBJ3D_DUMMYBLOCK ) +{ + m_centroid = aBBox.GetCenter(); + m_bbox.Reset(); + m_bbox.Set( aBBox ); +} + + +bool CDUMMYBLOCK::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +{ + float t; + + if( !m_bbox.Intersect( aRay, &t ) ) + return false; + + if( t < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = t; + //aHitInfo.m_HitPoint = aRay.at( t ); + if( aRay.m_dirIsNeg[2] ) + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f ); + else + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f,-1.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; +} + + +bool CDUMMYBLOCK::IntersectP(const RAY &aRay , float aMaxDistance ) const +{ + float t; + + if( !m_bbox.Intersect( aRay, &t ) ) + return false; + + if( t < aMaxDistance ) + return true; + + return false; +} + + +bool CDUMMYBLOCK::Intersects( const CBBOX &aBBox ) const +{ + return m_bbox.Intersects( aBBox ); +} + + +SFVEC3F CDUMMYBLOCK::GetDiffuseColor( const HITINFO &aHitInfo ) const +{ + (void)aHitInfo; // unused + + return m_diffusecolor; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cdummyblock.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cdummyblock.h new file mode 100644 index 0000000000..e62564175f --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cdummyblock.h @@ -0,0 +1,57 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cdummyblock.h + * @brief + */ + +#ifndef _CDUMMYBLOCK_H_ +#define _CDUMMYBLOCK_H_ + +#include "cobject.h" + +/** + * A dummy block is used to fill the polygons. It will only will be intersepted + * from top or from bottom + */ +class CDUMMYBLOCK : public COBJECT +{ + +public: + explicit CDUMMYBLOCK( const CBBOX &aBBox ); + + void SetColor( SFVEC3F aObjColor ) { m_diffusecolor = aObjColor; } + +// Imported from COBJECT + bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; + bool IntersectP(const RAY &aRay , float aMaxDistance ) const; + bool Intersects( const CBBOX &aBBox ) const; + SFVEC3F GetDiffuseColor( const HITINFO &aHitInfo ) const; +private: + SFVEC3F m_diffusecolor; +}; + + +#endif // _CDUMMYBLOCK_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/clayeritem.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/clayeritem.cpp new file mode 100644 index 0000000000..8562aa1afd --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/clayeritem.cpp @@ -0,0 +1,463 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file clayeritem.cpp + * @brief + */ + +#include "clayeritem.h" +#include "3d_fastmath.h" +#include + + +CLAYERITEM::CLAYERITEM( const COBJECT2D *aObject2D, float aZMin, float aZMax ) : + COBJECT( OBJ3D_LAYERITEM ), + m_object2d(aObject2D) +{ + wxASSERT( aObject2D ); + + CBBOX2D bbox2d = m_object2d->GetBBox(); + bbox2d.ScaleNextUp(); + bbox2d.ScaleNextUp(); + + m_bbox.Reset(); + m_bbox.Set( SFVEC3F( bbox2d.Min().x, bbox2d.Min().y, aZMin ), + SFVEC3F( bbox2d.Max().x, bbox2d.Max().y, aZMax ) ); + m_bbox.ScaleNextUp(); + m_centroid = SFVEC3F( aObject2D->GetCentroid().x, + aObject2D->GetCentroid().y, + (aZMax + aZMin) * 0.5f ); +} + + +bool CLAYERITEM::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +{ + float tBBoxStart; + float tBBoxEnd; + + if( !m_bbox.Intersect( aRay, &tBBoxStart, &tBBoxEnd ) ) + return false; + + if( tBBoxStart >= aHitInfo.m_tHit ) + return false; + + if( fabs(tBBoxStart - tBBoxEnd) < FLT_EPSILON ) + return false; + + bool startedInside = m_bbox.Inside( aRay.m_Origin ); + + if( !startedInside ) + { + float tTop = FLT_MAX; + float tBot = FLT_MAX; + bool hit_top = false; + bool hit_bot = false; + + if( (float)fabs(aRay.m_Dir.z) > FLT_EPSILON ) + { + tBot = (m_bbox.Min().z - aRay.m_Origin.z) * aRay.m_InvDir.z; + tTop = (m_bbox.Max().z - aRay.m_Origin.z) * aRay.m_InvDir.z; + + float tBBoxStartAdjusted = NextFloatUp( tBBoxStart ); + + if( tBot > FLT_EPSILON ) + { + hit_bot = tBot <= tBBoxStartAdjusted; + tBot = NextFloatDown( tBot ); + } + + if( tTop > FLT_EPSILON ) + { + hit_top = tTop <= tBBoxStartAdjusted; + tTop = NextFloatDown( tTop ); + } + } + + tBBoxStart = NextFloatDown( tBBoxStart ); + tBBoxEnd = NextFloatUp( tBBoxEnd ); + + SFVEC2F topHitPoint2d; + SFVEC2F botHitPoint2d; + + if( hit_top ) + topHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tTop, + aRay.m_Origin.y + aRay.m_Dir.y * tTop ); + + if( hit_bot ) + botHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tBot, + aRay.m_Origin.y + aRay.m_Dir.y * tBot ); + + if( hit_top && hit_bot ) + { + if( tBot < tTop ) + { + if( m_object2d->IsPointInside( botHitPoint2d ) ) + { + if( tBot < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = tBot; + //aHitInfo.m_HitPoint = aRay.at( tBot ); + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + } + else + { + if( m_object2d->IsPointInside( topHitPoint2d ) ) + { + if( tTop < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = tTop; + //aHitInfo.m_HitPoint = aRay.at( tTop ); + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + } + } + else + { + if( hit_top ) + { + if( tTop < tBot ) + { + if( m_object2d->IsPointInside( topHitPoint2d ) ) + { + if( tTop < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = tTop; + //aHitInfo.m_HitPoint = aRay.at( tTop ); + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + } + } + else + { + if( hit_bot ) + { + if( tBot < tTop ) + { + if( m_object2d->IsPointInside( botHitPoint2d ) ) + { + if( tBot < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = tBot; + //aHitInfo.m_HitPoint = aRay.at( tBot ); + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + } + } + else + { + // At this point, the ray miss the two planes but it still + // hits the box. It means that the rays are "(almost)paralell" + // to the planes, so must calc the intersection + } + } + } + + + SFVEC3F boxHitPointStart = aRay.at( tBBoxStart ); + SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd ); + + SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y ); + //SFVEC2F boxHitPointStart2D( m_bbox.GetCenter().x, m_bbox.GetCenter().y ); + + SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y ); + + float tOut; + SFVEC2F outNormal; + RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D ); + + if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) ) + { + // The hitT is a hit value for the segment length 'start' - 'end', + // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position + // and calculate the real hitT of the ray. + SFVEC3F hitPoint = boxHitPointStart + + (boxHitPointEnd - boxHitPointStart) * tOut; + const float t = glm::length( hitPoint - aRay.m_Origin ); + + if( t < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = t; + //aHitInfo.m_HitPoint = hitPoint; + aHitInfo.m_HitNormal = SFVEC3F( outNormal.x, outNormal.y, 0.0f ); + aHitInfo.pHitObject = this; + + return true; + } + } + + return false; + } + else + { + // Started inside + + const SFVEC3F boxHitPointStart = aRay.at( tBBoxStart ); + const SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd ); + + const SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y ); + + const SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y ); + + if(!(m_object2d->IsPointInside( boxHitPointStart2D ) && + m_object2d->IsPointInside( boxHitPointEnd2D ) ) ) + return false; + + float tOut; + SFVEC2F outNormal; + RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D ); + + if( (m_object2d->IsPointInside( boxHitPointStart2D ) && + m_object2d->IsPointInside( boxHitPointEnd2D ) ) ) + { + if( tBBoxEnd < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = tBBoxEnd; + + if( aRay.m_Dir.z > 0.0f ) + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f ); + else + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f ); + + aHitInfo.pHitObject = this; + + return true; + } + } + else + { + if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) ) + { + // The hitT is a hit value for the segment length 'start' - 'end', + // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position + // and calculate the real hitT of the ray. + const SFVEC3F hitPoint = boxHitPointStart + + (boxHitPointEnd - boxHitPointStart) * tOut; + const float t = glm::length( hitPoint - aRay.m_Origin ); + + if( t < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = t; + //aHitInfo.m_HitPoint = hitPoint; + aHitInfo.m_HitNormal = SFVEC3F( outNormal.x, outNormal.y, 0.0f ); + aHitInfo.pHitObject = this; + + return true; + } + } + } + } + + return false; +} + + +bool CLAYERITEM::IntersectP(const RAY &aRay , float aMaxDistance ) const +{ + float tBBoxStart; + float tBBoxEnd; + + if( !m_bbox.Intersect( aRay, &tBBoxStart, &tBBoxEnd ) ) + return false; + + if( ( tBBoxStart > aMaxDistance ) || + //( tBBoxEnd < FLT_EPSILON ) + ( fabs(tBBoxStart - tBBoxEnd) < FLT_EPSILON ) ) + return false; + + float tTop = FLT_MAX; + float tBot = FLT_MAX; + bool hit_top = false; + bool hit_bot = false; + + if( (float)fabs(aRay.m_Dir.z) > FLT_EPSILON ) + { + tBot = (m_bbox.Min().z - aRay.m_Origin.z) * aRay.m_InvDir.z; + tTop = (m_bbox.Max().z - aRay.m_Origin.z) * aRay.m_InvDir.z; + + const float tBBoxStartAdjusted = NextFloatUp( tBBoxStart ); + + if( tBot > FLT_EPSILON ) + { + hit_bot = tBot <= tBBoxStartAdjusted; + tBot = NextFloatDown( tBot ); + } + + if( tTop > FLT_EPSILON ) + { + hit_top = tTop <= tBBoxStartAdjusted; + tTop = NextFloatDown( tTop ); + } + } + + tBBoxStart = NextFloatDown( tBBoxStart ); + tBBoxEnd = NextFloatUp( tBBoxEnd ); + + SFVEC2F topHitPoint2d; + SFVEC2F botHitPoint2d; + + if( hit_top ) + topHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tTop, + aRay.m_Origin.y + aRay.m_Dir.y * tTop ); + + if( hit_bot ) + botHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tBot, + aRay.m_Origin.y + aRay.m_Dir.y * tBot ); + + if( hit_top && hit_bot ) + { + if( tBot < tTop ) + { + if( m_object2d->IsPointInside( botHitPoint2d ) ) + { + if( tBot < aMaxDistance ) + return true; + + return false; + } + } + else + { + if( m_object2d->IsPointInside( topHitPoint2d ) ) + { + if( tTop < aMaxDistance ) + return true; + + return false; + } + } + } + else + { + if( hit_top ) + { + if( tTop < tBot ) + { + if( m_object2d->IsPointInside( topHitPoint2d ) ) + { + if( tTop < aMaxDistance ) + return true; + + return false; + } + } + } + else + { + if( hit_bot ) + { + if( tBot < tTop ) + { + if( m_object2d->IsPointInside( botHitPoint2d ) ) + { + if( tBot < aMaxDistance ) + return true; + + return false; + } + } + } + else + { + // At this point, the ray miss the two planes but it still + // hits the box. It means that the rays are "(almost)paralell" + // to the planes, so must calc the intersection + } + } + } + + SFVEC3F boxHitPointStart = aRay.at( tBBoxStart ); + SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd ); + + SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y ); + + SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y ); + + float tOut; + SFVEC2F outNormal; + RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D ); + + if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) ) + { + //if( (tOut > FLT_EPSILON) && (tOut < 1.0f) ) + { + // The hitT is a hit value for the segment length 'start' - 'end', + // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position + // and calculate the real hitT of the ray. + const SFVEC3F hitPoint = boxHitPointStart + + (boxHitPointEnd - boxHitPointStart) * tOut; + const float t = glm::length( hitPoint - aRay.m_Origin ); + + if( (t < 1.0f) && ( t > FLT_EPSILON ) ) + return true; + } + } + + return false; +} + + +bool CLAYERITEM::Intersects( const CBBOX &aBBox ) const +{ + if( !m_bbox.Intersects( aBBox ) ) + return false; + + const CBBOX2D bbox2D( SFVEC2F( aBBox.Min().x, aBBox.Min().y), + SFVEC2F( aBBox.Max().x, aBBox.Max().y) ); + + return m_object2d->Intersects( bbox2D ); +} + + +SFVEC3F CLAYERITEM::GetDiffuseColor( const HITINFO &aHitInfo ) const +{ + (void)aHitInfo; // unused + + return m_diffusecolor; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/clayeritem.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/clayeritem.h new file mode 100644 index 0000000000..3c695087e5 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/clayeritem.h @@ -0,0 +1,58 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file clayeritem.h + * @brief + */ + +#ifndef _CLAYERITEM_H_ +#define _CLAYERITEM_H_ + +#include "cobject.h" +#include "../shapes2D/cobject2d.h" + + +class CLAYERITEM : public COBJECT +{ +protected: + const COBJECT2D *m_object2d; + +public: + + CLAYERITEM( const COBJECT2D *aObject2D, float aZMin, float aZMax ); + + void SetColor( SFVEC3F aObjColor ) { m_diffusecolor = aObjColor; } + + // Imported from COBJECT + bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; + bool IntersectP(const RAY &aRay , float aMaxDistance ) const; + bool Intersects( const CBBOX &aBBox ) const; + SFVEC3F GetDiffuseColor( const HITINFO &aHitInfo ) const; + +private: + SFVEC3F m_diffusecolor; +}; + +#endif // _CLAYERITEM_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cobject.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cobject.cpp new file mode 100644 index 0000000000..d047060e53 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cobject.cpp @@ -0,0 +1,65 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cobject.cpp + * @brief + */ + +#include "cobject.h" +#include + + +COBJECT3D_STATS *COBJECT3D_STATS::s_instance = 0; + +static const CBLINN_PHONG_MATERIAL s_defaultMaterial = CBLINN_PHONG_MATERIAL(); + +COBJECT::COBJECT( OBJECT3D_TYPE aObjType ) +{ + m_obj_type = aObjType; + COBJECT3D_STATS::Instance().AddOne( aObjType ); + m_material = &s_defaultMaterial; +} + + +static const char *OBJECT3D_STR[OBJ3D_MAX] = +{ + "OBJ3D_CYLINDER", + "OBJ3D_DUMMYBLOCK", + "OBJ3D_LAYERITEM", + "OBJ3D_XYPLANE", + "OBJ3D_ROUNDSEG", + "OBJ3D_TRIANGLE" +}; + + +void COBJECT3D_STATS::PrintStats() +{ + printf( "OBJ3D Statistics:\n" ); + + for( unsigned int i = 0; i < OBJ3D_MAX; ++i ) + { + printf( " %20s %u\n", OBJECT3D_STR[i], m_counter[i] ); + } +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cobject.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cobject.h index aa0577b332..3a7b24c948 100644 --- a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cobject.h +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cobject.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,7 +30,6 @@ #ifndef _COBJECT_H_ #define _COBJECT_H_ -#include "plugins/3dapi/xv3d_types.h" #include "cbbox.h" #include "../hitinfo.h" #include "../cmaterial.h" @@ -47,7 +46,7 @@ enum OBJECT3D_TYPE OBJ3D_MAX }; -class GLM_ALIGN(CLASS_ALIGNMENT) COBJECT +class COBJECT { protected: CBBOX m_bbox; @@ -57,7 +56,7 @@ protected: public: - COBJECT( OBJECT3D_TYPE aObjType ); + explicit COBJECT( OBJECT3D_TYPE aObjType ); void SetMaterial( const CMATERIAL *aMaterial ) { m_material = aMaterial; } const CMATERIAL *GetMaterial() const { return m_material; } @@ -103,7 +102,9 @@ class COBJECT3D_STATS { public: - void ResetStats() { memset( m_counter, 0, sizeof(unsigned int) * OBJ3D_MAX ); } + void ResetStats() { memset( m_counter, + 0, + sizeof( unsigned int ) * OBJ3D_MAX ); } unsigned int GetCountOf( OBJECT3D_TYPE aObjType ) const { return m_counter[aObjType]; } diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cplane.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cplane.cpp new file mode 100644 index 0000000000..730ec7f308 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cplane.cpp @@ -0,0 +1,130 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cplane.cpp + * @brief + */ + +#include "cplane.h" + + +CXYPLANE::CXYPLANE( const CBBOX &aBBox ) : COBJECT( OBJ3D_XYPLANE ) +{ + m_centerPoint = aBBox.GetCenter(); + m_centroid = m_centerPoint; + + m_bbox.Reset(); + m_bbox.Set( aBBox ); + m_xsize = aBBox.GetExtent().x; + m_ysize = aBBox.GetExtent().y; + m_xsize_inv2 = 1.0f / (2.0f * m_xsize); + m_ysize_inv2 = 1.0f / (2.0f * m_ysize); +} + + +CXYPLANE::CXYPLANE( SFVEC3F aCenterPoint, + float aXSize, + float aYSize ) : COBJECT( OBJ3D_XYPLANE ) +{ + m_centerPoint = aCenterPoint; + m_xsize = aXSize; + m_ysize = aYSize; + m_xsize_inv2 = 1.0f / (2.0f * aXSize); + m_ysize_inv2 = 1.0f / (2.0f * aYSize); + m_bbox.Set( SFVEC3F( aCenterPoint.x - aXSize / 2.0f, + aCenterPoint.y - aYSize / 2.0f, + aCenterPoint.z ), + SFVEC3F( aCenterPoint.x + aXSize / 2.0f, + aCenterPoint.y + aYSize / 2.0f, + aCenterPoint.z ) ); + m_centroid = aCenterPoint; +} + + +bool CXYPLANE::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +{ + const float t = (m_centerPoint.z - aRay.m_Origin.z) * aRay.m_InvDir.z; + + if( ( t < FLT_EPSILON ) || + ( t >= aHitInfo.m_tHit ) ) + return false; + + const float vSU = t * aRay.m_Dir.x + aRay.m_Origin.x - m_centerPoint.x; + + if( (vSU < -m_xsize) || (vSU > m_xsize) ) + return false; + + const float vSV = t * aRay.m_Dir.y + aRay.m_Origin.y - m_centerPoint.y; + + if( (vSV < -m_ysize) || (vSV > m_ysize) ) + return false; + + aHitInfo.m_tHit = t; + //aHitInfo.m_HitPoint = aRay.at( t ); + aHitInfo.pHitObject = this; + + if( aRay.m_dirIsNeg[2] ) + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f ); + else + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f,-1.0f ); + + return true; +} + + +bool CXYPLANE::IntersectP(const RAY &aRay , float aMaxDistance ) const +{ + const float t = (m_centerPoint.z - aRay.m_Origin.z) * aRay.m_InvDir.z; + + if( ( t < FLT_EPSILON ) || + ( t >= aMaxDistance ) ) + return false; + + const float vSU = t * aRay.m_Dir.x + aRay.m_Origin.x - m_centerPoint.x; + + if( (vSU < -m_xsize) || (vSU > m_xsize) ) + return false; + + const float vSV = t * aRay.m_Dir.y + aRay.m_Origin.y - m_centerPoint.y; + + if( (vSV < -m_ysize) || (vSV > m_ysize) ) + return false; + + return true; +} + + +bool CXYPLANE::Intersects( const CBBOX &aBBox ) const +{ + return m_bbox.Intersects( aBBox ); +} + + +SFVEC3F CXYPLANE::GetDiffuseColor( const HITINFO &aHitInfo ) const +{ + (void)aHitInfo; // unused + + return m_diffusecolor; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cplane.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cplane.h new file mode 100644 index 0000000000..299bb9714d --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/cplane.h @@ -0,0 +1,70 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file cplane.h + * @brief + */ + +#ifndef _CPLANE_H_ +#define _CPLANE_H_ + +#include "cobject.h" + +/** + * A plane that is parallel to XY plane + */ +class CXYPLANE : public COBJECT +{ + +public: + explicit CXYPLANE( const CBBOX &aBBox ); + + /** + * Constructor CXYPLANE + * @param aCenterPoint = position of the center of the plane + * @param aXSize = size by X axis + * @param aYSize = size by Y axis + */ + CXYPLANE( SFVEC3F aCenterPoint, float aXSize, float aYSize ); + + void SetColor( SFVEC3F aObjColor ) { m_diffusecolor = aObjColor; } + + // Imported from COBJECT + bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; + bool IntersectP(const RAY &aRay , float aMaxDistance ) const; + bool Intersects( const CBBOX &aBBox ) const; + SFVEC3F GetDiffuseColor( const HITINFO &aHitInfo ) const; + +private: + SFVEC3F m_centerPoint; + float m_xsize; + float m_ysize; + float m_xsize_inv2; + float m_ysize_inv2; + SFVEC3F m_diffusecolor; +}; + + +#endif // _CPLANE_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/croundseg.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/croundseg.cpp new file mode 100644 index 0000000000..91b305c894 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/croundseg.cpp @@ -0,0 +1,433 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file croundseg.cpp + * @brief + */ + +#include "croundseg.h" + +CROUNDSEG::CROUNDSEG( const CROUNDSEGMENT2D &aSeg2D, + float aZmin, + float aZmax ) : COBJECT( OBJ3D_ROUNDSEG ), + m_segment( aSeg2D.m_segment ) +{ + m_radius = aSeg2D.GetRadius(); + m_radius_squared = m_radius * m_radius; + m_inv_radius = 1.0f / m_radius; + + m_plane_dir_left = SFVEC3F( -m_segment.m_Dir.y, m_segment.m_Dir.x, 0.0f ); + m_plane_dir_right = SFVEC3F( m_segment.m_Dir.y, -m_segment.m_Dir.x, 0.0f ); + + m_bbox.Reset(); + + m_bbox.Set( SFVEC3F( m_segment.m_Start.x, m_segment.m_Start.y, aZmin), + SFVEC3F( m_segment.m_End.x, m_segment.m_End.y, aZmax) ); + + m_bbox.Set( m_bbox.Min() - SFVEC3F( m_radius, m_radius, 0.0f ), + m_bbox.Max() + SFVEC3F( m_radius, m_radius, 0.0f ) ); + + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); + + m_center_left = m_centroid + m_plane_dir_left * m_radius; + m_center_right = m_centroid + m_plane_dir_right * m_radius; + + m_seglen_over_two_squared = (m_segment.m_Length / 2.0f) * + (m_segment.m_Length / 2.0f); +} + + +bool CROUNDSEG::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +{ + // Top / Botton plane + // ///////////////////////////////////////////////////////////////////////// + float zPlanePos = aRay.m_dirIsNeg[2]? m_bbox.Max().z : m_bbox.Min().z; + + float tPlane = ( zPlanePos - aRay.m_Origin.z) * aRay.m_InvDir.z; + + if( ( tPlane >= aHitInfo.m_tHit ) || ( tPlane < FLT_EPSILON ) ) + return false; // Early exit + + SFVEC2F planeHitPoint2d( aRay.m_Origin.x + aRay.m_Dir.x * tPlane, + aRay.m_Origin.y + aRay.m_Dir.y * tPlane ); + + float dSquared = m_segment.DistanceToPointSquared( planeHitPoint2d ); + + if( dSquared <= m_radius_squared ) + { + if( tPlane < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = tPlane; + //aHitInfo.m_HitPoint = SFVEC3F( planeHitPoint2d.x, + // planeHitPoint2d.y, + // aRay.m_Origin.z + aRay.m_Dir.z * tPlane ); + aHitInfo.m_HitNormal = SFVEC3F( 0.0f, + 0.0f, + aRay.m_dirIsNeg[2]? 1.0f: -1.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + + // Test LEFT / RIGHT plane + // ///////////////////////////////////////////////////////////////////////// + float normal_dot_ray = glm::dot( m_plane_dir_right, aRay.m_Dir ); + + if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane + { + const float n_dot_ray_origin = glm::dot( m_plane_dir_right, + m_center_right - aRay.m_Origin ); + const float t = n_dot_ray_origin / normal_dot_ray; + + if( t > 0.0f ) + { + const SFVEC3F hitP = aRay.at( t ); + + const SFVEC3F v = hitP - m_center_right; + const float len = glm::dot( v, v ); + + if( (len <= m_seglen_over_two_squared) && + (hitP.z >= m_bbox.Min().z) && + (hitP.z <= m_bbox.Max().z) ) + { + if( t < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = t; + //aHitInfo.m_HitPoint = hitP; + aHitInfo.m_HitNormal = SFVEC3F( m_plane_dir_right.x, + m_plane_dir_right.y, + 0.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + } + } + else + { + normal_dot_ray = glm::dot( m_plane_dir_left, aRay.m_Dir ); + + if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane + { + const float n_dot_ray_origin = glm::dot( m_plane_dir_left, + m_center_left - aRay.m_Origin ); + const float t = n_dot_ray_origin / normal_dot_ray; + + if( t > 0.0f ) + { + const SFVEC3F hitP = aRay.at( t ); + + const SFVEC3F v = hitP - m_center_left; + const float len = glm::dot( v, v ); + + if( (len <= m_seglen_over_two_squared) && + (hitP.z >= m_bbox.Min().z) && + (hitP.z <= m_bbox.Max().z) ) + { + if( t < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = t; + //aHitInfo.m_HitPoint = hitP; + aHitInfo.m_HitNormal = SFVEC3F( m_plane_dir_left.x, + m_plane_dir_left.y, + 0.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + } + } + } + + // Based on: + // http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp + + // Ray-sphere intersection: geometric + // ///////////////////////////////////////////////////////////////////////// + + const double OCx_Start = aRay.m_Origin.x - m_segment.m_Start.x; + const double OCy_Start = aRay.m_Origin.y - m_segment.m_Start.y; + + const double p_dot_p_Start = OCx_Start * OCx_Start + OCy_Start * OCy_Start; + + const double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x + + (double)aRay.m_Dir.y * (double)aRay.m_Dir.y; + + const double b_Start = (double)aRay.m_Dir.x * (double)OCx_Start + + (double)aRay.m_Dir.y * (double)OCy_Start; + + const double c_Start = p_dot_p_Start - m_radius_squared; + + const float delta_Start = (float)(b_Start * b_Start - a * c_Start); + + if( delta_Start > FLT_EPSILON ) + { + const float sdelta = sqrtf( delta_Start ); + const float t = (-b_Start - sdelta) / a; + const float z = aRay.m_Origin.z + t * aRay.m_Dir.z; + + if( (z >= m_bbox.Min().z) && + (z <= m_bbox.Max().z) ) + { + if( t < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = t; + SFVEC2F hitPoint2D = aRay.at2D( t ); + //aHitInfo.m_HitPoint = aRay.at( t ); + aHitInfo.m_HitNormal = SFVEC3F( + (hitPoint2D.x - m_segment.m_Start.x) * m_inv_radius, + (hitPoint2D.y - m_segment.m_Start.y) * m_inv_radius, + 0.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + } + + const double OCx_End = aRay.m_Origin.x - m_segment.m_End.x; + const double OCy_End = aRay.m_Origin.y - m_segment.m_End.y; + + const double p_dot_p_End = OCx_End * OCx_End + OCy_End * OCy_End; + + const double b_End = (double)aRay.m_Dir.x * (double)OCx_End + + (double)aRay.m_Dir.y * (double)OCy_End; + + const double c_End = p_dot_p_End - m_radius_squared; + + const float delta_End = (float)(b_End * b_End - a * c_End); + + if( delta_End > FLT_EPSILON ) + { + const float sdelta = sqrtf( delta_End ); + const float t = (-b_End - sdelta) / a; + const float z = aRay.m_Origin.z + t * aRay.m_Dir.z; + + if( (z >= m_bbox.Min().z) && + (z <= m_bbox.Max().z) ) + { + if( t < aHitInfo.m_tHit ) + { + aHitInfo.m_tHit = t; + //aHitInfo.m_HitPoint = aRay.at( t ); + const SFVEC2F hitPoint2D = aRay.at2D( t ); + + aHitInfo.m_HitNormal = SFVEC3F( + (hitPoint2D.x - m_segment.m_End.x) * m_inv_radius, + (hitPoint2D.y - m_segment.m_End.y) * m_inv_radius, + 0.0f ); + aHitInfo.pHitObject = this; + + return true; + } + + return false; + } + } + + return false; +} + + +bool CROUNDSEG::IntersectP( const RAY &aRay, float aMaxDistance ) const +{ + // Top / Botton plane + // ///////////////////////////////////////////////////////////////////////// + const float zPlanePos = aRay.m_dirIsNeg[2]? m_bbox.Max().z : m_bbox.Min().z; + + const float tPlane = ( zPlanePos - aRay.m_Origin.z) * aRay.m_InvDir.z; + + if( ( tPlane >= aMaxDistance) || ( tPlane < FLT_EPSILON ) ) + return false; // Early exit + + const SFVEC2F planeHitPoint2d( aRay.m_Origin.x + aRay.m_Dir.x * tPlane, + aRay.m_Origin.y + aRay.m_Dir.y * tPlane ); + + const float dSquared = m_segment.DistanceToPointSquared( planeHitPoint2d ); + + if( dSquared <= m_radius_squared ) + { + if( tPlane < aMaxDistance ) + return true; + + return false; + } + + // Since the IntersectP is used for shadows, we are simplifying the test + // intersection and only consider the top/bottom plane of the segment + return false; +#if 0 + // Test LEFT / RIGHT plane + // ///////////////////////////////////////////////////////////////////////// + float normal_dot_ray = glm::dot( m_plane_dir_right, aRay.m_Dir ); + + if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane + { + float n_dot_ray_origin = glm::dot( m_plane_dir_right, + m_center_right - aRay.m_Origin ); + float t = n_dot_ray_origin / normal_dot_ray; + + if( t > 0.0f ) + { + SFVEC3F hitP = aRay.at( t ); + + SFVEC3F v = hitP - m_center_right; + float len = glm::dot( v, v ); + + if( (len <= m_seglen_over_two_squared) && + (hitP.z >= m_bbox.Min().z) && (hitP.z <= m_bbox.Max().z) ) + { + if( t < aMaxDistance ) + return true; + + return false; + } + } + } + else + { + normal_dot_ray = glm::dot( m_plane_dir_left, aRay.m_Dir ); + + if( normal_dot_ray < 0.0f ) // If the dot is neg, the it hits the plane + { + const float n_dot_ray_origin = glm::dot( m_plane_dir_left, + m_center_left - aRay.m_Origin ); + const float t = n_dot_ray_origin / normal_dot_ray; + + if( t > 0.0f ) + { + SFVEC3F hitP = aRay.at( t ); + + SFVEC3F v = hitP - m_center_left; + float len = glm::dot( v, v ); + + if( (len <= m_seglen_over_two_squared) && + (hitP.z >= m_bbox.Min().z) && (hitP.z <= m_bbox.Max().z) ) + { + if( t < aMaxDistance ) + return true; + + return false; + } + } + } + } + + // Based on: + // http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp + + // Ray-sphere intersection: geometric + // ///////////////////////////////////////////////////////////////////////// + + double OCx_Start = aRay.m_Origin.x - m_segment.m_Start.x; + double OCy_Start = aRay.m_Origin.y - m_segment.m_Start.y; + + double p_dot_p_Start = OCx_Start * OCx_Start + OCy_Start * OCy_Start; + + double a = (double)aRay.m_Dir.x * (double)aRay.m_Dir.x + + (double)aRay.m_Dir.y * (double)aRay.m_Dir.y; + + double b_Start = (double)aRay.m_Dir.x * (double)OCx_Start + + (double)aRay.m_Dir.y * (double)OCy_Start; + + double c_Start = p_dot_p_Start - m_radius_squared; + + float delta_Start = (float)(b_Start * b_Start - a * c_Start); + + if( delta_Start > FLT_EPSILON ) + { + float sdelta = sqrtf( delta_Start ); + float t = (-b_Start - sdelta) / a; + float z = aRay.m_Origin.z + t * aRay.m_Dir.z; + + if( (z >= m_bbox.Min().z) && + (z <= m_bbox.Max().z) ) + { + if( t < aMaxDistance ) + return true; + + return false; + } + } + + double OCx_End = aRay.m_Origin.x - m_segment.m_End.x; + double OCy_End = aRay.m_Origin.y - m_segment.m_End.y; + + double p_dot_p_End = OCx_End * OCx_End + OCy_End * OCy_End; + + + double b_End = (double)aRay.m_Dir.x * (double)OCx_End + + (double)aRay.m_Dir.y * (double)OCy_End; + + double c_End = p_dot_p_End - m_radius_squared; + + float delta_End = (float)(b_End * b_End - a * c_End); + + if( delta_End > FLT_EPSILON ) + { + float sdelta = sqrtf( delta_End ); + float t = (-b_End - sdelta) / a; + float z = aRay.m_Origin.z + t * aRay.m_Dir.z; + + if( (z >= m_bbox.Min().z) && + (z <= m_bbox.Max().z) ) + { + if( t < aMaxDistance ) + return true; + + return false; + } + } + + return false; +#endif +} + + +bool CROUNDSEG::Intersects( const CBBOX &aBBox ) const +{ + //!TODO: improove + return m_bbox.Intersects( aBBox ); +} + + +SFVEC3F CROUNDSEG::GetDiffuseColor( const HITINFO &aHitInfo ) const +{ + (void)aHitInfo; // unused + + return m_diffusecolor; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/croundseg.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/croundseg.h new file mode 100644 index 0000000000..4d73baf2c9 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/croundseg.h @@ -0,0 +1,112 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file croundseg.h + * @brief + */ + +#ifndef _CROUNDSEG_H_ +#define _CROUNDSEG_H_ + +#include "cobject.h" +#include "../shapes2D/croundsegment2d.h" + +/** + * + */ +class CROUNDSEG : public COBJECT +{ + +public: + /** + * Constructor CROUNDSEG + */ + CROUNDSEG( const CROUNDSEGMENT2D &aSeg2D, float aZmin, float aZmax ); + + void SetColor( SFVEC3F aObjColor ) { m_diffusecolor = aObjColor; } + + // Imported from COBJECT + bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; + bool IntersectP( const RAY &aRay, float aMaxDistance ) const; + bool Intersects( const CBBOX &aBBox ) const; + SFVEC3F GetDiffuseColor( const HITINFO &aHitInfo ) const; + +private: + RAYSEG2D m_segment; + + SFVEC3F m_center_left; + SFVEC3F m_center_right; + SFVEC3F m_plane_dir_left; + SFVEC3F m_plane_dir_right; + + float m_radius; + float m_radius_squared; + float m_inv_radius; + float m_seglen_over_two_squared; + + SFVEC3F m_diffusecolor; +}; + +#if 0 +/** + * This is a object similar to a round segment but with a ring + * It is used for Oblong holes + */ +class COBLONGRING : public COBJECT +{ +public: + /** + * Constructor CROUNDSEG + */ + CROUNDSEG( const SFVEC2F &aStart, + const SFVEC2F &aEnd, + float aInnerRadius, + float aOutterRadius, + float aZmin, + float aZmax ); + + // Imported from COBJECT + bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; + bool Intersects( const CBBOX &aBBox ) const; + +private: + RAYSEG2D m_segment; + + SFVEC3F m_center_left; + SFVEC3F m_center_right; + SFVEC3F m_plane_dir_left; + SFVEC3F m_plane_dir_right; + + float m_inner_radius; + float m_inner_radius_squared; + float m_inner_inv_radius; + float m_outter_radius; + float m_outter_radius_squared; + float m_outter_inv_radius; + float m_width; + float m_seglen_over_two_squared; +}; +#endif +#endif // _CROUNDSEG_H_ diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ctriangle.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ctriangle.cpp new file mode 100644 index 0000000000..630d59c82c --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ctriangle.cpp @@ -0,0 +1,342 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +/** + * @file ctriangle.cpp + * @brief Implements a triangle ray intersection based on article: + * http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_7_Kd-Trees_and_More_Speed.shtml + * by Jacco Bikker, that implement optimizations based on Ingo Wald's thesis. + */ + + +#include "ctriangle.h" + + +void CTRIANGLE::pre_calc_const() +{ + const SFVEC3F &A = m_vertex[0]; + const SFVEC3F &B = m_vertex[1]; + const SFVEC3F &C = m_vertex[2]; + const SFVEC3F c = B - A; + const SFVEC3F b = C - A; + + m_bbox.Reset(); + m_bbox.Set( A ); + m_bbox.Union( B ); + m_bbox.Union( C ); + m_bbox.ScaleNextUp(); + m_centroid = m_bbox.GetCenter(); + + m_n = glm::cross( b, c ); + + if( glm::abs( m_n.x ) > glm::abs( m_n.y ) ) + { + if( glm::abs( m_n.x ) > glm::abs( m_n.z ) ) + m_k = 0; + else + m_k = 2; + } + else + { + if( glm::abs( m_n.y ) > glm::abs( m_n.z ) ) + m_k = 1; + else + m_k = 2; + } + + int u = (m_k + 1) % 3; + int v = (m_k + 2) % 3; + + + // precomp + float krec = 1.0f / m_n[m_k]; + + m_nu = m_n[u] * krec; + m_nv = m_n[v] * krec; + m_nd = glm::dot( m_n, A ) * krec; + + + // first line equation + float reci = 1.0f / (b[u] * c[v] - b[v] * c[u]); + + m_bnu = b[u] * reci; + m_bnv = -b[v] * reci; + + + // second line equation + m_cnu = c[v] * reci; + m_cnv = -c[u] * reci; + + // finalize normal + m_n = glm::normalize( m_n ); + + m_normal[0] = m_n; + m_normal[1] = m_n; + m_normal[2] = m_n; +} + + +CTRIANGLE::CTRIANGLE( const SFVEC3F &aV1, + const SFVEC3F &aV2, + const SFVEC3F &aV3 ) : COBJECT( OBJ3D_TRIANGLE ) +{ + m_vertex[0] = aV1; + m_vertex[1] = aV2; + m_vertex[2] = aV3; + + m_vertexColorRGBA[0] = 0xFFFFFFFF; + m_vertexColorRGBA[1] = 0xFFFFFFFF; + m_vertexColorRGBA[2] = 0xFFFFFFFF; + + pre_calc_const(); +} + + +CTRIANGLE::CTRIANGLE( const SFVEC3F &aV1, + const SFVEC3F &aV2, + const SFVEC3F &aV3, + const SFVEC3F &aFaceNormal ) : COBJECT( OBJ3D_TRIANGLE ) +{ + m_vertex[0] = aV1; + m_vertex[1] = aV2; + m_vertex[2] = aV3; + + m_vertexColorRGBA[0] = 0xFFFFFFFF; + m_vertexColorRGBA[1] = 0xFFFFFFFF; + m_vertexColorRGBA[2] = 0xFFFFFFFF; + + pre_calc_const(); + + m_normal[0] = aFaceNormal; + m_normal[1] = aFaceNormal; + m_normal[2] = aFaceNormal; +} + + +CTRIANGLE::CTRIANGLE( const SFVEC3F &aV1, + const SFVEC3F &aV2, + const SFVEC3F &aV3, + const SFVEC3F &aN1, + const SFVEC3F &aN2, + const SFVEC3F &aN3 ) : COBJECT( OBJ3D_TRIANGLE ) +{ + m_vertex[0] = aV1; + m_vertex[1] = aV2; + m_vertex[2] = aV3; + + m_vertexColorRGBA[0] = 0xFFFFFFFF; + m_vertexColorRGBA[1] = 0xFFFFFFFF; + m_vertexColorRGBA[2] = 0xFFFFFFFF; + + pre_calc_const(); + + m_normal[0] = aN1; + m_normal[1] = aN2; + m_normal[2] = aN3; +} + + +void CTRIANGLE::SetColor( const SFVEC3F &aColor ) +{ + m_vertexColorRGBA[0] = ((unsigned int)(aColor.r * 255) << 24) | + ((unsigned int)(aColor.g * 255) << 16) | + ((unsigned int)(aColor.b * 255) << 8) | 0xFF; + m_vertexColorRGBA[1] = m_vertexColorRGBA[0]; + m_vertexColorRGBA[2] = m_vertexColorRGBA[0]; +} + + +void CTRIANGLE::SetColor( const SFVEC3F &aVC0, + const SFVEC3F &aVC1, + const SFVEC3F &aVC2 ) +{ + m_vertexColorRGBA[0] = ((unsigned int)(aVC0.r * 255) << 24) | + ((unsigned int)(aVC0.g * 255) << 16) | + ((unsigned int)(aVC0.b * 255) << 8) | 0xFF; + m_vertexColorRGBA[1] = ((unsigned int)(aVC1.r * 255) << 24) | + ((unsigned int)(aVC1.g * 255) << 16) | + ((unsigned int)(aVC1.b * 255) << 8) | 0xFF; + m_vertexColorRGBA[2] = ((unsigned int)(aVC2.r * 255) << 24) | + ((unsigned int)(aVC2.g * 255) << 16) | + ((unsigned int)(aVC2.b * 255) << 8) | 0xFF; +} + + +void CTRIANGLE::SetColor( unsigned int aFaceColorRGBA ) +{ + m_vertexColorRGBA[0] = aFaceColorRGBA; + m_vertexColorRGBA[1] = aFaceColorRGBA; + m_vertexColorRGBA[2] = aFaceColorRGBA; +} + + +void CTRIANGLE::SetColor( unsigned int aVertex1ColorRGBA, + unsigned int aVertex2ColorRGBA, + unsigned int aVertex3ColorRGBA ) +{ + m_vertexColorRGBA[0] = aVertex1ColorRGBA; + m_vertexColorRGBA[1] = aVertex2ColorRGBA; + m_vertexColorRGBA[2] = aVertex3ColorRGBA; +} + + +void CTRIANGLE::SetUV( const SFVEC2F &aUV1, + const SFVEC2F &aUV2, + const SFVEC2F &aUV3 ) +{ + m_uv[0] = aUV1; + m_uv[1] = aUV2; + m_uv[2] = aUV3; +} + + +static const unsigned int s_modulo[] = { 0, 1, 2, 0, 1 }; + +bool CTRIANGLE::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +{ + //!TODO: precalc this, improove it +#define ku s_modulo[m_k + 1] +#define kv s_modulo[m_k + 2] + + const SFVEC3F &O = aRay.m_Origin; + const SFVEC3F &D = aRay.m_Dir; + const SFVEC3F &A = m_vertex[0]; + + const float lnd = 1.0f / (D[m_k] + m_nu * D[ku] + m_nv * D[kv]); + const float t = (m_nd - O[m_k] - m_nu * O[ku] - m_nv * O[kv]) * lnd; + + if( !( (aHitInfo.m_tHit > t) && (t > 0.0f) ) ) + return false; + + const float hu = O[ku] + t * D[ku] - A[ku]; + const float hv = O[kv] + t * D[kv] - A[kv]; + const float beta = hv * m_bnu + hu * m_bnv; + + if( beta < 0.0f ) + return false; + + const float gamma = hu * m_cnu + hv * m_cnv; + + if( gamma < 0 ) + return false; + + const float v = gamma; + const float u = beta; + + if( (u + v) > 1.0f ) + return false; + + if( glm::dot( D, m_n ) > 0.0f ) + return false; + + aHitInfo.m_tHit = t; + //aHitInfo.m_UV + + // interpolate vertex normals with UVW using Gouraud's shading + aHitInfo.m_HitNormal = glm::normalize( (1.0f - u - v) * m_normal[0] + + u * m_normal[1] + + v * m_normal[2] ); + + aHitInfo.pHitObject = this; + + return true; +#undef ku +#undef kv +} + + +bool CTRIANGLE::IntersectP( const RAY &aRay, + float aMaxDistance ) const +{ + //!TODO: precalc this +#define ku s_modulo[m_k + 1] +#define kv s_modulo[m_k + 2] + + const SFVEC3F O = aRay.m_Origin; + const SFVEC3F D = aRay.m_Dir; + const SFVEC3F A = m_vertex[0]; + + const float lnd = 1.0f / (D[m_k] + m_nu * D[ku] + m_nv * D[kv]); + const float t = (m_nd - O[m_k] - m_nu * O[ku] - m_nv * O[kv]) * lnd; + + if( !( (aMaxDistance > t) && (t > 0.0f) ) ) + return false; + + const float hu = O[ku] + t * D[ku] - A[ku]; + const float hv = O[kv] + t * D[kv] - A[kv]; + const float beta = hv * m_bnu + hu * m_bnv; + + if( beta < 0.0f ) + return false; + + const float gamma = hu * m_cnu + hv * m_cnv; + + if( gamma < 0.0f ) + return false; + + const float v = gamma; + const float u = beta; + + if( (u + v) > 1.0f ) + return false; + + if( glm::dot( D, m_n ) > 0.0f ) + return false; + + return true; +#undef ku +#undef kv +} + + +bool CTRIANGLE::Intersects( const CBBOX &aBBox ) const +{ + //!TODO: improove + return m_bbox.Intersects( aBBox ); +} + + +SFVEC3F CTRIANGLE::GetDiffuseColor( const HITINFO &aHitInfo ) const +{ + const unsigned int rgbC1 = m_vertexColorRGBA[0]; + const unsigned int rgbC2 = m_vertexColorRGBA[1]; + const unsigned int rgbC3 = m_vertexColorRGBA[2]; + + const SFVEC3F c1 = SFVEC3F( (float)((rgbC1 >> 24) & 0xFF) / 255.0f, + (float)((rgbC1 >> 16) & 0xFF) / 255.0f, + (float)((rgbC1 >> 8) & 0xFF) / 255.0f ); + const SFVEC3F c2 = SFVEC3F( (float)((rgbC2 >> 24) & 0xFF) / 255.0f, + (float)((rgbC2 >> 16) & 0xFF) / 255.0f, + (float)((rgbC2 >> 8) & 0xFF) / 255.0f ); + const SFVEC3F c3 = SFVEC3F( (float)((rgbC3 >> 24) & 0xFF) / 255.0f, + (float)((rgbC3 >> 16) & 0xFF) / 255.0f, + (float)((rgbC3 >> 8) & 0xFF) / 255.0f ); + + const float u = aHitInfo.m_UV.x; + const float v = aHitInfo.m_UV.y; + const float w = 1.0f - u - v; + + return w * c1 + u * c2 + v * c3; +} diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ctriangle.h b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ctriangle.h new file mode 100644 index 0000000000..f6860f40a6 --- /dev/null +++ b/3d-viewer/3d_rendering/3d_render_raytracing/shapes3D/ctriangle.h @@ -0,0 +1,88 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +/** + * @file ctriangle.h + * @brief Implements a triangle ray intersection based on article + * http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_7_Kd-Trees_and_More_Speed.shtml + * by Jacco Bikker, that implement optimizations based on Ingo Wald's thesis. + */ + + +#ifndef _CTRIANGLE_H_ +#define _CTRIANGLE_H_ + +#include "cobject.h" + +/** + * A triangle object + */ +class CTRIANGLE : public COBJECT +{ + +public: + CTRIANGLE( const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3 ); + + CTRIANGLE( const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3, + const SFVEC3F &aFaceNormal ); + + CTRIANGLE( const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3, + const SFVEC3F &aN1, const SFVEC3F &aN2, const SFVEC3F &aN3 ); + + void SetColor( const SFVEC3F &aColor ); + + void SetColor( const SFVEC3F &aVC0, const SFVEC3F &aVC1, const SFVEC3F &aVC2 ); + + void SetColor( unsigned int aFaceColorRGBA ); + + void SetColor( unsigned int aVertex1ColorRGBA, + unsigned int aVertex2ColorRGBA, + unsigned int aVertex3ColorRGBA ); + + void SetUV( const SFVEC2F &aUV1, const SFVEC2F &aUV2, const SFVEC2F &aUV3 ); + + // Imported from COBJECT + bool Intersect( const RAY &aRay, HITINFO &aHitInfo ) const; + bool IntersectP(const RAY &aRay , float aMaxDistance ) const; + bool Intersects( const CBBOX &aBBox ) const; + SFVEC3F GetDiffuseColor( const HITINFO &aHitInfo ) const; + +private: + void pre_calc_const(); + +private: + SFVEC3F m_normal[3]; // 36 + SFVEC3F m_vertex[3]; // 36 + SFVEC3F m_n; // 12 + SFVEC2F m_uv[3]; // 24 + unsigned int m_vertexColorRGBA[3]; // 12 + float m_nu, m_nv, m_nd; // 12 + unsigned int m_k; // 4 + float m_bnu, m_bnv; // 8 + float m_cnu, m_cnv; // 8 + // 152 bytes (max 160 == 5 * 32) +}; + +#endif // _CTRIANGLE_H_ diff --git a/3d-viewer/3d_rendering/buffers_debug.cpp b/3d-viewer/3d_rendering/buffers_debug.cpp new file mode 100644 index 0000000000..b5132cef52 --- /dev/null +++ b/3d-viewer/3d_rendering/buffers_debug.cpp @@ -0,0 +1,147 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** + * @file buffers_debug.cpp + * @brief + */ + +#include "buffers_debug.h" +#include // Used for save an image to disk + +/** + * @brief dbg_save_rgb_buffer + * @param aFileName + * @param aRGBpixelBuffer: from wxWidget documentation + * "The data given must have the size (width*height*3). + * The data must have been allocated with malloc(), NOT with operator new." + * @param aXSize + * @param aYSize + */ +static void dbg_save_rgb_buffer( wxString aFileName, + unsigned char *aRGBpixelBuffer, + unsigned int aXSize, + unsigned int aYSize ) +{ + wxImage image( aXSize, aYSize ); + image.SetData( aRGBpixelBuffer ); + image = image.Mirror( false ); + image.SaveFile( aFileName + ".png", wxBITMAP_TYPE_PNG ); + image.Destroy(); +} + + +void DBG_SaveBuffer( wxString aFileName, + const unsigned char *aInBuffer, + unsigned int aXSize, + unsigned int aYSize ) +{ + const unsigned int wxh = aXSize * aYSize; + + unsigned char *pixelbuffer = (unsigned char*) malloc( wxh * 3 ); + + for( unsigned int i = 0; i < wxh; ++i ) + { + unsigned char v = aInBuffer[i]; + + // Set RGB value with all same values intensities + pixelbuffer[i * 3 + 0] = v; + pixelbuffer[i * 3 + 1] = v; + pixelbuffer[i * 3 + 2] = v; + } + + dbg_save_rgb_buffer( aFileName, pixelbuffer, aXSize, aYSize ); +} + + +void DBG_SaveBuffer( wxString aFileName, + const float *aInBuffer, + unsigned int aXSize, + unsigned int aYSize ) +{ + const unsigned int wxh = aXSize * aYSize; + + unsigned char *pixelbuffer = (unsigned char*) malloc( wxh * 3 ); + + for( unsigned int i = 0; i < wxh; ++i ) + { + const unsigned char v = (unsigned char)glm::min( (int)(aInBuffer[i] * 255.0f), + 255 ); + + // Set RGB value with all same values intensities + pixelbuffer[i * 3 + 0] = v; + pixelbuffer[i * 3 + 1] = v; + pixelbuffer[i * 3 + 2] = v; + } + + dbg_save_rgb_buffer( aFileName, pixelbuffer, aXSize, aYSize ); +} + + +void DBG_SaveBuffer( wxString aFileName, + const SFVEC3F *aInBuffer, + unsigned int aXSize, + unsigned int aYSize ) +{ + const unsigned int wxh = aXSize * aYSize; + + unsigned char *pixelbuffer = (unsigned char*) malloc( wxh * 3 ); + + for( unsigned int i = 0; i < wxh; ++i ) + { + const SFVEC3F &v = aInBuffer[i]; + const unsigned int ix3 = i * 3; + + // Set RGB value with all same values intensities + pixelbuffer[ix3 + 0] = (unsigned char)glm::min( (int)(v.r * 255.0f), 255 ); + pixelbuffer[ix3 + 1] = (unsigned char)glm::min( (int)(v.g * 255.0f), 255 ); + pixelbuffer[ix3 + 2] = (unsigned char)glm::min( (int)(v.b * 255.0f), 255 ); + } + + dbg_save_rgb_buffer( aFileName, pixelbuffer, aXSize, aYSize ); +} + + +void DBG_SaveNormalsBuffer( wxString aFileName, + const SFVEC3F *aInNormalsBuffer, + unsigned int aXSize, + unsigned int aYSize ) +{ + const unsigned int wxh = aXSize * aYSize; + + unsigned char *pixelbuffer = (unsigned char*) malloc( wxh * 3 ); + + for( unsigned int i = 0; i < wxh; ++i ) + { + const SFVEC3F &v = aInNormalsBuffer[i]; + const unsigned int ix3 = i * 3; + + // Set RGB value with all same values intensities + pixelbuffer[ix3 + 0] = (unsigned char)glm::min( (int)((v.r + 1.0f) * 127.0f), 255 ); + pixelbuffer[ix3 + 1] = (unsigned char)glm::min( (int)((v.g + 1.0f) * 127.0f), 255 ); + pixelbuffer[ix3 + 2] = (unsigned char)glm::min( (int)((v.b + 1.0f) * 127.0f), 255 ); + } + + dbg_save_rgb_buffer( aFileName, pixelbuffer, aXSize, aYSize ); +} diff --git a/3d-viewer/3d_rendering/buffers_debug.h b/3d-viewer/3d_rendering/buffers_debug.h new file mode 100644 index 0000000000..bb6b385974 --- /dev/null +++ b/3d-viewer/3d_rendering/buffers_debug.h @@ -0,0 +1,55 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** + * @file buffers_debug.h + * @brief + */ + +#ifndef BUFFER_DEBUG_H +#define BUFFER_DEBUG_H + +#include +#include + +void DBG_SaveBuffer( wxString aFileName, + const unsigned char *aInBuffer, + unsigned int aXSize, unsigned int aYSize ); + +void DBG_SaveBuffer( wxString aFileName, + const float *aInBuffer, + unsigned int aXSize, + unsigned int aYSize ); + +void DBG_SaveBuffer( wxString aFileName, + const SFVEC3F *aInBuffer, + unsigned int aXSize, + unsigned int aYSize ); + +void DBG_SaveNormalsBuffer( wxString aFileName, + const SFVEC3F *aInNormalsBuffer, + unsigned int aXSize, + unsigned int aYSize ); + +#endif // BUFFER_DEBUG_H diff --git a/3d-viewer/3d_rendering/c3d_render_base.cpp b/3d-viewer/3d_rendering/c3d_render_base.cpp new file mode 100644 index 0000000000..fb2cdf4124 --- /dev/null +++ b/3d-viewer/3d_rendering/c3d_render_base.cpp @@ -0,0 +1,56 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file c3d_render_base.cpp + * @brief implements the initialization of the base class + */ + + +#include "c3d_render_base.h" + + +/** + * Trace mask used to enable or disable the trace output of this class. + * The debug output can be turned on by setting the WXTRACE environment variable to + * "KI_TRACE_3D_RENDER". See the wxWidgets documentation on wxLogTrace for + * more information. + */ +const wxChar * C3D_RENDER_BASE::m_logTrace = wxT( "KI_TRACE_3D_RENDER" ); + + +C3D_RENDER_BASE::C3D_RENDER_BASE(CINFO3D_VISU &aSettings) : + m_settings( aSettings ) +{ + wxLogTrace( m_logTrace, wxT( "C3D_RENDER_BASE::C3D_RENDER_BASE" ) ); + m_is_opengl_initialized = false; + m_windowSize = wxSize( -1, -1 ); + m_reloadRequested = true; +} + + +C3D_RENDER_BASE::~C3D_RENDER_BASE() +{ +} + diff --git a/3d-viewer/3d_rendering/c3d_render_base.h b/3d-viewer/3d_rendering/c3d_render_base.h index 57508efed5..e5af933ad9 100644 --- a/3d-viewer/3d_rendering/c3d_render_base.h +++ b/3d-viewer/3d_rendering/c3d_render_base.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -33,7 +33,7 @@ #include #include "../3d_canvas/cinfo3d_visu.h" -#include "3d_cache/3d_cache.h" +#include /** * This is a base class to hold data and functions for render targets. @@ -45,29 +45,61 @@ class C3D_RENDER_BASE // Operations public: - C3D_RENDER_BASE( CINFO3D_VISU &aSettings, - S3D_CACHE *a3DModelManager ); + explicit C3D_RENDER_BASE( CINFO3D_VISU &aSettings ); virtual ~C3D_RENDER_BASE() = 0; + /** + * @brief SetCurWindowSize - Before each render, the canvas will tell the + * render what is the size of its windows, so render can take actions if it + * changed. + * @param aSize: the current size of the render window + */ virtual void SetCurWindowSize( const wxSize &aSize ) = 0; - virtual void Redraw( bool aIsMoving ) = 0; + /** + * @brief Redraw - Ask to redraw the view + * @param aIsMoving: if the user is moving the scene, it should be render in + * preview mode + * @param aStatusTextReporter: a pointer to the status progress reporter + * @return it will return true if the render would like to redraw again + */ + virtual bool Redraw( bool aIsMoving, REPORTER *aStatusTextReporter = NULL ) = 0; + /** + * @brief ReloadRequest - !TODO: this must be reviewed to add flags to + * improve specific render + */ void ReloadRequest() { m_reloadRequested = true; } + /** + * @brief IsReloadRequestPending - Query if there is a pending reload request + * @return true if it wants to reload, false if there is no reload pending + */ + bool IsReloadRequestPending() const { return m_reloadRequested; } + + /** + * @brief GetWaitForEditingTimeOut - Give the interface the time (in ms) + * that it should wait for editing or movements before + * (this works for display preview mode) + * @return a value in miliseconds + */ + virtual int GetWaitForEditingTimeOut() = 0; + // Attributes protected: - CINFO3D_VISU &m_settings; - S3D_CACHE *m_3d_model_manager; + /// settings refrence in use for this render + CINFO3D_VISU &m_settings; + + /// flag if the opengl specific for this render was already initialized bool m_is_opengl_initialized; + + /// !TODO: this must be reviewed in order to flag change types bool m_reloadRequested; - /** - * The window size that this camera is working. - */ + /// The window size that this camera is working. wxSize m_windowSize; /** diff --git a/3d-viewer/3d_rendering/ccamera.cpp b/3d-viewer/3d_rendering/ccamera.cpp index a7737ba887..084c601738 100644 --- a/3d-viewer/3d_rendering/ccamera.cpp +++ b/3d-viewer/3d_rendering/ccamera.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,8 +27,6 @@ * @brief */ -#include -#include "../common_ogl/openGL_includes.h" #include "ccamera.h" #include @@ -41,65 +39,131 @@ */ const wxChar *CCAMERA::m_logTrace = wxT( "KI_TRACE_CCAMERA" ); +#define MIN_ZOOM 0.10f +#define MAX_ZOOM 1.25f CCAMERA::CCAMERA( float aRangeScale ) { wxLogTrace( m_logTrace, wxT( "CCAMERA::CCAMERA" ) ); + m_range_scale = aRangeScale; + m_camera_pos_init = SFVEC3F( 0.0f, 0.0f, -(aRangeScale * 2.0f ) ); + m_board_lookat_pos_init = SFVEC3F( 0.0f ); + m_windowSize = SFVEC2I( 0, 0 ); + m_projectionType = PROJECTION_PERSPECTIVE; + m_interpolation_mode = INTERPOLATION_BEZIER; + + Reset(); +} + + +void CCAMERA::Reset() +{ m_parametersChanged = true; - m_projectionType = PROJECTION_PERSPECTIVE; m_projectionMatrix = glm::mat4( 1.0f ); - m_projectionMatrix_inv = glm::mat4( 1.0f ); + m_projectionMatrixInv = glm::mat4( 1.0f ); m_rotationMatrix = glm::mat4( 1.0f ); + m_rotationMatrixAux = glm::mat4( 1.0f ); m_lastPosition = wxPoint( 0, 0 ); - m_windowSize = wxSize( 0, 0 ); + m_zoom = 1.0f; - m_range_scale = aRangeScale; - m_camera_pos_init = SFVEC3F( 0.0f, 0.0f, -aRangeScale ); + m_zoom_t0 = 1.0f; + m_zoom_t1 = 1.0f; m_camera_pos = m_camera_pos_init; - m_boardLookAt_pos = SFVEC3F( 0.0, 0.0, 0.0 ); + m_camera_pos_t0 = m_camera_pos_init; + m_camera_pos_t1 = m_camera_pos_init; + m_lookat_pos = m_board_lookat_pos_init; + m_lookat_pos_t0 = m_board_lookat_pos_init; + m_lookat_pos_t1 = m_board_lookat_pos_init; + + m_rotate_aux = SFVEC3F( 0.0f ); + m_rotate_aux_t0 = SFVEC3F( 0.0f ); + m_rotate_aux_t1 = SFVEC3F( 0.0f ); + + updateRotationMatrix(); updateViewMatrix(); - m_viewMatrix_inverse = glm::inverse( m_viewMatrix ); + m_viewMatrixInverse = glm::inverse( m_viewMatrix ); m_scr_nX.clear(); m_scr_nY.clear(); - memset( &m_frustum, 0, sizeof( m_frustum ) ); + rebuildProjection(); +} + + +void CCAMERA::Reset_T1() +{ + m_camera_pos_t1 = m_camera_pos_init; + m_zoom_t1 = 1.0f; + m_rotate_aux_t1 = SFVEC3F( 0.0f ); + m_lookat_pos_t1 = m_board_lookat_pos_init; } void CCAMERA::updateViewMatrix() { m_viewMatrix = glm::translate( glm::mat4( 1.0f ), m_camera_pos ) * - m_rotationMatrix * glm::translate( glm::mat4( 1.0f ), m_boardLookAt_pos ); + m_rotationMatrix * m_rotationMatrixAux * + glm::translate( glm::mat4( 1.0f ), -m_lookat_pos ); } -const glm::mat4 &CCAMERA::GetRotationMatrix() const +void CCAMERA::updateRotationMatrix() { - return m_rotationMatrix; + m_rotationMatrixAux = glm::rotate( glm::mat4( 1.0f ), + m_rotate_aux.x, + SFVEC3F( 1.0f, 0.0f, 0.0f ) ); + + m_rotationMatrixAux = glm::rotate( m_rotationMatrixAux, + m_rotate_aux.y, + SFVEC3F( 0.0f, 1.0f, 0.0f ) ); + + m_rotationMatrixAux = glm::rotate( m_rotationMatrixAux, + m_rotate_aux.z, + SFVEC3F( 0.0f, 0.0f, 1.0f ) ); + + m_parametersChanged = true; + + updateViewMatrix(); + updateFrustum(); +} + + +const glm::mat4 CCAMERA::GetRotationMatrix() const +{ + return m_rotationMatrix * m_rotationMatrixAux; } void CCAMERA::rebuildProjection() { - m_frustum.ratio = (float) m_windowSize.x / m_windowSize.y; - m_frustum.nearD = 0.01f; - m_frustum.farD = glm::length( m_camera_pos_init ) * 2.0f; // Consider that we can render double the length, review if that is OK... + if( (m_windowSize.x == 0) || + (m_windowSize.y == 0) ) + return; + + m_frustum.ratio = (float) m_windowSize.x / (float)m_windowSize.y; + + // Consider that we can render double the length multiplied by the 2/sqrt(2) + // + m_frustum.farD = glm::length( m_camera_pos_init ) * 2.0f * ( 2.0f * sqrtf( 2.0f ) ); switch( m_projectionType ) { + default: case PROJECTION_PERSPECTIVE: + + m_frustum.nearD = 0.10f; + // Ratio width / height of the window display m_frustum.angle = 45.0f * m_zoom; - m_projectionMatrix = glm::perspective( m_frustum.angle * ( glm::pi() / 180.0f ), + m_projectionMatrix = glm::perspective( glm::radians( m_frustum.angle ), m_frustum.ratio, m_frustum.nearD, m_frustum.farD ); - m_projectionMatrix_inv = glm::inverse( m_projectionMatrix ); + m_projectionMatrixInv = glm::inverse( m_projectionMatrix ); - m_frustum.tang = (float)tan( m_frustum.angle * ( glm::pi() / 180.0f ) * 0.5f ) ; + m_frustum.tang = glm::tan( glm::radians( m_frustum.angle ) * 0.5f ) ; m_focalLen.x = ( (float)m_windowSize.y / (float)m_windowSize.x ) / m_frustum.tang; m_focalLen.y = 1.0f / m_frustum.tang; @@ -111,17 +175,19 @@ void CCAMERA::rebuildProjection() break; case PROJECTION_ORTHO: - const float orthoReductionFactor = m_zoom / 400.0f; + + m_frustum.nearD = 0.01f; + + const float orthoReductionFactor = m_zoom / 75.0f; // Initialize Projection Matrix for Ortographic View m_projectionMatrix = glm::ortho( -m_windowSize.x * orthoReductionFactor, m_windowSize.x * orthoReductionFactor, -m_windowSize.y * orthoReductionFactor, m_windowSize.y * orthoReductionFactor, - m_frustum.nearD, - m_frustum.farD ); + m_frustum.nearD, m_frustum.farD ); - m_projectionMatrix_inv = glm::inverse( m_projectionMatrix ); + m_projectionMatrixInv = glm::inverse( m_projectionMatrix ); m_frustum.nw = m_windowSize.x * orthoReductionFactor * 2.0f; m_frustum.nh = m_windowSize.y * orthoReductionFactor * 2.0f; @@ -135,20 +201,22 @@ void CCAMERA::rebuildProjection() m_scr_nY.resize( m_windowSize.y ); // Precalc X values for camera -> ray generation - for( unsigned int x = 0; x < (unsigned int)m_windowSize.x; x++ ) + for( unsigned int x = 0; x < (unsigned int)m_windowSize.x; ++x ) { // Converts 0.0 .. 1.0 - float xNormalizedDeviceCoordinates = ( ( (float)x + 0.5f ) / (m_windowSize.x - 0.0f) ); + const float xNormalizedDeviceCoordinates = ( ( (float)x + 0.5f ) / + (m_windowSize.x - 0.0f) ); // Converts -1.0 .. 1.0 m_scr_nX[x] = 2.0f * xNormalizedDeviceCoordinates - 1.0f; } // Precalc Y values for camera -> ray generation - for( unsigned int y = 0; y < (unsigned int)m_windowSize.y; y++ ) + for( unsigned int y = 0; y < (unsigned int)m_windowSize.y; ++y ) { // Converts 0.0 .. 1.0 - float yNormalizedDeviceCoordinates = ( ( (float)y + 0.5f ) / (m_windowSize.y - 0.0f) ); + const float yNormalizedDeviceCoordinates = ( ( (float)y + 0.5f ) / + (m_windowSize.y - 0.0f) ); // Converts -1.0 .. 1.0 m_scr_nY[y] = 2.0f * yNormalizedDeviceCoordinates - 1.0f; @@ -161,12 +229,18 @@ void CCAMERA::rebuildProjection() void CCAMERA::updateFrustum() { // Update matrix and vectors - m_viewMatrix_inverse = glm::inverse( m_viewMatrix ); + m_viewMatrixInverse = glm::inverse( m_viewMatrix ); - m_right = glm::normalize( SFVEC3F( m_viewMatrix_inverse * glm::vec4( SFVEC3F( 1.0, 0.0, 0.0 ), 0.0 ) ) ); - m_up = glm::normalize( SFVEC3F( m_viewMatrix_inverse * glm::vec4( SFVEC3F( 0.0, 1.0, 0.0 ), 0.0 ) ) ); - m_dir = glm::normalize( SFVEC3F( m_viewMatrix_inverse * glm::vec4( SFVEC3F( 0.0, 0.0, 1.0 ), 0.0 ) ) ); - m_pos = SFVEC3F( m_viewMatrix_inverse * glm::vec4( SFVEC3F( 0.0, 0.0, 0.0 ), 1.0 ) ); + m_right = glm::normalize( SFVEC3F( m_viewMatrixInverse * + glm::vec4( SFVEC3F( 1.0, 0.0, 0.0 ), 0.0 ) ) ); + + m_up = glm::normalize( SFVEC3F( m_viewMatrixInverse * + glm::vec4( SFVEC3F( 0.0, 1.0, 0.0 ), 0.0 ) ) ); + + m_dir = glm::normalize( SFVEC3F( m_viewMatrixInverse * + glm::vec4( SFVEC3F( 0.0, 0.0, 1.0 ), 0.0 ) ) ); + + m_pos = SFVEC3F( m_viewMatrixInverse * glm::vec4( SFVEC3F( 0.0, 0.0, 0.0 ), 1.0 ) ); /* @@ -195,188 +269,51 @@ void CCAMERA::updateFrustum() m_up_nY.resize( m_windowSize.y ); // Precalc X values for camera -> ray generation - SFVEC3F right_nw = m_right * m_frustum.nw; + const SFVEC3F right_nw = m_right * m_frustum.nw; - for( unsigned int x = 0; x < (unsigned int)m_windowSize.x; x++ ) + for( unsigned int x = 0; x < (unsigned int)m_windowSize.x; ++x ) m_right_nX[x] = right_nw * m_scr_nX[x]; // Precalc Y values for camera -> ray generation - SFVEC3F up_nh = m_up * m_frustum.nh; + const SFVEC3F up_nh = m_up * m_frustum.nh; - for( unsigned int y = 0; y < (unsigned int)m_windowSize.y; y++ ) - m_up_nY[y] = m_frustum.nc + (up_nh * m_scr_nY[y]); + for( unsigned int y = 0; y < (unsigned int)m_windowSize.y; ++y ) + m_up_nY[y] = up_nh * m_scr_nY[y]; } -void CCAMERA::MakeRay( const SFVEC2I &aWindowPos, SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection ) const +void CCAMERA::MakeRay( const SFVEC2I &aWindowPos, + SFVEC3F &aOutOrigin, + SFVEC3F &aOutDirection ) const { - aOutOrigin = m_up_nY[aWindowPos.y] + - m_right_nX[aWindowPos.x]; + //const SFVEC2I minWindowsPos = glm::min( aWindowPos, m_windowSize ); + wxASSERT( aWindowPos.x < m_windowSize.x ); + wxASSERT( aWindowPos.y < m_windowSize.y ); + const SFVEC2I &minWindowsPos = aWindowPos; - aOutDirection = glm::normalize( aOutOrigin - m_pos ); + switch( m_projectionType ) + { + default: + case PROJECTION_PERSPECTIVE: + aOutOrigin = m_up_nY[minWindowsPos.y] + m_right_nX[minWindowsPos.x] + m_frustum.nc; + aOutDirection = glm::normalize( aOutOrigin - m_pos ); + break; + + case PROJECTION_ORTHO: + aOutOrigin = (m_up_nY[minWindowsPos.y] + m_right_nX[minWindowsPos.x]) * 0.5f + + m_frustum.nc; + aOutDirection = -m_dir; + break; + } } -void CCAMERA::GLdebug_Lines() +void CCAMERA::MakeRayAtCurrrentMousePosition( SFVEC3F &aOutOrigin, + SFVEC3F &aOutDirection ) const { - SFVEC3F ntl = m_frustum.ntl; - SFVEC3F ntr = m_frustum.ntr; - SFVEC3F nbl = m_frustum.nbl; - SFVEC3F nbr = m_frustum.nbr; - - SFVEC3F ftl = m_frustum.ftl; - SFVEC3F ftr = m_frustum.ftr; - SFVEC3F fbl = m_frustum.fbl; - SFVEC3F fbr = m_frustum.fbr; - - glColor4f( 1.0, 1.0, 1.0, 0.7 ); - - glBegin(GL_LINE_LOOP); - //near plane - glVertex3f(ntl.x,ntl.y,ntl.z); - glVertex3f(ntr.x,ntr.y,ntr.z); - glVertex3f(nbr.x,nbr.y,nbr.z); - glVertex3f(nbl.x,nbl.y,nbl.z); - glEnd(); - - glBegin(GL_LINE_LOOP); - //far plane - glVertex3f(ftr.x,ftr.y,ftr.z); - glVertex3f(ftl.x,ftl.y,ftl.z); - glVertex3f(fbl.x,fbl.y,fbl.z); - glVertex3f(fbr.x,fbr.y,fbr.z); - glEnd(); - - glBegin(GL_LINE_LOOP); - //bottom plane - glVertex3f(nbl.x,nbl.y,nbl.z); - glVertex3f(nbr.x,nbr.y,nbr.z); - glVertex3f(fbr.x,fbr.y,fbr.z); - glVertex3f(fbl.x,fbl.y,fbl.z); - glEnd(); - - glBegin(GL_LINE_LOOP); - //top plane - glVertex3f(ntr.x,ntr.y,ntr.z); - glVertex3f(ntl.x,ntl.y,ntl.z); - glVertex3f(ftl.x,ftl.y,ftl.z); - glVertex3f(ftr.x,ftr.y,ftr.z); - glEnd(); - - glBegin(GL_LINE_LOOP); - //left plane - glVertex3f(ntl.x,ntl.y,ntl.z); - glVertex3f(nbl.x,nbl.y,nbl.z); - glVertex3f(fbl.x,fbl.y,fbl.z); - glVertex3f(ftl.x,ftl.y,ftl.z); - glEnd(); - - glBegin(GL_LINE_LOOP); - // right plane - glVertex3f(nbr.x,nbr.y,nbr.z); - glVertex3f(ntr.x,ntr.y,ntr.z); - glVertex3f(ftr.x,ftr.y,ftr.z); - glVertex3f(fbr.x,fbr.y,fbr.z); - glEnd(); -} - - -void CCAMERA::GLdebug_Vectors() -{ - SFVEC3F right = m_pos + m_right * m_frustum.nearD; - SFVEC3F up = m_pos + m_up * m_frustum.nearD; - SFVEC3F dir = m_pos - m_dir * m_frustum.nearD; - - glColor4f( 1.0, 0.0, 0.0, 1.0 ); - glBegin( GL_LINES ); - glVertex3fv( &m_pos.x ); - glVertex3fv( &right.x ); - glEnd(); - - glColor4f( 0.0, 1.0, 0.0, 1.0 ); - glBegin( GL_LINES ); - glVertex3fv( &m_pos.x ); - glVertex3fv( &up.x ); - glEnd(); - - glColor4f( 0.0, 0.0, 1.0, 1.0 ); - glBegin( GL_LINES ); - glVertex3fv( &m_pos.x ); - glVertex3fv( &dir.x ); - glEnd(); -} - - -void CCAMERA::GLdebug_Planes() -{ - SFVEC3F ntl = m_frustum.ntl; - SFVEC3F ntr = m_frustum.ntr; - SFVEC3F nbl = m_frustum.nbl; - SFVEC3F nbr = m_frustum.nbr; - - SFVEC3F ftl = m_frustum.ftl; - SFVEC3F ftr = m_frustum.ftr; - SFVEC3F fbl = m_frustum.fbl; - SFVEC3F fbr = m_frustum.fbr; - - // Initialize alpha blending function. - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - - glBegin(GL_QUADS); - - //near plane - glColor4f( 0.0, 0.0, 0.5, 0.6 ); - glVertex3f(ntl.x,ntl.y,ntl.z); - glVertex3f(ntr.x,ntr.y,ntr.z); - glVertex3f(nbr.x,nbr.y,nbr.z); - glVertex3f(nbl.x,nbl.y,nbl.z); - - //far plane - glColor4f( 0.0, 0.0, 0.5, 0.6 ); - glVertex3f(ftr.x,ftr.y,ftr.z); - glVertex3f(ftl.x,ftl.y,ftl.z); - glVertex3f(fbl.x,fbl.y,fbl.z); - glVertex3f(fbr.x,fbr.y,fbr.z); - - //bottom plane - glColor4f( 0.0, 0.5, 0.0, 0.6 ); - glVertex3f(nbl.x,nbl.y,nbl.z); - glVertex3f(nbr.x,nbr.y,nbr.z); - glVertex3f(fbr.x,fbr.y,fbr.z); - glVertex3f(fbl.x,fbl.y,fbl.z); - - //top plane - glColor4f( 0.0, 0.5, 0.0, 0.6 ); - glVertex3f(ntr.x,ntr.y,ntr.z); - glVertex3f(ntl.x,ntl.y,ntl.z); - glVertex3f(ftl.x,ftl.y,ftl.z); - glVertex3f(ftr.x,ftr.y,ftr.z); - - //left plane - glColor4f( 0.5, 0.0, 0.0, 0.6 ); - glVertex3f(ntl.x,ntl.y,ntl.z); - glVertex3f(nbl.x,nbl.y,nbl.z); - glVertex3f(fbl.x,fbl.y,fbl.z); - glVertex3f(ftl.x,ftl.y,ftl.z); - - // right plane - glColor4f( 0.5, 0.0, 0.0, 0.6 ); - glVertex3f(nbr.x,nbr.y,nbr.z); - glVertex3f(ntr.x,ntr.y,ntr.z); - glVertex3f(ftr.x,ftr.y,ftr.z); - glVertex3f(fbr.x,fbr.y,fbr.z); - - glEnd(); - - glDisable( GL_BLEND ); -/* - glColor3f( 0.0, 0.0, 1.0 ); - OGL_draw_arrow( m_pos, - m_frustum.nc, - 0.1);*/ - - + MakeRay( SFVEC2I( m_lastPosition.x, + m_windowSize.y - m_lastPosition.y ), + aOutOrigin, aOutDirection ); } @@ -388,7 +325,25 @@ const glm::mat4 &CCAMERA::GetProjectionMatrix() const const glm::mat4 &CCAMERA::GetProjectionMatrixInv() const { - return m_projectionMatrix_inv; + return m_projectionMatrixInv; +} + + +void CCAMERA::ResetXYpos() +{ + m_parametersChanged = true; + m_camera_pos.x = 0.0f; + m_camera_pos.y = 0.0f; + + updateViewMatrix(); + updateFrustum(); +} + + +void CCAMERA::ResetXYpos_T1() +{ + m_camera_pos_t1.x = 0.0f; + m_camera_pos_t1.y = 0.0f; } @@ -400,7 +355,7 @@ const glm::mat4 &CCAMERA::GetViewMatrix() const const glm::mat4 &CCAMERA::GetViewMatrix_Inv() const { - return m_viewMatrix_inverse; + return m_viewMatrixInverse; } @@ -420,13 +375,30 @@ void CCAMERA::SetProjection( PROJECTION_TYPE aProjectionType ) } -void CCAMERA::SetCurWindowSize( const wxSize &aSize ) +void CCAMERA::ToggleProjection() { - if( m_windowSize != aSize ) + if( m_projectionType == PROJECTION_ORTHO ) + m_projectionType = PROJECTION_PERSPECTIVE; + else + m_projectionType = PROJECTION_ORTHO; + + rebuildProjection(); +} + + +bool CCAMERA::SetCurWindowSize( const wxSize &aSize ) +{ + const SFVEC2I newSize = SFVEC2I( aSize.x, aSize.y ); + + if( m_windowSize != newSize ) { - m_windowSize = aSize; + m_windowSize = newSize; rebuildProjection(); + + return true; } + + return false; } @@ -441,41 +413,90 @@ void CCAMERA::ZoomReset() } -void CCAMERA::ZoomIn( float aFactor ) +bool CCAMERA::ZoomIn( float aFactor ) { - float old_zoom = m_zoom; - - m_zoom /= aFactor; - - if( m_zoom <= 0.05f ) - m_zoom = 0.05f; - - m_camera_pos.z = m_zoom * m_camera_pos_init.z; - - if( old_zoom != m_zoom ) + if( m_zoom > MIN_ZOOM ) { - updateViewMatrix(); - rebuildProjection(); + const float old_zoom = m_zoom; + + m_zoom /= aFactor; + + if( m_zoom <= MIN_ZOOM ) + m_zoom = MIN_ZOOM; + + m_camera_pos.z = m_zoom * m_camera_pos_init.z; + + if( old_zoom != m_zoom ) + { + updateViewMatrix(); + rebuildProjection(); + + return true; + } } + + return false; } -void CCAMERA::ZoomOut( float aFactor ) +bool CCAMERA::ZoomOut( float aFactor ) { - float old_zoom = m_zoom; - - m_zoom *= aFactor; - - if( m_zoom >= 1.5f ) - m_zoom = 1.5f; - - m_camera_pos.z = m_zoom * m_camera_pos_init.z; - - if( old_zoom != m_zoom ) + if( m_zoom < MAX_ZOOM ) { - updateViewMatrix(); - rebuildProjection(); + const float old_zoom = m_zoom; + + m_zoom *= aFactor; + + if( m_zoom >= MAX_ZOOM ) + m_zoom = MAX_ZOOM; + + m_camera_pos.z = m_zoom * m_camera_pos_init.z; + + if( old_zoom != m_zoom ) + { + updateViewMatrix(); + rebuildProjection(); + + return true; + } } + + return false; +} + +bool CCAMERA::ZoomIn_T1( float aFactor ) +{ + if( m_zoom > MIN_ZOOM ) + { + m_zoom_t1 = m_zoom / aFactor; + + if( m_zoom_t1 <= MIN_ZOOM ) + m_zoom_t1 = MIN_ZOOM; + + m_camera_pos_t1.z = m_zoom_t1 * m_camera_pos_init.z; + + return true; + } + + return false; +} + + +bool CCAMERA::ZoomOut_T1( float aFactor ) +{ + if( m_zoom < MAX_ZOOM ) + { + m_zoom_t1 = m_zoom * aFactor; + + if( m_zoom_t1 >= MAX_ZOOM ) + m_zoom_t1 = MAX_ZOOM; + + m_camera_pos_t1.z = m_zoom_t1 * m_camera_pos_init.z; + + return true; + } + + return false; } @@ -485,9 +506,80 @@ float CCAMERA::ZoomGet() const } +void CCAMERA::RotateX( float aAngleInRadians ) +{ + m_rotate_aux.x += aAngleInRadians; + updateRotationMatrix(); +} + + +void CCAMERA::RotateY( float aAngleInRadians ) +{ + m_rotate_aux.y += aAngleInRadians; + updateRotationMatrix(); +} + + +void CCAMERA::RotateZ( float aAngleInRadians ) +{ + m_rotate_aux.z += aAngleInRadians; + updateRotationMatrix(); +} + + +void CCAMERA::RotateX_T1( float aAngleInRadians ) +{ + m_rotate_aux_t1.x += aAngleInRadians; +} + + +void CCAMERA::RotateY_T1( float aAngleInRadians ) +{ + m_rotate_aux_t1.y += aAngleInRadians; +} + + +void CCAMERA::RotateZ_T1( float aAngleInRadians ) +{ + m_rotate_aux_t1.z += aAngleInRadians; +} + + +void CCAMERA::SetT0_and_T1_current_T() +{ + m_camera_pos_t0 = m_camera_pos; + m_lookat_pos_t0 = m_lookat_pos; + m_rotate_aux_t0 = m_rotate_aux; + m_zoom_t0 = m_zoom; + + m_camera_pos_t1 = m_camera_pos; + m_lookat_pos_t1 = m_lookat_pos; + m_rotate_aux_t1 = m_rotate_aux; + m_zoom_t1 = m_zoom; +} + + +void CCAMERA::Interpolate( float t ) +{ + wxASSERT( t >= 0.0f ); + + const float t0 = 1.0f - t; + + m_camera_pos = m_camera_pos_t0 * t0 + m_camera_pos_t1 * t; + m_lookat_pos = m_lookat_pos_t0 * t0 + m_lookat_pos_t1 * t; + m_rotate_aux = m_rotate_aux_t0 * t0 + m_rotate_aux_t1 * t; + m_zoom = m_zoom_t0 * t0 + m_zoom_t1 * t; + + m_parametersChanged = true; + + updateRotationMatrix(); + rebuildProjection(); +} + + bool CCAMERA::ParametersChanged() { - bool parametersChanged = m_parametersChanged; + const bool parametersChanged = m_parametersChanged; m_parametersChanged = false; diff --git a/3d-viewer/3d_rendering/ccamera.h b/3d-viewer/3d_rendering/ccamera.h index cd2e838fe3..363fcbdd3d 100644 --- a/3d-viewer/3d_rendering/ccamera.h +++ b/3d-viewer/3d_rendering/ccamera.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,9 +30,8 @@ #ifndef CCAMERA_H #define CCAMERA_H -#include "plugins/3dapi/xv3d_types.h" -#include "3d_rendering/3d_render_raytracing/ray.h" -#include // for wxSize +#include "../3d_rendering/3d_render_raytracing/ray.h" +#include // for wxSize #include enum PROJECTION_TYPE @@ -62,6 +61,16 @@ struct FRUSTUM float nw, nh, fw, fh; }; + +enum CAMERA_INTERPOLATION +{ + INTERPOLATION_LINEAR, + INTERPOLATION_EASING_IN_OUT, // Quadratic + INTERPOLATION_BEZIER, + +}; + + /** * Class CCAMERA * is a virtual class used to derive CCAMERA objects from. @@ -75,10 +84,11 @@ class CCAMERA /** * @brief CCAMERA initialize a camera - * @param aRangeScale: it will be expected that the board will have a -aRangeScale/2 to +aRangeScale/2 + * @param aRangeScale: it will be expected that the board will have a + * -aRangeScale/2 to +aRangeScale/2 * it will initialize the initial Z position with aRangeScale */ - CCAMERA( float aRangeScale ); + explicit CCAMERA( float aRangeScale ); /** @@ -86,7 +96,7 @@ class CCAMERA * Get the rotation matrix to be applied in a transformation camera * @return the rotation matrix of the camera */ - const glm::mat4 &GetRotationMatrix() const; + const glm::mat4 GetRotationMatrix() const; const glm::mat4 &GetViewMatrix() const; const glm::mat4 &GetViewMatrix_Inv() const; @@ -102,7 +112,23 @@ class CCAMERA float GetNear() const { return m_frustum.nearD; } float GetFar() const { return m_frustum.farD; } - virtual void SetBoardLookAtPos( const SFVEC3F &aBoardPos ) = 0; + void SetBoardLookAtPos( const SFVEC3F &aBoardPos ) { + if( m_board_lookat_pos_init != aBoardPos ) + { + m_board_lookat_pos_init = aBoardPos; + SetLookAtPos( aBoardPos ); + } + } + + virtual void SetLookAtPos( const SFVEC3F &aLookAtPos ) = 0; + + void SetLookAtPos_T1( const SFVEC3F &aLookAtPos ) { + m_lookat_pos_t1 = aLookAtPos; + } + + const SFVEC3F &GetLookAtPos_T1() const { return m_lookat_pos_t1; } + + const SFVEC3F &GetCameraPos() const { return m_camera_pos; } /** * Calculate a new mouse drag position @@ -113,43 +139,99 @@ class CCAMERA virtual void Pan( const SFVEC3F &aDeltaOffsetInc ) = 0; + virtual void Pan_T1( const SFVEC3F &aDeltaOffsetInc ) = 0; + /** * Reset the camera to initial state */ - virtual void Reset() = 0; + virtual void Reset(); + virtual void Reset_T1(); + void ResetXYpos(); + void ResetXYpos_T1(); /** - * It updates the current mouse position without make any new recalculations on camera. + * It updates the current mouse position without make any new recalculations + * on camera. */ void SetCurMousePosition( const wxPoint &aPosition ); void SetProjection( PROJECTION_TYPE aProjectionType ); - void SetCurWindowSize( const wxSize &aSize ); + void ToggleProjection(); + + /** + * @brief SetCurWindowSize - update the windows size of the camera + * @param aSize + * @return true if the windows size changed since last time + */ + bool SetCurWindowSize( const wxSize &aSize ); void ZoomReset(); - void ZoomIn( float aFactor ); + bool ZoomIn( float aFactor ); - void ZoomOut( float aFactor ); + bool ZoomOut( float aFactor ); + + bool ZoomIn_T1( float aFactor ); + + bool ZoomOut_T1( float aFactor ); float ZoomGet() const ; + void RotateX( float aAngleInRadians ); + void RotateY( float aAngleInRadians ); + void RotateZ( float aAngleInRadians ); + + void RotateX_T1( float aAngleInRadians ); + void RotateY_T1( float aAngleInRadians ); + void RotateZ_T1( float aAngleInRadians ); + + /** + * @brief SetT0_and_T1_current_T - This will set T0 and T1 with the current values + */ + virtual void SetT0_and_T1_current_T(); + + /** + * @brief Interpolate - It will update the matrix to interpolate between T0 and T1 values + * @param t the interpolation time, between 0.0f and 1.0f (it will clamp if >1) + */ + virtual void Interpolate( float t ); + + void SetInterpolateMode( CAMERA_INTERPOLATION aInterpolateMode ) + { + m_interpolation_mode = aInterpolateMode; + } + /** * Function ParametersChanged - * @return true if some of the parameters in camera was changed, it will reset the flag + * @return true if some of the parameters in camera was changed, + * it will reset the flag */ bool ParametersChanged(); + /** + * Function ParametersChangedQuery + * @return true if some of the parameters in camera was changed, + * it will NOT reset the flag + */ + bool ParametersChangedQuery() const { return m_parametersChanged; } + + /** + * @brief MakeRay - Make a ray based on a windows screen position + * @param aWindowPos: the windows buffer position + * @param aOutOrigin: out origin position of the ray + * @param aOutDirection: out direction + */ void MakeRay( const SFVEC2I &aWindowPos, SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection ) const; - void GLdebug_Lines(); - - void GLdebug_Planes(); - - void GLdebug_Vectors(); + /** + * @brief MakeRayAtCurrrentMousePosition - Make a ray based on the latest mouse position + * @param aOutOrigin: out origin position of the ray + * @param aOutDirection: out direction + */ + void MakeRayAtCurrrentMousePosition( SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection ) const; protected: @@ -157,6 +239,8 @@ class CCAMERA void updateFrustum(); void updateViewMatrix(); + void updateRotationMatrix(); + /** * @brief m_range_scale - the nominal range expected to be used in the camera. * It will be used to initialize the Z position @@ -167,11 +251,13 @@ class CCAMERA * 3D zoom value (Min 0.0 ... Max 1.0) */ float m_zoom; + float m_zoom_t0; + float m_zoom_t1; /** * The window size that this camera is working. */ - wxSize m_windowSize; + SFVEC2I m_windowSize; /** * The last mouse position in the screen @@ -179,10 +265,11 @@ class CCAMERA wxPoint m_lastPosition; glm::mat4 m_rotationMatrix; + glm::mat4 m_rotationMatrixAux; glm::mat4 m_viewMatrix; - glm::mat4 m_viewMatrix_inverse; + glm::mat4 m_viewMatrixInverse; glm::mat4 m_projectionMatrix; - glm::mat4 m_projectionMatrix_inv; + glm::mat4 m_projectionMatrixInv; PROJECTION_TYPE m_projectionType; FRUSTUM m_frustum; @@ -196,17 +283,30 @@ class CCAMERA SFVEC3F m_camera_pos_init; SFVEC3F m_camera_pos; + SFVEC3F m_camera_pos_t0; + SFVEC3F m_camera_pos_t1; - SFVEC3F m_boardLookAt_pos; + SFVEC3F m_lookat_pos; + SFVEC3F m_lookat_pos_t0; + SFVEC3F m_lookat_pos_t1; + SFVEC3F m_board_lookat_pos_init; ///< Default boardlookat position (the board center) + + SFVEC3F m_rotate_aux; ///< Stores the rotation angle auxiliar + SFVEC3F m_rotate_aux_t0; + SFVEC3F m_rotate_aux_t1; + + CAMERA_INTERPOLATION m_interpolation_mode; /** - * Precalc values array used to calc ray for each pixel (constant for the same window size) + * Precalc values array used to calc ray for each pixel + * (constant for the same window size) */ std::vector< float > m_scr_nX; std::vector< float > m_scr_nY; /** - * Precalc values array used to calc ray for each pixel, for X and Y axis of each new camera position + * Precalc values array used to calc ray for each pixel, + * for X and Y axis of each new camera position */ std::vector< SFVEC3F > m_right_nX; std::vector< SFVEC3F > m_up_nY; diff --git a/3d-viewer/3d_rendering/ccolorrgb.cpp b/3d-viewer/3d_rendering/ccolorrgb.cpp new file mode 100644 index 0000000000..4fc2ad4d80 --- /dev/null +++ b/3d-viewer/3d_rendering/ccolorrgb.cpp @@ -0,0 +1,72 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** + * @file ccolorrgb.cpp + * @brief + */ + + +#include "ccolorrgb.h" + + +CCOLORRGB::CCOLORRGB( const SFVEC3F &aColor ) +{ + r = (unsigned int)glm::clamp( (int)(aColor.r * 255), 0, 255 ); + g = (unsigned int)glm::clamp( (int)(aColor.g * 255), 0, 255 ); + b = (unsigned int)glm::clamp( (int)(aColor.b * 255), 0, 255 ); +} + + +CCOLORRGB BlendColor( const CCOLORRGB &aC1, const CCOLORRGB &aC2 ) +{ + const unsigned int r = aC1.r + aC2.r; + const unsigned int g = aC1.g + aC2.g; + const unsigned int b = aC1.b + aC2.b; + + return CCOLORRGB( (r >> 1), (g >> 1), (b >> 1) ); +} + + +CCOLORRGB BlendColor( const CCOLORRGB &aC1, const CCOLORRGB &aC2, const CCOLORRGB &aC3 ) +{ + const unsigned int r = aC1.r + aC2.r + aC3.r; + const unsigned int g = aC1.g + aC2.g + aC3.g; + const unsigned int b = aC1.b + aC2.b + aC3.b; + + return CCOLORRGB( (r / 3), (g / 3), (b / 3) ); +} + + +CCOLORRGB BlendColor( const CCOLORRGB &aC1, + const CCOLORRGB &aC2, + const CCOLORRGB &aC3, + const CCOLORRGB &aC4 ) +{ + const unsigned int r = aC1.r + aC2.r + aC3.r + aC4.r; + const unsigned int g = aC1.g + aC2.g + aC3.g + aC4.g; + const unsigned int b = aC1.b + aC2.b + aC3.b + aC4.b; + + return CCOLORRGB( (r >> 2), (g >> 2), (b >> 2) ); +} diff --git a/3d-viewer/3d_rendering/ccolorrgb.h b/3d-viewer/3d_rendering/ccolorrgb.h new file mode 100644 index 0000000000..a413b2d96f --- /dev/null +++ b/3d-viewer/3d_rendering/ccolorrgb.h @@ -0,0 +1,60 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** + * @file ccolorrgb.h + * @brief + */ + +#ifndef CCOLORRGB_H +#define CCOLORRGB_H + +#include + +union CCOLORRGB +{ + unsigned char c[3]; + + struct + { + unsigned char r; + unsigned char g; + unsigned char b; + }; + + CCOLORRGB( const SFVEC3F &aColor ); + CCOLORRGB() { r = 0; g = 0; b = 0; } + CCOLORRGB( unsigned char aR, unsigned char aG, unsigned char aB ) { r = aR; + g = aG; + b = aB; } +}; + +CCOLORRGB BlendColor( const CCOLORRGB &aC1, const CCOLORRGB &aC2 ); +CCOLORRGB BlendColor( const CCOLORRGB &aC1, const CCOLORRGB &aC2, const CCOLORRGB &aC3 ); +CCOLORRGB BlendColor( const CCOLORRGB &aC1, + const CCOLORRGB &aC2, + const CCOLORRGB &aC3, + const CCOLORRGB &aC4 ); + +#endif // CCOLORRGB_H diff --git a/3d-viewer/3d_rendering/cimage.cpp b/3d-viewer/3d_rendering/cimage.cpp index e2135316f1..755ef0b889 100644 --- a/3d-viewer/3d_rendering/cimage.cpp +++ b/3d-viewer/3d_rendering/cimage.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,11 +28,11 @@ */ #include "cimage.h" -#include // Used for save an image to disk -#include // For memcpy +#include "buffers_debug.h" +#include // For memcpy #ifndef CLAMP -#define CLAMP(n, min, max) {if (n < min) n=min; else if (n > max) n = max;} +#define CLAMP(n, min, max) {if( n < min ) n=min; else if( n > max ) n = max;} #endif @@ -83,12 +83,14 @@ bool CIMAGE::wrapCoords( int *aXo, int *aYo ) const y = (y < 0)?0:y; y = (y >= (int)(m_height - 1))?(m_height - 1):y; break; + case WRAP_WRAP: x = (x < 0)?((m_width - 1)+x):x; x = (x >= (int)(m_width - 1))?(x - m_width):x; y = (y < 0)?((m_height - 1)+y):y; y = (y >= (int)(m_height - 1))?(y - m_height):y; break; + default: break; } @@ -139,6 +141,7 @@ void CIMAGE::Hline( int aXStart, int aXEnd, int aY, unsigned char aValue ) if( aXStart > aXEnd ) { int swap = aXStart; + aXStart = aXEnd; aXEnd = swap; } @@ -200,12 +203,12 @@ void CIMAGE::CopyFull( const CIMAGE *aImgA, const CIMAGE *aImgB, E_IMAGE_OP aOpe if( aOperation == COPY_RAW ) { - if ( aImgA == NULL ) + if( aImgA == NULL ) return; } else { - if ( (aImgA == NULL) || (aImgB == NULL) ) + if( (aImgA == NULL) || (aImgB == NULL) ) return; } @@ -247,7 +250,7 @@ void CIMAGE::CopyFull( const CIMAGE *aImgA, const CIMAGE *aImgB, E_IMAGE_OP aOpe aV = aImgA->m_pixels[it]; bV = aImgB->m_pixels[it]; - m_pixels[it] = abs(aV - bV); + m_pixels[it] = abs( aV - bV ); } break; @@ -321,7 +324,7 @@ void CIMAGE::CopyFull( const CIMAGE *aImgA, const CIMAGE *aImgB, E_IMAGE_OP aOpe // with a generic convolution matrix and get the values from there. // http://docs.gimp.org/nl/plug-in-convmatrix.html static const S_FILTER FILTERS[] = { - // Hi Pass + // FILTER_HIPASS { { { 0, -1, -1, -1, 0}, {-1, 2, -4, 2, -1}, @@ -333,7 +336,7 @@ static const S_FILTER FILTERS[] = { 255 }, - // Blur + // FILTER_GAUSSIAN_BLUR { { { 3, 5, 7, 5, 3}, { 5, 9, 12, 9, 5}, @@ -345,19 +348,7 @@ static const S_FILTER FILTERS[] = { 0 }, - // Blur Invert - { - { { 0, 0, 0, 0, 0}, - { 0, 0, -1, 0, 0}, - { 0, -1, 0, -1, 0}, - { 0, 0, -1, 0, 0}, - { 0, 0, 0, 0, 0} - }, - 4, - 255 - }, - - // Blur + // FILTER_GAUSSIAN_BLUR2 { { { 1, 4, 7, 4, 1}, { 4, 16, 26, 16, 4}, @@ -369,7 +360,19 @@ static const S_FILTER FILTERS[] = { 0 }, - // Cartoon + // FILTER_INVERT_BLUR + { + { { 0, 0, 0, 0, 0}, + { 0, 0, -1, 0, 0}, + { 0, -1, 0, -1, 0}, + { 0, 0, -1, 0, 0}, + { 0, 0, 0, 0, 0} + }, + 4, + 255 + }, + + // FILTER_CARTOON { { {-1, -1, -1, -1, 0}, {-1, 0, 0, 0, 0}, @@ -381,7 +384,7 @@ static const S_FILTER FILTERS[] = { 0 }, - // Emboss + // FILTER_EMBOSS { { {-1, -1, -1, -1, 0}, {-1, -1, -1, 0, 1}, @@ -393,7 +396,7 @@ static const S_FILTER FILTERS[] = { 128 }, - // Sharpen + // FILTER_SHARPEN { { {-1, -1, -1, -1, -1}, {-1, 2, 2, 2, -1}, @@ -405,7 +408,7 @@ static const S_FILTER FILTERS[] = { 0 }, - // Melt + // FILTER_MELT { { { 4, 2, 6, 8, 1}, { 1, 2, 5, 4, 2}, @@ -417,7 +420,7 @@ static const S_FILTER FILTERS[] = { 0 }, - // Sobel Gx + // FILTER_SOBEL_GX { { { 0, 0, 0, 0, 0}, { 0, -1, 0, 1, 0}, @@ -429,7 +432,7 @@ static const S_FILTER FILTERS[] = { 0 }, - // Sobel Gy + // FILTER_SOBEL_GY { { { 1, 2, 4, 2, 1}, {-1, -1, 0, 1, 1}, @@ -439,14 +442,26 @@ static const S_FILTER FILTERS[] = { }, 1, 0 + }, + + // FILTER_BLUR_3X3 + { + { { 0, 0, 0, 0, 0}, + { 0, 1, 2, 1, 0}, + { 0, 2, 4, 2, 0}, + { 0, 1, 2, 1, 0}, + { 0, 0, 0, 0, 0}, + }, + 16, + 0 } };// Filters -//!TODO: This functions can be optimized slipting it between the edges and -// do it without use the getpixel function. -// Optimization can be done to m_pixels[ix + iy * m_width] -// but keep in mind the parallel process of the algorithm +// !TODO: This functions can be optimized slipting it between the edges and +// do it without use the getpixel function. +// Optimization can be done to m_pixels[ix + iy * m_width] +// but keep in mind the parallel process of the algorithm void CIMAGE::EfxFilter( CIMAGE *aInImg, E_FILTER aFilterType ) { S_FILTER filter = FILTERS[aFilterType]; @@ -454,10 +469,7 @@ void CIMAGE::EfxFilter( CIMAGE *aInImg, E_FILTER aFilterType ) aInImg->m_wraping = WRAP_CLAMP; m_wraping = WRAP_CLAMP; - #ifdef USE_OPENMP #pragma omp parallel for - #endif /* USE_OPENMP */ - for( int iy = 0; iy < (int)m_height; iy++) { for( int ix = 0; ix < (int)m_width; ix++ ) @@ -469,7 +481,9 @@ void CIMAGE::EfxFilter( CIMAGE *aInImg, E_FILTER aFilterType ) for( int sx = 0; sx < 5; sx++ ) { int factor = filter.kernel[sx][sy]; - unsigned char pixelv = aInImg->Getpixel( ix + sx - 2, iy + sy - 2 ); + unsigned char pixelv = aInImg->Getpixel( ix + sx - 2, + iy + sy - 2 ); + v += pixelv * factor; } } @@ -491,7 +505,8 @@ void CIMAGE::SetPixelsFromNormalizedFloat( const float * aNormalizedFloatArray ) for( unsigned int i = 0; i < m_wxh; i++ ) { int v = aNormalizedFloatArray[i] * 255; - CLAMP(v, 0, 255); + + CLAMP( v, 0, 255 ); m_pixels[i] = v; } } @@ -499,21 +514,5 @@ void CIMAGE::SetPixelsFromNormalizedFloat( const float * aNormalizedFloatArray ) void CIMAGE::SaveAsPNG( wxString aFileName ) const { - unsigned char* pixelbuffer = (unsigned char*) malloc( m_wxh * 3 ); - - wxImage image( m_width, m_height ); - - for( unsigned int i = 0; i < m_wxh; i++) - { - unsigned char v = m_pixels[i]; - // Set RGB value with all same values intensities - pixelbuffer[i * 3 + 0] = v; - pixelbuffer[i * 3 + 1] = v; - pixelbuffer[i * 3 + 2] = v; - } - - image.SetData( pixelbuffer ); - image = image.Mirror( false ); - image.SaveFile( aFileName + ".png", wxBITMAP_TYPE_PNG ); - image.Destroy(); + DBG_SaveBuffer( aFileName, m_pixels, m_width, m_height ); } diff --git a/3d-viewer/3d_rendering/cimage.h b/3d-viewer/3d_rendering/cimage.h index 572534e37d..341dc54d34 100644 --- a/3d-viewer/3d_rendering/cimage.h +++ b/3d-viewer/3d_rendering/cimage.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,8 +60,15 @@ enum E_WRAP { enum E_FILTER { FILTER_HIPASS, FILTER_GAUSSIAN_BLUR, - FILTER_INVERT_BLUR, FILTER_GAUSSIAN_BLUR2, + FILTER_INVERT_BLUR, + FILTER_CARTOON, + FILTER_EMBOSS, + FILTER_SHARPEN, + FILTER_MELT, + FILTER_SOBEL_GX, + FILTER_SOBEL_GY, + FILTER_BLUR_3X3, }; /// 5x5 Filter struct parameters @@ -139,7 +146,8 @@ public: /** * Function CopyFull - * perform a copy operation, based on operation type. The result destination is the self image class + * perform a copy operation, based on operation type. + * The result destination is the self image class * @param aImgA an image input * @param aImgB an image input * @param aOperation operation to perform diff --git a/3d-viewer/3d_rendering/cpostshader.cpp b/3d-viewer/3d_rendering/cpostshader.cpp new file mode 100644 index 0000000000..1f6b43ec88 --- /dev/null +++ b/3d-viewer/3d_rendering/cpostshader.cpp @@ -0,0 +1,213 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** + * @file cpostshader.cpp + * @brief a base class to create post shaders + */ + + +#include "cpostshader.h" +#include "buffers_debug.h" +#include + + +CPOSTSHADER::CPOSTSHADER( const CCAMERA &aCamera ) : m_camera(aCamera) +{ + m_size = SFVEC2UI( 0, 0 ); + m_normals = NULL; + m_color = NULL; + m_depth = NULL; + m_wc_hitposition = NULL; + m_shadow_att_factor = NULL; + m_tmin = FLT_MAX; + m_tmax = FLT_MIN; +} + + +CPOSTSHADER::~CPOSTSHADER() +{ + destroy_buffers(); +} + + +void CPOSTSHADER::UpdateSize( unsigned int xSize, unsigned int ySize ) +{ + destroy_buffers(); + + m_size.x = xSize; + m_size.y = ySize; + + const unsigned int n_elements = xSize * ySize; + + m_normals = new SFVEC3F[n_elements]; + m_color = new SFVEC3F[n_elements]; + m_depth = new float[n_elements]; + m_wc_hitposition = new SFVEC3F[n_elements]; + m_shadow_att_factor = new float[n_elements]; +} + + +void CPOSTSHADER::UpdateSize( const SFVEC2UI &aSize ) +{ + UpdateSize( aSize.x, aSize.y ); +} + + +void CPOSTSHADER::SetPixelData( unsigned int x, + unsigned int y, + const SFVEC3F &aNormal, + const SFVEC3F &aColor, + const SFVEC3F &aHitPosition, + float aDepth, + float aShadowAttFactor ) +{ + wxASSERT( x < m_size.x ); + wxASSERT( y < m_size.y ); + wxASSERT( (aShadowAttFactor >= 0.0f) && (aShadowAttFactor <= 1.0f) ); + + const unsigned int idx = x + y * m_size.x; + + m_normals[ idx ] = aNormal; + m_color [ idx ] = aColor; + m_depth [ idx ] = aDepth; + m_shadow_att_factor [ idx ] = aShadowAttFactor; + m_wc_hitposition[ idx ] = aHitPosition; + + + if( aDepth > FLT_EPSILON ) + { + if( aDepth < m_tmin ) + m_tmin = aDepth; + + if( aDepth > m_tmax ) + m_tmax = aDepth; + } +} + + +void CPOSTSHADER::destroy_buffers() +{ + delete m_normals; m_normals = 0; + delete m_color; m_color = 0; + delete m_depth; m_depth = 0; + delete m_shadow_att_factor; m_shadow_att_factor = 0; + delete m_wc_hitposition; m_wc_hitposition = 0; +} + + +const SFVEC3F &CPOSTSHADER::GetNormalAt( const SFVEC2F &aPos ) const +{ + return m_normals[ getIndex( aPos ) ]; +} + + +const SFVEC3F &CPOSTSHADER::GetColorAt( const SFVEC2F &aPos ) const +{ + return m_color[ getIndex( aPos ) ]; +} + + +float CPOSTSHADER::GetDepthAt( const SFVEC2F &aPos ) const +{ + return m_depth[ getIndex( aPos ) ]; +} + + +const SFVEC3F &CPOSTSHADER::GetPositionAt( const SFVEC2F &aPos ) const +{ + return m_wc_hitposition[ getIndex( aPos ) ]; +} + + +const SFVEC3F &CPOSTSHADER::GetNormalAt( const SFVEC2I &aPos ) const +{ + return m_normals[ getIndex( aPos ) ]; +} + + +const SFVEC3F &CPOSTSHADER::GetColorAt( const SFVEC2I &aPos ) const +{ + return m_color[ getIndex( aPos ) ]; +} + + +const SFVEC3F &CPOSTSHADER::GetColorAtNotProtected( const SFVEC2I &aPos ) const +{ + return m_color[ aPos.x + m_size.x * aPos.y ]; +} + + +float CPOSTSHADER::GetDepthAt( const SFVEC2I &aPos ) const +{ + return m_depth[ getIndex( aPos ) ]; +} + + +float CPOSTSHADER::GetDepthNormalizedAt( const SFVEC2I &aPos ) const +{ + const float depth = m_depth[ getIndex( aPos ) ]; + + if( depth >= m_tmin ) + return (depth - m_tmin) / (m_tmax - m_tmin); + + return 0.0f; +} + + +const SFVEC3F &CPOSTSHADER::GetPositionAt( const SFVEC2I &aPos ) const +{ + return m_wc_hitposition[ getIndex( aPos ) ]; +} + + +const float &CPOSTSHADER::GetShadowFactorAt( const SFVEC2I &aPos ) const +{ + return m_shadow_att_factor[ getIndex( aPos ) ]; +} + + +void CPOSTSHADER::DebugBuffersOutputAsImages() const +{ + DBG_SaveBuffer( "m_shadow_att_factor", m_shadow_att_factor, m_size.x, m_size.y ); + DBG_SaveBuffer( "m_color", m_color, m_size.x, m_size.y ); + DBG_SaveNormalsBuffer( "m_normals", m_normals, m_size.x, m_size.y ); + + // Normalize depth + // ///////////////////////////////////////////////////////////////////////// + float *normalizedDepth = (float*) malloc( m_size.x * m_size.y * sizeof( float ) ); + + float *normalizedDepthPTr = normalizedDepth; + + for( unsigned int iy = 0; iy < m_size.y; ++iy ) + for( unsigned int ix = 0; ix < m_size.x; ++ix ) + { + *normalizedDepthPTr = GetDepthNormalizedAt( SFVEC2I( ix, iy) ); + normalizedDepthPTr++; + } + + DBG_SaveBuffer( "m_depthNormalized", normalizedDepth, m_size.x, m_size.y ); + + free( normalizedDepth ); +} diff --git a/3d-viewer/3d_rendering/cpostshader.h b/3d-viewer/3d_rendering/cpostshader.h new file mode 100644 index 0000000000..757def235f --- /dev/null +++ b/3d-viewer/3d_rendering/cpostshader.h @@ -0,0 +1,116 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** + * @file cpostshader.h + * @brief a base class to create post shaders + */ + +#ifndef CPOSTSHADER_H +#define CPOSTSHADER_H + +#include "ccamera.h" + +class CPOSTSHADER +{ +public: + explicit CPOSTSHADER( const CCAMERA &aCamera ); + virtual ~CPOSTSHADER(); + + virtual SFVEC3F Shade( const SFVEC2I &aShaderPos ) const = 0; + + void UpdateSize( const SFVEC2UI &aSize ); + + void UpdateSize( unsigned int xSize, unsigned int ySize ); + + void InitFrame() { m_tmin = FLT_MAX; m_tmax = 0.0f; } + + void SetPixelData( unsigned int x, + unsigned int y, + const SFVEC3F &aNormal, + const SFVEC3F &aColor, + const SFVEC3F &aHitPosition, + float aDepth, + float aShadowAttFactor ); + + const SFVEC3F &GetColorAtNotProtected( const SFVEC2I &aPos ) const; + + void DebugBuffersOutputAsImages() const; + +protected: + const SFVEC3F &GetNormalAt( const SFVEC2F &aPos ) const; + const SFVEC3F &GetColorAt( const SFVEC2F &aPos ) const; + const SFVEC3F &GetPositionAt( const SFVEC2F &aPos ) const; + float GetDepthAt( const SFVEC2F &aPos ) const; + + const SFVEC3F &GetNormalAt( const SFVEC2I &aPos ) const; + const SFVEC3F &GetColorAt( const SFVEC2I &aPos ) const; + const SFVEC3F &GetPositionAt( const SFVEC2I &aPos ) const; + const float &GetShadowFactorAt( const SFVEC2I &aPos ) const; + + float GetDepthAt( const SFVEC2I &aPos ) const; + float GetDepthNormalizedAt( const SFVEC2I &aPos ) const; + float GetMaxDepth() const { return m_tmax; } + +private: + void destroy_buffers(); + + inline unsigned int getIndex( const SFVEC2F &aPos ) const + { + SFVEC2F clampPos; + + clampPos.x = glm::clamp( aPos.x, 0.0f, 1.0f ); + clampPos.y = glm::clamp( aPos.y, 0.0f, 1.0f ); + + const unsigned int idx = (unsigned int)( (float)m_size.x * clampPos.x + + (float)m_size.x * (float)m_size.y * + clampPos.y ); + + return glm::min( idx, m_size.x * m_size.y ); + } + + inline unsigned int getIndex( const SFVEC2I &aPos ) const + { + SFVEC2I clampPos; + clampPos.x = glm::clamp( aPos.x, 0, (int)m_size.x - 1 ); + clampPos.y = glm::clamp( aPos.y, 0, (int)m_size.y - 1 ); + + return (unsigned int)( clampPos.x + m_size.x * clampPos.y ); + } + +protected: + const CCAMERA &m_camera; + + SFVEC2UI m_size; + SFVEC3F *m_normals; + SFVEC3F *m_color; + SFVEC3F *m_wc_hitposition; + float *m_depth; + float *m_shadow_att_factor; + float m_tmin; + float m_tmax; +}; + + +#endif // CPOSTSHADER_H diff --git a/3d-viewer/3d_rendering/cpostshader_ssao.cpp b/3d-viewer/3d_rendering/cpostshader_ssao.cpp new file mode 100644 index 0000000000..3d86528591 --- /dev/null +++ b/3d-viewer/3d_rendering/cpostshader_ssao.cpp @@ -0,0 +1,292 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** + * @file cpostshader_ssao.cpp + * @brief Implements a post shader screen space ambient occlusion on software + */ + +#include "cpostshader_ssao.h" +#include "../3d_fastmath.h" + + +CPOSTSHADER_SSAO::CPOSTSHADER_SSAO( const CCAMERA &aCamera ) : CPOSTSHADER( aCamera ) +{ + +} +//https://github.com/OniDaito/CoffeeGL/blob/master/misc/ssao.frag + +// By martinsh +//http://www.gamedev.net/topic/556187-the-best-ssao-ive-seen/?view=findpost&p=4632208 + +float CPOSTSHADER_SSAO::aoFF( const SFVEC2I &aShaderPos, + const SFVEC3F &ddiff, + const SFVEC3F &cnorm, + int c1, + int c2 ) const +{ + float return_value = -1.0f; + + const float rd = glm::length( ddiff ); + + const float shadow_factor_at_shade_pos = GetShadowFactorAt( aShaderPos ) * 1.00f; + + float shadow_factor = shadow_factor_at_shade_pos; + + const SFVEC2I vr = aShaderPos + SFVEC2I( c1, c2 ); + + // Calculate a blured shadow based on hit-light test calculation + // ///////////////////////////////////////////////////////////////////////// + + // This limit to the zero of the function (see below) + if( (rd > FLT_EPSILON) && (rd < 1.0f) ) + { + // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLSh4Lyh4LzIrMC41KSkiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjEwMDAsIndpbmRvdyI6WyItMC41OTk1NTEyNjc1Njk4MjUiLCIxLjI3Mzk0NjE3NzQxNjI5ODgiLCItMC4xMTQzMjE1NjkyMTMwMTAwOCIsIjEuMDM4NTk5OTM1MzkzODM1MyJdfV0- + // zero: 1.0 + const float attDistFactor = 1.0f - (rd / (rd / 2.0f + 0.5f)); + + const float shadow_factor_at_sample = GetShadowFactorAt( vr ); + + shadow_factor = shadow_factor_at_sample * attDistFactor + + (1.0f - attDistFactor) * shadow_factor_at_shade_pos; + } + + //shadow_factor = (1.0f - shadow_factor) / 8.0f; + //shadow_factor = (0.66f - ( 1.0f - 1.0f / ( shadow_factor * 2.00f + 1.0f ) )) / 12.0f; + //shadow_factor = 0.50f - ( 1.0f - 1.0f / ( shadow_factor * 1.00f + 1.0f ) ); // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIwLjUtKDEuMC0xLjAvKHgqMS4wKzEuMCkpIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiLTAuNTk5NTUxMjY3NTY5ODI1IiwiMS4yNzM5NDYxNzc0MTYyOTg4IiwiLTAuMTE0MzIxNTY5MjEzMDEwMDgiLCIxLjAzODU5OTkzNTM5MzgzNTMiXX1d + //shadow_factor = 0.40f - ( 1.0f - 1.0f / ( shadow_factor * 0.67f + 1.0f ) ); // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIwLjQtKDEuMC0xLjAvKHgqMC42NysxLjApKSIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0wLjU5OTU1MTI2NzU2OTgyNSIsIjEuMjczOTQ2MTc3NDE2Mjk4OCIsIi0wLjExNDMyMTU2OTIxMzAxMDA4IiwiMS4wMzg1OTk5MzUzOTM4MzUzIl19XQ-- + //shadow_factor = 0.20f - ( 1.0f - 1.0f / ( shadow_factor * 0.25f + 1.0f ) ); // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIwLjItKDEuMC0xLjAvKHgqMC4yNSsxLjApKSIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0wLjU5OTU1MTI2NzU2OTgyNSIsIjEuMjczOTQ2MTc3NDE2Mjk4OCIsIi0wLjExNDMyMTU2OTIxMzAxMDA4IiwiMS4wMzg1OTk5MzUzOTM4MzUzIl19XQ-- + //shadow_factor = 1.0f / ( shadow_factor * 15.0f + 3.0f ) - 0.05f; // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLjAvKHgqMTUuMCszLjApLTAuMDUiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjEwMDAsIndpbmRvdyI6WyItMC41OTk1NTEyNjc1Njk4MjUiLCIxLjI3Mzk0NjE3NzQxNjI5ODgiLCItMC4xMTQzMjE1NjkyMTMwMTAwOCIsIjEuMDM4NTk5OTM1MzkzODM1MyJdfV0- + //shadow_factor = 1.0f / ( shadow_factor * 10.0f + 2.0f ) - 0.08f; // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLjAvKHgqMTAuMCsyLjApLTAuMDgiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjEwMDAsIndpbmRvdyI6WyItMC41OTk1NTEyNjc1Njk4MjUiLCIxLjI3Mzk0NjE3NzQxNjI5ODgiLCItMC4xMTQzMjE1NjkyMTMwMTAwOCIsIjEuMDM4NTk5OTM1MzkzODM1MyJdfV0- + //shadow_factor = (1.0f / ( shadow_factor * 3.0f + 1.5f ))- 0.22f; // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLjAvKHgqMy4wKzEuNSktMC4yMiIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0wLjU5OTU1MTI2NzU2OTgyNSIsIjEuMjczOTQ2MTc3NDE2Mjk4OCIsIi0wLjExNDMyMTU2OTIxMzAxMDA4IiwiMS4wMzg1OTk5MzUzOTM4MzUzIl19XQ-- + shadow_factor = (1.0f / ( shadow_factor * 1.7f + 1.9f ))- 0.28f; // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIoMS4wLyh4KjEuNysxLjkpKS0wLjI4IiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiLTAuNTk5NTUxMjY3NTY5ODI1IiwiMS4yNzM5NDYxNzc0MTYyOTg4IiwiLTAuMTE0MzIxNTY5MjEzMDEwMDgiLCIxLjAzODU5OTkzNTM5MzgzNTMiXX1d + //shadow_factor = 0.25f - shadow_factor * 0.25f; + //shadow_factor = (0.7f - shadow_factor * shadow_factor * 1.0f ) / 9.0f; + + + // Calculate the edges ambient oclusion + // ///////////////////////////////////////////////////////////////////////// + + //if( (rd > FLT_EPSILON) && (rd < 0.2f) ) // This limit to the zero of the function (see below) + //if( rd > FLT_EPSILON ) + if( (rd > FLT_EPSILON) && (rd < 5.0f) ) + { + const SFVEC3F vv = glm::normalize( ddiff ); + + // Calculate an attenuation distance factor, this was get the best + // results by experimentation + // Changing this factor will change how much shadow in relation to the + // distance of the hit it will be in shadow + + float attDistFactor = 0.0f; + + // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLSh4Lyh4LzIrMC4wMTUpKSIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0wLjAzMTM1ODM0NTk2MDIzOTAyIiwiMC4wNDUzODAxMDkzODYzOTI2MyIsIi0wLjAyMjM1MDcxNDk0NzUxMjk0IiwiMC4wMjQ4NzI5NDk4ODExODM0ODIiXX1d + // zero: 0.03 + //attDistFactor = 1.0f - (rd / (rd/2.0f + 0.015f)); + + // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLSh4Lyh4LzIrMC4xMCkpIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiLTAuOTI4NjMzODAzMTQ3MTcyOSIsIjAuOTQ0ODYzNjQxODM4OTQ5NyIsIi0wLjE1MjA2MTc2MjkxMzQxMjI4IiwiMS4wMDA4NTk3NDE2OTM0MzI4Il19XQ-- + // zero: 0.2 + + + // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIwLjgtKHgvKHgvMiswLjE1KSkiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjAsImVxIjoiLXgqMC4wNSswLjI1IiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiLTAuMjE1NzI4MDU1ODgzMjU4NjYiLCIyLjEyNjE0Mzc1MDM0OTM4ODciLCItMC4wOTM1NDA0NzY0MjczNjA0MiIsIjEuMzQ3NjExNDA0MzMxMTkyNCJdfV0- + // zero: 0.2 + attDistFactor = 0.8f - (rd / (rd / 2.0f + 0.15f)); + + attDistFactor = glm::max( attDistFactor, -rd * 0.05f + 0.25f ); + + + // Original: + // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIoMS0xL3NxcnQoMS8oeCp4KSsxKSkiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjEwMDAsIndpbmRvdyI6WyItMC42ODY3NDc3NDcxMDg0MTQyIiwiMy44ODcyMjA2MjQ0Mzk3MzM0IiwiLTAuOTA5NTYyNzcyOTMyNDk2IiwiMS45MDUxODY5OTQxNzQwNTczIl19XQ-- + // zero: inf + //const float attDistFactor = (1.0f - 1.0f / sqrt( 1.0f / ( rd * rd) + 1.0f) ); + + //float attDistFactor = 1.0f; + + const float aaFactor = (1.0f - glm::clamp( glm::dot( GetNormalAt( vr ), -vv ), 0.0f, 1.0f) ) * + glm::clamp( glm::dot( cnorm, vv ), 0.0f, 1.0f ) * attDistFactor; + + return_value = (aaFactor * 1.0f + shadow_factor ) * 1.00f; + + // Test / Debug code + //return_value = glm::max( aaFactor, shadow_factor ); + //return_value = aaFactor; + } + else + { + return_value = ( 0.0f + shadow_factor ) * 1.00f; + + // Test / Debug code + //return_value = 0.0f; + } + + // Test / Debug code + //return glm::clamp( return_value, 0.0f, 0.500f ); + //return 0.0f; + return return_value; +} + + +float CPOSTSHADER_SSAO::giFF( const SFVEC2I &aShaderPos, + const SFVEC3F &ddiff, + const SFVEC3F &cnorm, + int c1, + int c2 ) const +{ + if( (ddiff.x > FLT_EPSILON) || + (ddiff.y > FLT_EPSILON) || + (ddiff.z > FLT_EPSILON) ) + { + const SFVEC3F vv = glm::normalize( ddiff ); + const float rd = glm::length( ddiff ); + const SFVEC2I vr = aShaderPos + SFVEC2I( c1, c2 ); + + return glm::clamp( glm::dot( GetNormalAt( vr ), -vv), 0.0f, 1.0f ) * + glm::clamp( glm::dot( cnorm, vv ), 0.0f, 1.0f ) / ( rd * rd + 1.0f ); + } + + return 0.0f; +} + + +SFVEC3F CPOSTSHADER_SSAO::Shade( const SFVEC2I &aShaderPos ) const +{ + // Test source code + //return SFVEC3F( GetShadowFactorAt( aShaderPos ) ); + //return GetColorAt( aShaderPos ); + //return SFVEC3F( 1.0f - GetDepthNormalizedAt( aShaderPos ) ); + //return SFVEC3F( (1.0f / GetDepthAt( aShaderPos )) * 0.5f ); + //return SFVEC3F( 1.0f - GetDepthNormalizedAt( aShaderPos ) + + // (1.0f / GetDepthAt( aShaderPos )) * 0.5f ); + +#if 1 + float cdepth = GetDepthAt( aShaderPos ); + + if( cdepth > FLT_EPSILON ) + { + + float cNormalizedDepth = GetDepthNormalizedAt( aShaderPos ); + + wxASSERT( cNormalizedDepth <= 1.0f ); + wxASSERT( cNormalizedDepth >= 0.0f ); + + cdepth = ( (1.50f - cNormalizedDepth) + + ( 1.0f - (1.0f / (cdepth + 1.0f) ) ) * 2.5f ); + + // Test source code + //cdepth = ( (1.75f - cNormalizedDepth) + (1.0f / cdepth) * 2.0f ); + //cdepth = 1.5f - cNormalizedDepth; + //cdepth = (1.0f / cdepth) * 2.0f; + + // read current normal,position and color. + const SFVEC3F n = GetNormalAt( aShaderPos ); + const SFVEC3F p = GetPositionAt( aShaderPos ); + //const SFVEC3F col = GetColorAt( aShaderPos ); + + // initialize variables: + float ao = 0.0f; + SFVEC3F gi = SFVEC3F(0.0f); + + // This calculated the "window range" of the shader. So it will get + // more or less sparsed samples + const int incx = 3; + const int incy = 3; + + //3 rounds of 8 samples each. + for( unsigned int i = 0; i < 3; ++i ) + { + static const int mask[3] = { 0x01, 0x03, 0x03 }; + const int pw = 1 + (Fast_rand() & mask[i]); + const int ph = 1 + (Fast_rand() & mask[i]); + + const int npw = (int)((pw + incx * i) * cdepth ); + const int nph = (int)((ph + incy * i) * cdepth ); + + const SFVEC3F ddiff = GetPositionAt( aShaderPos + SFVEC2I( npw, nph ) ) - p; + const SFVEC3F ddiff2 = GetPositionAt( aShaderPos + SFVEC2I( npw,-nph ) ) - p; + const SFVEC3F ddiff3 = GetPositionAt( aShaderPos + SFVEC2I(-npw, nph ) ) - p; + const SFVEC3F ddiff4 = GetPositionAt( aShaderPos + SFVEC2I(-npw,-nph ) ) - p; + const SFVEC3F ddiff5 = GetPositionAt( aShaderPos + SFVEC2I( 0, nph ) ) - p; + const SFVEC3F ddiff6 = GetPositionAt( aShaderPos + SFVEC2I( 0,-nph ) ) - p; + const SFVEC3F ddiff7 = GetPositionAt( aShaderPos + SFVEC2I( npw, 0 ) ) - p; + const SFVEC3F ddiff8 = GetPositionAt( aShaderPos + SFVEC2I(-npw, 0 ) ) - p; + + ao+= aoFF( aShaderPos, ddiff , n, npw, nph ); + ao+= aoFF( aShaderPos, ddiff2, n, npw,-nph ); + ao+= aoFF( aShaderPos, ddiff3, n, -npw, nph ); + ao+= aoFF( aShaderPos, ddiff4, n, -npw,-nph ); + ao+= aoFF( aShaderPos, ddiff5, n, 0, nph ); + ao+= aoFF( aShaderPos, ddiff6, n, 0,-nph ); + ao+= aoFF( aShaderPos, ddiff7, n, npw, 0 ); + ao+= aoFF( aShaderPos, ddiff8, n, -npw, 0 ); + + gi+= giFF( aShaderPos, ddiff , n, npw, nph) * + giColorCurve( GetColorAt( aShaderPos + SFVEC2I( npw, nph ) ) ); + gi+= giFF( aShaderPos, ddiff2, n, npw, -nph) * + giColorCurve( GetColorAt( aShaderPos + SFVEC2I( npw,-nph ) ) ); + gi+= giFF( aShaderPos, ddiff3, n,-npw, nph) * + giColorCurve( GetColorAt( aShaderPos + SFVEC2I( -npw, nph ) ) ); + gi+= giFF( aShaderPos, ddiff4, n,-npw, -nph) * + giColorCurve( GetColorAt( aShaderPos + SFVEC2I( -npw,-nph ) ) ); + gi+= giFF( aShaderPos, ddiff5, n, 0.0f, nph) * + giColorCurve( GetColorAt( aShaderPos + SFVEC2I( 0, nph ) ) ); + gi+= giFF( aShaderPos, ddiff6, n, 0.0f,-nph) * + giColorCurve( GetColorAt( aShaderPos + SFVEC2I( 0,-nph ) ) ); + gi+= giFF( aShaderPos, ddiff7, n, npw, 0.0f) * + giColorCurve( GetColorAt( aShaderPos + SFVEC2I( npw, 0) ) ); + gi+= giFF( aShaderPos, ddiff8, n,-npw, 0.0f) * + giColorCurve( GetColorAt( aShaderPos + SFVEC2I( -npw, 0) ) ); + } + ao = (ao / 24.0f) + 0.0f; // Apply a bias for the ambient oclusion + + //ao = 1.0f - ( 1.0f / (ao * 1.0f + 1.0f) ); + gi = (gi * 5.0f / 24.0f); // Apply a bias for the global illumination + + return SFVEC3F( SFVEC3F(ao) - gi ); + + // Test source code + //return SFVEC3F( col ); + //return SFVEC3F( col - SFVEC3F(ao) + gi * 5.0f ); + //return SFVEC3F( SFVEC3F(1.0f) - SFVEC3F(ao) + gi * 5.0f ); + //return SFVEC3F(cdepth); + //return 1.0f - SFVEC3F(ao); + //return SFVEC3F(ao); + } + else + return SFVEC3F(0.0f); +#endif +} + + +SFVEC3F CPOSTSHADER_SSAO::giColorCurve( const SFVEC3F &aColor ) const +{ + const SFVEC3F vec1 = SFVEC3F(1.0f); + + // http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLjAtKDEvKHgqMS4wKzEuMCkpK3gqMC4xIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiLTAuMDYyMTg0NjE1Mzg0NjE1NTA1IiwiMS4xNDI5ODQ2MTUzODQ2MTQ2IiwiLTAuMTI3MDk5OTk5OTk5OTk5NzciLCIxLjEzMjYiXX1d + return vec1 - ( vec1 / (aColor + vec1) ) + aColor * SFVEC3F(0.10f); + + // http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLjAtKDEuMC8oeCoyLjArMS4wKSkreCowLjEiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjEwMDAsIndpbmRvdyI6WyItMC4wNjIxODQ2MTUzODQ2MTU1MDUiLCIxLjE0Mjk4NDYxNTM4NDYxNDYiLCItMC4xMjcwOTk5OTk5OTk5OTk3NyIsIjEuMTMyNiJdfV0- + //return vec1 - ( vec1 / (aColor * SFVEC3F(2.0f) + vec1) ) + aColor * SFVEC3F(0.10f); + + //return aColor; +} diff --git a/3d-viewer/3d_rendering/cpostshader_ssao.h b/3d-viewer/3d_rendering/cpostshader_ssao.h new file mode 100644 index 0000000000..f1583299c9 --- /dev/null +++ b/3d-viewer/3d_rendering/cpostshader_ssao.h @@ -0,0 +1,74 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** + * @file cpostshader_ssao.h + * @brief Implements a post shader screen space ambient occlusion on software + */ + +#ifndef CPOSTSHADER_SSAO_H +#define CPOSTSHADER_SSAO_H + + +#include "cpostshader.h" + + +class CPOSTSHADER_SSAO : public CPOSTSHADER +{ +public: + explicit CPOSTSHADER_SSAO( const CCAMERA &aCamera ); + //~CPOSTSHADER_SSAO(); + + // Imported from CPOSTSHADER + SFVEC3F Shade(const SFVEC2I &aShaderPos ) const; + +private: + SFVEC3F posFromDepth( const SFVEC2F &coord ) const; + + float ec_depth( const SFVEC2F &tc ) const; + + float aoFF( const SFVEC2I &aShaderPos, + const SFVEC3F &ddiff, + const SFVEC3F &cnorm, + int c1, + int c2) const; + + float giFF( const SFVEC2I &aShaderPos, + const SFVEC3F &ddiff, + const SFVEC3F &cnorm, + int c1, + int c2 ) const; + + /** + * @brief giColorCurve - Apply a curve transformation to the original color + * it will atenuate the bright colors (works as a gamma function): + * http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLjAtKDEvKHgqMS4wKzEuMCkpK3gqMC4zMCIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0wLjA2MjE4NDYxNTM4NDYxNTUwNSIsIjEuMTQyOTg0NjE1Mzg0NjE0NiIsIi0wLjEyNzA5OTk5OTk5OTk5OTc3IiwiMS4xMzI2Il19XQ-- + * @param aColor input color + * @return transformated color + */ + SFVEC3F giColorCurve( const SFVEC3F &aColor ) const; +}; + + +#endif // CPOSTSHADER_SSAO_H diff --git a/3d-viewer/3d_rendering/ctrack_ball.cpp b/3d-viewer/3d_rendering/ctrack_ball.cpp index 517116bab3..95ddb71245 100644 --- a/3d-viewer/3d_rendering/ctrack_ball.cpp +++ b/3d-viewer/3d_rendering/ctrack_ball.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,12 +24,13 @@ /** * @file ctrack_ball.cpp - * @brief + * @brief Implementation of a track ball camera. A track ball is placed in the + * center of the screen and rotates the camera. */ -#include "common_ogl/openGL_includes.h" #include "ctrack_ball.h" #include "trackball.h" +#include "../3d_math.h" #include @@ -38,7 +39,12 @@ CTRACK_BALL::CTRACK_BALL( float aRangeScale ) : CCAMERA( aRangeScale ) wxLogTrace( m_logTrace, wxT( "CTRACK_BALL::CTRACK_BALL" ) ); memset( m_quat, 0, sizeof( m_quat ) ); + memset( m_quat_t0, 0, sizeof( m_quat_t0 ) ); + memset( m_quat_t1, 0, sizeof( m_quat_t1 ) ); + trackball( m_quat, 0.0, 0.0, 0.0, 0.0 ); + trackball( m_quat_t0, 0.0, 0.0, 0.0, 0.0 ); + trackball( m_quat_t1, 0.0, 0.0, 0.0, 0.0 ); } @@ -50,7 +56,8 @@ void CTRACK_BALL::Drag( const wxPoint &aNewMousePosition ) // "Pass the x and y coordinates of the last and current positions of // the mouse, scaled so they are from (-1.0 ... 1.0)." - float zoom = std::min( m_zoom, 1.0f ); + const float zoom = 1.0f; + trackball( spin_quat, zoom * (2.0 * m_lastPosition.x - m_windowSize.x) / m_windowSize.x, zoom * (m_windowSize.y - 2.0 * m_lastPosition.y) / m_windowSize.y, @@ -59,7 +66,7 @@ void CTRACK_BALL::Drag( const wxPoint &aNewMousePosition ) add_quats( spin_quat, m_quat, m_quat ); - GLfloat rotationMatrix[4][4]; + float rotationMatrix[4][4]; build_rotmatrix( rotationMatrix, m_quat ); @@ -70,18 +77,21 @@ void CTRACK_BALL::Drag( const wxPoint &aNewMousePosition ) updateFrustum(); } -void CTRACK_BALL::SetBoardLookAtPos( const SFVEC3F &aBoardPos ) + +void CTRACK_BALL::SetLookAtPos( const SFVEC3F &aLookAtPos ) { - if( m_boardLookAt_pos != aBoardPos ) + if( m_lookat_pos != aLookAtPos ) { - m_boardLookAt_pos = -aBoardPos; + m_lookat_pos = aLookAtPos; updateViewMatrix(); updateFrustum(); + m_parametersChanged = true; } } + void CTRACK_BALL::Pan( const wxPoint &aNewMousePosition ) { m_parametersChanged = true; @@ -89,10 +99,11 @@ void CTRACK_BALL::Pan( const wxPoint &aNewMousePosition ) // Current zoom and an additional factor are taken into account // for the amount of panning. - float zoom = std::min( m_zoom, 1.0f ); - float panFactor = m_range_scale * zoom * (zoom * 4.0f); + const float zoom = std::min( m_zoom, 1.0f ); + const float panFactor = m_range_scale * zoom * (zoom * 4.0f); + m_camera_pos.x -= panFactor * ( m_lastPosition.x - aNewMousePosition.x ) / m_windowSize.x; - m_camera_pos.y -= panFactor * (aNewMousePosition.y - m_lastPosition.y ) / m_windowSize.y; + m_camera_pos.y -= panFactor * ( aNewMousePosition.y - m_lastPosition.y ) / m_windowSize.y; updateViewMatrix(); updateFrustum(); @@ -110,8 +121,73 @@ void CTRACK_BALL::Pan( const SFVEC3F &aDeltaOffsetInc ) } -void CTRACK_BALL::Reset() +void CTRACK_BALL::Pan_T1( const SFVEC3F &aDeltaOffsetInc ) { - m_parametersChanged = true; + m_camera_pos_t1 = m_camera_pos + aDeltaOffsetInc; } + +void CTRACK_BALL::Reset() +{ + CCAMERA::Reset(); + + memset( m_quat, 0, sizeof( m_quat ) ); + trackball( m_quat, 0.0, 0.0, 0.0, 0.0 ); +} + + +void CTRACK_BALL::Reset_T1() +{ + CCAMERA::Reset_T1(); + + memset( m_quat_t1, 0, sizeof( m_quat_t1 ) ); + trackball( m_quat_t1, 0.0, 0.0, 0.0, 0.0 ); +} + + +void CTRACK_BALL::SetT0_and_T1_current_T() +{ + CCAMERA::SetT0_and_T1_current_T(); + + memcpy( m_quat_t0, m_quat, sizeof( m_quat ) ); + memcpy( m_quat_t1, m_quat, sizeof( m_quat ) ); +} + + +void CTRACK_BALL::Interpolate( float t ) +{ + wxASSERT( t >= 0.0f ); + + // Limit t o 1.0 + t = (t > 1.0f)?1.0f:t; + + switch( m_interpolation_mode ) + { + case INTERPOLATION_BEZIER: + t = BezierBlend( t ); + break; + + case INTERPOLATION_EASING_IN_OUT: + t = QuadricEasingInOut( t ); + break; + + case INTERPOLATION_LINEAR: + default: + break; + } + + const float t0 = 1.0f - t; + + m_quat[0] = m_quat_t0[0] * t0 + m_quat_t1[0] * t; + m_quat[1] = m_quat_t0[1] * t0 + m_quat_t1[1] * t; + m_quat[2] = m_quat_t0[2] * t0 + m_quat_t1[2] * t; + m_quat[3] = m_quat_t0[3] * t0 + m_quat_t1[3] * t; + + float rotationMatrix[4][4]; + + build_rotmatrix( rotationMatrix, m_quat ); + + m_rotationMatrix = glm::make_mat4( &rotationMatrix[0][0] ); + + CCAMERA::Interpolate( t ); +} diff --git a/3d-viewer/3d_rendering/ctrack_ball.h b/3d-viewer/3d_rendering/ctrack_ball.h index 435af66a3d..12d6e3e51a 100644 --- a/3d-viewer/3d_rendering/ctrack_ball.h +++ b/3d-viewer/3d_rendering/ctrack_ball.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,7 +24,7 @@ /** * @file ctrack_ball.h - * @brief + * @brief Declaration for a track ball camera */ #ifndef CTRACK_BALL_H @@ -38,7 +38,7 @@ class CTRACK_BALL : public CCAMERA public: - CTRACK_BALL( float aRangeScale ); + explicit CTRACK_BALL( float aRangeScale ); void Drag( const wxPoint &aNewMousePosition ); @@ -46,10 +46,17 @@ class CTRACK_BALL : public CCAMERA void Pan( const SFVEC3F &aDeltaOffsetInc ); - void SetBoardLookAtPos( const SFVEC3F &aBoardPos ); + void Pan_T1( const SFVEC3F &aDeltaOffsetInc ); + + void SetLookAtPos( const SFVEC3F &aLookAtPos ); void Reset(); + void Reset_T1(); + + void SetT0_and_T1_current_T(); + + void Interpolate( float t ); private: @@ -57,7 +64,8 @@ class CTRACK_BALL : public CCAMERA * quarternion of the trackball */ double m_quat[4]; - + double m_quat_t0[4]; + double m_quat_t1[4]; }; #endif // CTRACK_BALL_H diff --git a/3d-viewer/3d_rendering/test_cases.cpp b/3d-viewer/3d_rendering/test_cases.cpp new file mode 100644 index 0000000000..f545ba13b8 --- /dev/null +++ b/3d-viewer/3d_rendering/test_cases.cpp @@ -0,0 +1,384 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file test_cases.cpp + * @brief Implements a test cases to test individual implementations classes + * @brief it run only once and only in debug build + */ + +#include +#include "3d_render_raytracing/shapes3D/cbbox.h" +#include "3d_render_raytracing/shapes2D/cbbox2d.h" +#include "3d_render_raytracing/shapes2D/cfilledcircle2d.h" +#include "3d_render_raytracing/shapes2D/croundsegment2d.h" +#include "3d_render_raytracing/shapes2D/cpolygon2d.h" +#include "3d_render_raytracing/cfrustum.h" + +//#ifdef DEBUG +#if 0 +static bool s_Run_Test_Cases = true; + +void Run_3d_viewer_test_cases() +{ + if( s_Run_Test_Cases == false) + return; + + s_Run_Test_Cases = true; + + // Test CBBOX2D + // ///////////////////////////////////////////////////////////////////////// + CBBOX2D bbox2d_A; + CBBOX2D bbox2d_B; + + // Test a not initialized box conditions + /* + wxASSERT( bbox2d_A.IsInitialized() == false ); + wxASSERT( bbox2d_A.Area() == 0.0f ); + wxASSERT( bbox2d_A.GetCenter() == SFVEC2F( 0.0f, 0.0f ) ); + wxASSERT( bbox2d_A.GetExtent() == SFVEC2F( 0.0f, 0.0f ) ); + wxASSERT( bbox2d_A.Inside( SFVEC2F( 0.0f, 0.0f ) ) == false ); + wxASSERT( bbox2d_A.Max() == SFVEC2F( 0.0f, 0.0f ) ); + wxASSERT( bbox2d_A.Min() == SFVEC2F( 0.0f, 0.0f ) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == false ); + wxASSERT( bbox2d_A.Intersects( bbox2d_A ) == false ); + wxASSERT( bbox2d_A.MaxDimension() == 0 ); + wxASSERT( bbox2d_A.Perimeter() == 0.0f ); + */ + bbox2d_A.Set( SFVEC2F(1.0f, -1.0f), SFVEC2F(-1.0f, 1.0f) ); + + wxASSERT( bbox2d_A.IsInitialized() == true ); + wxASSERT( bbox2d_A.Area() == 4.0f ); + wxASSERT( bbox2d_A.GetCenter() == SFVEC2F( 0.0f, 0.0f ) ); + wxASSERT( bbox2d_A.GetExtent() == SFVEC2F( 2.0f, 2.0f ) ); + wxASSERT( bbox2d_A.Inside( SFVEC2F( 0.0f, 0.0f ) ) == true ); + wxASSERT( bbox2d_A.Max() == SFVEC2F( 1.0f, 1.0f ) ); + wxASSERT( bbox2d_A.Min() == SFVEC2F(-1.0f,-1.0f ) ); + //wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == false ); + wxASSERT( bbox2d_A.Intersects( bbox2d_A ) == true ); + wxASSERT( bbox2d_A.MaxDimension() == 0 ); + wxASSERT( bbox2d_A.Perimeter() == 8.0f ); + + bbox2d_A.Scale( 2.0f ); + + wxASSERT( bbox2d_A.IsInitialized() == true ); + wxASSERT( bbox2d_A.Area() == 16.0f ); + wxASSERT( bbox2d_A.GetCenter() == SFVEC2F( 0.0f, 0.0f ) ); + wxASSERT( bbox2d_A.GetExtent() == SFVEC2F( 4.0f, 4.0f ) ); + wxASSERT( bbox2d_A.Inside( SFVEC2F( 0.0f, 0.0f ) ) == true ); + wxASSERT( bbox2d_A.Max() == SFVEC2F( 2.0f, 2.0f ) ); + wxASSERT( bbox2d_A.Min() == SFVEC2F(-2.0f,-2.0f ) ); + //wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == false ); + wxASSERT( bbox2d_A.Intersects( bbox2d_A ) == true ); + wxASSERT( bbox2d_A.MaxDimension() == 0 ); + wxASSERT( bbox2d_A.Perimeter() == 16.0f ); + + bbox2d_B.Set( SFVEC2F(2.1f, 2.0f), SFVEC2F( 3.0f, 3.0f) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == false ); + bbox2d_B.Set( SFVEC2F(2.0f, 2.1f), SFVEC2F( 3.0f, 3.0f) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == false ); + bbox2d_B.Set( SFVEC2F(2.1f, 2.1f), SFVEC2F( 3.0f, 3.0f) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == false ); + + bbox2d_B.Set( SFVEC2F(2.0f, 2.0f), SFVEC2F( 3.0f, 3.0f) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == true ); + + bbox2d_A.Union( bbox2d_B ); + + wxASSERT( bbox2d_A.IsInitialized() == true ); + wxASSERT( bbox2d_A.Area() == 25.0f ); + wxASSERT( bbox2d_A.GetCenter() == SFVEC2F( 0.5f, 0.5f ) ); + wxASSERT( bbox2d_A.GetExtent() == SFVEC2F( 5.0f, 5.0f ) ); + wxASSERT( bbox2d_A.Inside( SFVEC2F( 0.0f, 0.0f ) ) == true ); + wxASSERT( bbox2d_A.Inside( SFVEC2F( 3.0f, 3.0f ) ) == true ); + wxASSERT( bbox2d_A.Inside( SFVEC2F(-2.0f,-2.0f ) ) == true ); + wxASSERT( bbox2d_A.Max() == SFVEC2F( 3.0f, 3.0f ) ); + wxASSERT( bbox2d_A.Min() == SFVEC2F(-2.0f,-2.0f ) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == true ); + wxASSERT( bbox2d_A.Intersects( bbox2d_A ) == true ); + wxASSERT( bbox2d_A.MaxDimension() == 0 ); + wxASSERT( bbox2d_A.Perimeter() == 20.0f ); + + bbox2d_A.Set( SFVEC2F(-1.0f, -1.0f), SFVEC2F(1.0f, 1.0f) ); + bbox2d_B.Set( SFVEC2F(-2.0f, -2.0f), SFVEC2F(2.0f, 2.0f) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == true ); + wxASSERT( bbox2d_B.Intersects( bbox2d_A ) == true ); + + bbox2d_B.Set( SFVEC2F( 1.0f, 1.0f), SFVEC2F(1.0f, 1.0f) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == true ); + + bbox2d_B.Set( SFVEC2F( 1.1f, 1.1f), SFVEC2F(2.0f, 2.0f) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == false ); + + bbox2d_B.Set( SFVEC2F(-0.5f, -0.5f), SFVEC2F(0.5f, 0.5f) ); + wxASSERT( bbox2d_A.Intersects( bbox2d_B ) == true ); + + + // Test CFILLEDCIRCLE2D + // ///////////////////////////////////////////////////////////////////////// + + CFILLEDCIRCLE2D filledCircle2d( SFVEC2F( 2.0f, 2.0f ), 1.0f ); + + wxASSERT( filledCircle2d.IsPointInside( SFVEC2F( 2.0f, 2.0f ) ) == true ); + + wxASSERT( filledCircle2d.IsPointInside( SFVEC2F( 2.0f, 3.0f ) ) == true ); + wxASSERT( filledCircle2d.IsPointInside( SFVEC2F( 3.0f, 2.0f ) ) == true ); + wxASSERT( filledCircle2d.IsPointInside( SFVEC2F( 1.0f, 2.0f ) ) == true ); + wxASSERT( filledCircle2d.IsPointInside( SFVEC2F( 2.0f, 1.0f ) ) == true ); + + wxASSERT( filledCircle2d.IsPointInside( SFVEC2F( 2.8f, 2.8f ) ) == false ); + wxASSERT( filledCircle2d.IsPointInside( SFVEC2F( 2.6f, 2.6f ) ) == true ); + wxASSERT( filledCircle2d.IsPointInside( SFVEC2F( 1.2f, 1.2f ) ) == false ); + + bbox2d_B.Set( SFVEC2F( 0.0f, 0.0f), SFVEC2F( 4.0f, 4.0f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == true ); + + bbox2d_B.Set( SFVEC2F( 1.5f, 1.5f), SFVEC2F( 2.5f, 2.5f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == true ); + + // A box that does not intersect the sphere but still intersect the bbox of the sphere + bbox2d_B.Set( SFVEC2F( 0.0f, 0.0f), SFVEC2F( 1.2f, 1.2f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == false ); + + bbox2d_B.Set( SFVEC2F(-1.0f, -1.0f), SFVEC2F( 0.5f, 0.5f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == false ); + + bbox2d_B.Set( SFVEC2F( 0.0f, 0.0f), SFVEC2F( 2.0f, 2.0f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == true ); + + bbox2d_B.Set( SFVEC2F( 0.0f, 0.0f), SFVEC2F( 2.0f, 4.0f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == true ); + + bbox2d_B.Set( SFVEC2F( 2.0f, 0.0f), SFVEC2F( 4.0f, 4.0f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == true ); + + bbox2d_B.Set( SFVEC2F( 0.0f, 2.0f), SFVEC2F( 4.0f, 4.0f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == true ); + + bbox2d_B.Set( SFVEC2F( 0.0f, 0.0f), SFVEC2F( 4.0f, 2.0f) ); + wxASSERT( filledCircle2d.Intersects( bbox2d_B ) == true ); + + + // Test CROUNDSEGMENT2D + // ///////////////////////////////////////////////////////////////////////// + + CROUNDSEGMENT2D roundSegment2d( SFVEC2F(-1.0f, 0.0f), SFVEC2F( 1.0f, 0.0f), 2.0f); + + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 0.0f, 0.0f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 1.0f, 0.0f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F(-1.0f, 0.0f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F(-2.0f, 0.0f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 2.0f, 0.0f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 0.0f, 1.0f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 0.0f,-1.0f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 0.0f, 1.1f ) ) == false ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 0.0f,-1.1f ) ) == false ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 2.1f, 0.0f ) ) == false ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 2.1f, 0.0f ) ) == false ); + + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 1.8f, 0.8f ) ) == false ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 1.8f,-0.8f ) ) == false ); + + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F(-1.8f, 0.8f ) ) == false ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F(-1.8f,-0.8f ) ) == false ); + + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 1.6f, 0.6f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F( 1.6f,-0.6f ) ) == true ); + + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F(-1.6f, 0.6f ) ) == true ); + wxASSERT( roundSegment2d.IsPointInside( SFVEC2F(-1.6f,-0.6f ) ) == true ); + + bbox2d_A.Set( SFVEC2F(-2.0f,-1.0f), SFVEC2F( 2.0f, 1.0f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == true ); + + bbox2d_A.Set( SFVEC2F(-2.1f,-1.1f), SFVEC2F( 2.1f, 1.1f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == true ); + + bbox2d_A.Set( SFVEC2F(-1.9f,-0.9f), SFVEC2F( 1.9f, 0.9f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == true ); + + bbox2d_A.Set( SFVEC2F(-1.0f,-1.0f), SFVEC2F( 1.0f, 1.0f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == true ); + + bbox2d_A.Set( SFVEC2F(-1.0f,-0.5f), SFVEC2F( 1.0f, 0.5f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == true ); + + bbox2d_A.Set( SFVEC2F(-4.0f,-0.5f), SFVEC2F(-3.0f, 0.5f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == false ); + + bbox2d_A.Set( SFVEC2F( 1.8f, 0.8f), SFVEC2F( 2.0f, 1.0f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == false ); + + bbox2d_A.Set( SFVEC2F(-2.0f, 0.8f), SFVEC2F(-1.8f, 1.0f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == false ); + + bbox2d_A.Set( SFVEC2F(-2.0f,-1.0f), SFVEC2F(-1.8f,-0.8f) ); + wxASSERT( roundSegment2d.Intersects( bbox2d_A ) == false ); + + // Test CPOLYGON2D + // ///////////////////////////////////////////////////////////////////////// + Polygon2d_TestModule(); +#if 0 + // Test Frustum + // ///////////////////////////////////////////////////////////////////////// + { + CFRUSTUM frustum; + + SFVEC3F ori = SFVEC3F(0.0, 0.0, 0.0); + + float z = 10.0; +/* + const RAY topLeft( ori, glm::normalize( SFVEC3F(+1.0,-1.0, z) - ori ) ); + const RAY topRight( ori, glm::normalize( SFVEC3F(-1.0,-1.0, z) - ori ) ); + const RAY bottomLeft( ori, glm::normalize( SFVEC3F(+1.0,+1.0, z) - ori ) ); + const RAY bottomRight( ori, glm::normalize( SFVEC3F(-1.0,+1.0, z) - ori ) ); +*/ +/* + const RAY topLeft( ori, glm::normalize( SFVEC3F(+1.0,+1.0, z) - ori ) ); + const RAY topRight( ori, glm::normalize( SFVEC3F(-1.0,+1.0, z) - ori ) ); + const RAY bottomLeft( ori, glm::normalize( SFVEC3F(+1.0,-1.0, z) - ori ) ); + const RAY bottomRight( ori, glm::normalize( SFVEC3F(-1.0,-1.0, z) - ori ) ); +*/ +/* + const RAY topLeft( ori, glm::normalize( SFVEC3F(-1.0,-1.0, z) - ori ) ); + const RAY topRight( ori, glm::normalize( SFVEC3F(+1.0,-1.0, z) - ori ) ); + const RAY bottomLeft( ori, glm::normalize( SFVEC3F(-1.0,+1.0, z) - ori ) ); + const RAY bottomRight( ori, glm::normalize( SFVEC3F(+1.0,+1.0, z) - ori ) ); +*/ +/* + const RAY topLeft( ori, glm::normalize( SFVEC3F(-1.0,+1.0, z) - ori ) ); + const RAY topRight( ori, glm::normalize( SFVEC3F(+1.0,+1.0, z) - ori ) ); + const RAY bottomLeft( ori, glm::normalize( SFVEC3F(-1.0,-1.0, z) - ori ) ); + const RAY bottomRight( ori, glm::normalize( SFVEC3F(+1.0,-1.0, z) - ori ) ); +*/ +/* + const RAY topLeft( ori, glm::normalize( ori - SFVEC3F(+1.0,-1.0, z) ) ); + const RAY topRight( ori, glm::normalize( ori - SFVEC3F(-1.0,-1.0, z) ) ); + const RAY bottomLeft( ori, glm::normalize( ori - SFVEC3F(+1.0,+1.0, z) ) ); + const RAY bottomRight( ori, glm::normalize( ori - SFVEC3F(-1.0,+1.0, z) ) ); +*/ +/* + const RAY topLeft( ori, glm::normalize( ori - SFVEC3F(-1.0,-1.0, z) ) ); + const RAY topRight( ori, glm::normalize( ori - SFVEC3F(+1.0,-1.0, z) ) ); + const RAY bottomLeft( ori, glm::normalize( ori - SFVEC3F(-1.0,+1.0, z) ) ); + const RAY bottomRight( ori, glm::normalize( ori - SFVEC3F(+1.0,+1.0, z) ) ); +*/ +/* + const RAY topLeft( ori, glm::normalize( ori - SFVEC3F(-1.0,+1.0, z) ) ); + const RAY topRight( ori, glm::normalize( ori - SFVEC3F(+1.0,+1.0, z) ) ); + const RAY bottomLeft( ori, glm::normalize( ori - SFVEC3F(-1.0,-1.0, z) ) ); + const RAY bottomRight( ori, glm::normalize( ori - SFVEC3F(+1.0,-1.0, z) ) ); +*/ + + const RAY topLeft( ori, glm::normalize( ori - SFVEC3F(+1.0,+1.0, z) ) ); + const RAY topRight( ori, glm::normalize( ori - SFVEC3F(-1.0,+1.0, z) ) ); + const RAY bottomLeft( ori, glm::normalize( ori - SFVEC3F(+1.0,-1.0, z) ) ); + const RAY bottomRight( ori, glm::normalize( ori - SFVEC3F(-1.0,-1.0, z) ) ); + + + + frustum.GenerateFrustum( topLeft, topRight, bottomLeft, bottomRight ); + + CBBOX bbox3d; + + bbox3d.Set( SFVEC3F(-1.0f, -1.0f, z), SFVEC3F(+1.0f,+1.0f, z + 1.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F(-1.0f, -1.0f, z+z), SFVEC3F(+1.0f,+1.0f, z+z + 1.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F(-1.0f, -1.0f, 1.0f), SFVEC3F(0.0f,0.0f, 2.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F(-1.0f, -1.0f, -z-1.0f), SFVEC3F(+1.0f,+1.0f, -z) ); + wxASSERT( frustum.Intersect( bbox3d ) == false ); + + bbox3d.Set( SFVEC3F(z-1.0f, z-1.0f, 0.0), SFVEC3F(z+1.0f,z+1.0f, 1.0) ); + wxASSERT( frustum.Intersect( bbox3d ) == false ); + + bbox3d.Set( SFVEC3F(-z, -z, 0.0), SFVEC3F( -1.0f,-1.0f, 1.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == false ); + + bbox3d.Set( SFVEC3F(-z, -z, -1.0f), SFVEC3F(+z,+z, 1.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F(-z, -z, z-1), SFVEC3F(+z,+z, z+1.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F( 0.5, 0.5, 0.0), SFVEC3F( 2.0, 2.0, z) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F( 1.1, 1.0, 0.0), SFVEC3F( 2.0, 2.0, z) ); + wxASSERT( frustum.Intersect( bbox3d ) == false ); + } + { + CFRUSTUM frustum; + + float z = 10.0; + + SFVEC3F ori = SFVEC3F(0.0, 0.0, z); + + const RAY topLeft( ori, glm::normalize( SFVEC3F(-1.0, 1.0, 0.0) - ori ) ); + const RAY topRight( ori, glm::normalize( SFVEC3F(+1.0, 1.0, 0.0) - ori ) ); + const RAY bottomLeft( ori, glm::normalize( SFVEC3F(-1.0,-1.0, 0.0) - ori ) ); + const RAY bottomRight( ori, glm::normalize( SFVEC3F(+1.0,-1.0, 0.0) - ori ) ); + + frustum.GenerateFrustum( topLeft, topRight, bottomLeft, bottomRight ); + + CBBOX bbox3d; + + bbox3d.Set( SFVEC3F(-1.0f, -1.0f, -z), SFVEC3F(+1.0f,+1.0f, -z + 1.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F(-1.0f, -1.0f, -(z+z)), SFVEC3F(+1.0f,+1.0f, -(z+z + 1.0f)) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F(-1.0f, -1.0f, 1.0f), SFVEC3F(0.0f,0.0f, 2.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + // !TODO: The frustum alg is not excluse all the the situations + //bbox3d.Set( SFVEC3F(-1.0f, -1.0f, z+1.0f), SFVEC3F(+1.0f,+1.0f, +z+2.0f) ); + //wxASSERT( frustum.Intersect( bbox3d ) == false ); + + bbox3d.Set( SFVEC3F(z-1.0f, z-1.0f, 0.0), SFVEC3F(z+1.0f,z+1.0f, 1.0) ); + wxASSERT( frustum.Intersect( bbox3d ) == false ); + + bbox3d.Set( SFVEC3F(-z, -z, 0.0), SFVEC3F( -1.0f,-1.0f, 1.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == false ); + + bbox3d.Set( SFVEC3F(-z, -z, -1.0f), SFVEC3F(+z,+z, 1.0f) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F(-z, -z, -(z-1)), SFVEC3F(+z,+z, -(z+1.0f)) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F( 0.5, 0.5, 0.0), SFVEC3F( 2.0, 2.0, z) ); + wxASSERT( frustum.Intersect( bbox3d ) == true ); + + bbox3d.Set( SFVEC3F( 1.1, 1.0, 0.0), SFVEC3F( 2.0, 2.0, z) ); + wxASSERT( frustum.Intersect( bbox3d ) == false ); + } +#endif +} +#endif diff --git a/3d-viewer/3d_rendering/test_cases.h b/3d-viewer/3d_rendering/test_cases.h new file mode 100644 index 0000000000..c0aae09521 --- /dev/null +++ b/3d-viewer/3d_rendering/test_cases.h @@ -0,0 +1,38 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file test_cases.h + * @brief Implements a test cases to test individual implementations classes + * @brief it run only once and only in debug build + */ + +#ifndef TEST_CASES_H +#define TEST_CASES_H + + +void Run_3d_viewer_test_cases(); + + +#endif // TEST_CASES_H diff --git a/3d-viewer/3d_rendering/trackball.cpp b/3d-viewer/3d_rendering/trackball.cpp index b70ef438fe..741342b29c 100644 --- a/3d-viewer/3d_rendering/trackball.cpp +++ b/3d-viewer/3d_rendering/trackball.cpp @@ -50,7 +50,6 @@ * Gavin Bell */ #include -#include // used only to define GLfloat #include /* @@ -65,43 +64,39 @@ /* * Local function prototypes (not defined in trackball.h) */ -static double tb_project_to_sphere(double, double, double); -static void normalize_quat(double [4]); +static double tb_project_to_sphere( double, double, double ); +static void normalize_quat( double [4] ); -void -vzero(double *v) +void vzero( double *v ) { v[0] = 0.0; v[1] = 0.0; v[2] = 0.0; } -void -vset(double *v, double x, double y, double z) +void vset( double *v, double x, double y, double z ) { v[0] = x; v[1] = y; v[2] = z; } -void -vsub(const double *src1, const double *src2, double *dst) +void vsub( const double *src1, const double *src2, double *dst ) { dst[0] = src1[0] - src2[0]; dst[1] = src1[1] - src2[1]; dst[2] = src1[2] - src2[2]; } -void -vcopy(const double *v1, double *v2) +void vcopy( const double *v1, double *v2 ) { register int i; - for (i = 0 ; i < 3 ; i++) + + for( i = 0 ; i < 3 ; i++ ) v2[i] = v1[i]; } -void -vcross(const double *v1, const double *v2, double *cross) +void vcross( const double *v1, const double *v2, double *cross ) { double temp[3]; @@ -111,34 +106,29 @@ vcross(const double *v1, const double *v2, double *cross) vcopy(temp, cross); } -double -vlength(const double *v) +double vlength( const double *v ) { - return (double) sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + return (double) sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] ); } -void -vscale(double *v, double div) +void vscale( double *v, double div ) { v[0] *= div; v[1] *= div; v[2] *= div; } -void -vnormal(double *v) +void vnormal( double *v ) { - vscale(v, 1.0f/vlength(v)); + vscale( v, 1.0f / vlength( v ) ); } -double -vdot(const double *v1, const double *v2) +double vdot( const double *v1, const double *v2 ) { return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; } -void -vadd(const double *src1, const double *src2, double *dst) +void vadd( const double *src1, const double *src2, double *dst ) { dst[0] = src1[0] + src2[0]; dst[1] = src1[1] + src2[1]; @@ -157,17 +147,17 @@ vadd(const double *src1, const double *src2, double *dst) * It is assumed that the arguments to this routine are in the range * (-1.0 ... 1.0) */ -void -trackball(double q[4], double p1x, double p1y, double p2x, double p2y) +void trackball( double q[4], double p1x, double p1y, double p2x, double p2y ) { double a[3]; /* Axis of rotation */ double phi; /* how much to rotate about axis */ double p1[3], p2[3], d[3]; double t; - if (p1x == p2x && p1y == p2y) { + if( p1x == p2x && p1y == p2y ) + { /* Zero rotation */ - vzero(q); + vzero( q ); q[3] = 1.0; return; } @@ -176,8 +166,8 @@ trackball(double q[4], double p1x, double p1y, double p2x, double p2y) * First, figure out z-coordinates for projection of P1 and P2 to * deformed sphere */ - vset(p1, p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y)); - vset(p2, p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y)); + vset( p1, p1x, p1y, tb_project_to_sphere( TRACKBALLSIZE, p1x, p1y ) ); + vset( p2, p2x, p2y, tb_project_to_sphere( TRACKBALLSIZE, p2x, p2y ) ); /* * Now, we want the cross product of P1 and P2 @@ -187,47 +177,54 @@ trackball(double q[4], double p1x, double p1y, double p2x, double p2y) /* * Figure out how much to rotate around that axis. */ - vsub(p1, p2, d); - t = vlength(d) / (2.0f*TRACKBALLSIZE); + vsub( p1, p2, d ); + t = vlength( d ) / (2.0f * TRACKBALLSIZE); /* * Avoid problems with out-of-control values... */ - if (t > 1.0) t = 1.0; - if (t < -1.0) t = -1.0; - phi = 2.0f * (double) asin(t); + if( t > 1.0 ) + t = 1.0; - axis_to_quat(a,phi,q); + if( t < -1.0 ) + t = -1.0; + + phi = 2.0f * (double) asin( t ); + + axis_to_quat( a, phi, q ); } /* * Given an axis and angle, compute quaternion. */ -void -axis_to_quat(double a[3], double phi, double q[4]) +void axis_to_quat( double a[3], double phi, double q[4] ) { - vnormal(a); - vcopy(a, q); - vscale(q, (double) sin(phi/2.0)); - q[3] = (double) cos(phi/2.0); + vnormal( a ); + vcopy( a, q ); + vscale( q, (double) sin( phi / 2.0) ); + q[3] = (double) cos( phi / 2.0 ); } /* * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet * if we are away from the center of the sphere. */ -static double -tb_project_to_sphere(double r, double x, double y) +static double tb_project_to_sphere( double r, double x, double y ) { - double d, t, z; + double d, z; - d = (double) sqrt(x*x + y*y); - if (d < r * 0.70710678118654752440) { /* Inside sphere */ - z = (double) sqrt(r*r - d*d); - } else { /* On hyperbola */ - t = r / 1.41421356237309504880f; + d = (double) sqrt( x*x + y*y ); + + if( d < r * 0.70710678118654752440 ) + { /* Inside sphere */ + z = (double) sqrt( r*r - d*d ); + } + else + { /* On hyperbola */ + const double t = r / 1.41421356237309504880f; z = t*t / d; } + return z; } @@ -244,32 +241,33 @@ tb_project_to_sphere(double r, double x, double y) #define RENORMCOUNT 97 -void -add_quats(double q1[4], double q2[4], double dest[4]) +void add_quats( double q1[4], double q2[4], double dest[4] ) { static int count=0; double t1[4], t2[4], t3[4]; double tf[4]; - vcopy(q1,t1); - vscale(t1,q2[3]); + vcopy( q1, t1 ); + vscale( t1, q2[3] ); - vcopy(q2,t2); - vscale(t2,q1[3]); + vcopy( q2, t2 ); + vscale( t2, q1[3] ); - vcross(q2,q1,t3); - vadd(t1,t2,tf); - vadd(t3,tf,tf); - tf[3] = q1[3] * q2[3] - vdot(q1,q2); + vcross( q2, q1, t3 ); + vadd( t1, t2, tf ); + vadd( t3, tf, tf ); + + tf[3] = q1[3] * q2[3] - vdot( q1, q2 ); dest[0] = tf[0]; dest[1] = tf[1]; dest[2] = tf[2]; dest[3] = tf[3]; - if (++count > RENORMCOUNT) { + if( ++count > RENORMCOUNT ) + { count = 0; - normalize_quat(dest); + normalize_quat( dest ); } } @@ -285,34 +283,36 @@ add_quats(double q1[4], double q2[4], double dest[4]) * - Pletinckx, D., Quaternion calculus as a basic tool in computer * graphics, The Visual Computer 5, 2-13, 1989. */ -static void normalize_quat(double q[4]) +static void normalize_quat( double q[4] ) { int i; double mag; mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); - for (i = 0; i < 4; i++) q[i] /= mag; + + for( i = 0; i < 4; i++ ) + q[i] /= mag; } /* * Build a rotation matrix, given a quaternion rotation. * */ -void build_rotmatrix(GLfloat m[4][4], double q[4]) +void build_rotmatrix( float m[4][4], double q[4] ) { - m[0][0] = 1.0f - 2.0f * (q[1] * q[1] + q[2] * q[2]); - m[0][1] = 2.0f * (q[0] * q[1] - q[2] * q[3]); - m[0][2] = 2.0f * (q[2] * q[0] + q[1] * q[3]); + m[0][0] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2])); + m[0][1] = (float)(2.0 * (q[0] * q[1] - q[2] * q[3])); + m[0][2] = (float)(2.0 * (q[2] * q[0] + q[1] * q[3])); m[0][3] = 0.0f; - m[1][0] = 2.0f * (q[0] * q[1] + q[2] * q[3]); - m[1][1]= 1.0f - 2.0f * (q[2] * q[2] + q[0] * q[0]); - m[1][2] = 2.0f * (q[1] * q[2] - q[0] * q[3]); + m[1][0] = (float)(2.0 * (q[0] * q[1] + q[2] * q[3])); + m[1][1] = (float)(1.0 - 2.0f * (q[2] * q[2] + q[0] * q[0])); + m[1][2] = (float)(2.0 * (q[1] * q[2] - q[0] * q[3])); m[1][3] = 0.0f; - m[2][0] = 2.0f * (q[2] * q[0] - q[1] * q[3]); - m[2][1] = 2.0f * (q[1] * q[2] + q[0] * q[3]); - m[2][2] = 1.0f - 2.0f * (q[1] * q[1] + q[0] * q[0]); + m[2][0] = (float)(2.0 * (q[2] * q[0] - q[1] * q[3])); + m[2][1] = (float)(2.0 * (q[1] * q[2] + q[0] * q[3])); + m[2][2] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0])); m[2][3] = 0.0f; m[3][0] = 0.0f; diff --git a/3d-viewer/3d_rendering/trackball.h b/3d-viewer/3d_rendering/trackball.h index a860dff673..e6bffbeb1e 100644 --- a/3d-viewer/3d_rendering/trackball.h +++ b/3d-viewer/3d_rendering/trackball.h @@ -47,7 +47,7 @@ * The resulting rotation is returned as a quaternion rotation in the * first paramater. */ -void trackball(double q[4], double p1x, double p1y, double p2x, double p2y); +void trackball( double q[4], double p1x, double p1y, double p2x, double p2y ); /* * Given two quaternions, add them together to get a third quaternion. @@ -57,18 +57,18 @@ void trackball(double q[4], double p1x, double p1y, double p2x, double p2y); * rotation, the second and third the total rotation (which will be * over-written with the resulting new total rotation). */ -void add_quats(double *q1, double *q2, double *dest); +void add_quats( double *q1, double *q2, double *dest ); /* * A useful function, builds a rotation matrix in Matrix based on * given quaternion. */ -void build_rotmatrix(GLfloat m[4][4], double q[4]); +void build_rotmatrix( float m[4][4], double q[4] ); /* * This function computes a quaternion based on an axis (defined by * the given vector) and an angle about which to rotate. The angle is * expressed in radians. The result is put into the third argument. */ -void axis_to_quat(double a[3], double phi, double q[4]); +void axis_to_quat( double a[3], double phi, double q[4] ); diff --git a/3d-viewer/3d_struct.h b/3d-viewer/3d_struct.h deleted file mode 100644 index fc855672dc..0000000000 --- a/3d-viewer/3d_struct.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 2004 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2011 Wayne Stambaugh - * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_struct.h - */ - -#ifndef STRUCT_3D_H -#define STRUCT_3D_H - -#include -#include -#include <3d_material.h> -#include <3d_types.h> -#include "3d_rendering/3d_render_raytracing/shapes3D/cbbox.h" - - -class S3D_MASTER; -class STRUCT_3D_SHAPE; -class S3D_MODEL_PARSER; - -// Master structure for a 3D footprint shape description -class S3D_MASTER : public EDA_ITEM -{ -public: - S3DPOINT m_MatScale; ///< a scaling factor for the entire 3D footprint shape - S3DPOINT m_MatRotation; ///< a grotation for the entire 3D footprint shape - S3DPOINT m_MatPosition; ///< an offset for the entire 3D footprint shape - STRUCT_3D_SHAPE* m_3D_Drawings; ///< the list of basic shapes - S3D_MATERIAL* m_Materials; ///< the list of materiels used by the shapes - S3D_MODEL_PARSER* m_parser; ///< it store the loaded file to be rendered later - - enum FILE3D_TYPE - { - FILE3D_NONE = 0, - FILE3D_VRML, - FILE3D_IDF, - FILE3D_UNKNOWN - }; - - // Check defaults in S3D_MASTER - bool m_use_modelfile_diffuseColor; - bool m_use_modelfile_emissiveColor; - bool m_use_modelfile_specularColor; - bool m_use_modelfile_ambientIntensity; - bool m_use_modelfile_transparency; - bool m_use_modelfile_shininess; - -private: - wxString m_Shape3DName; ///< The 3D shape filename in 3D library - FILE3D_TYPE m_ShapeType; ///< Shape type based on filename extension - wxString m_Shape3DFullFilename; ///< Full file path name - wxString m_Shape3DNameExtension; ///< Extension of the shape file name - -public: - S3D_MASTER( EDA_ITEM* aParent ); - ~S3D_MASTER(); - - S3D_MASTER* Next() const { return (S3D_MASTER*) Pnext; } - S3D_MASTER* Back() const { return (S3D_MASTER*) Pback; } - - // Accessors - void Insert( S3D_MATERIAL* aMaterial ); - - void Copy( S3D_MASTER* pattern ); - - /** - * Function ReadData - * Select the parser to read the 3D data file (vrml, x3d ...) - * and build the description objects list - * @param aParser the parser that should be used to read model data and stored in - */ - int ReadData( S3D_MODEL_PARSER* aParser ); - - void Render( bool aIsRenderingJustNonTransparentObjects, - bool aIsRenderingJustTransparentObjects ); - - /** - * Function ObjectCoordsTo3DUnits - * @param aVertices = a list of 3D coordinates in shape units - * to convert to 3D canvas units, according to the - * footprint 3Dshape rotation, offset and scale parameters - */ - void ObjectCoordsTo3DUnits( std::vector< S3D_VERTEX >& aVertices ); - -#if defined(DEBUG) - void Show( int nestLevel, std::ostream& os ) const { ShowDummy( os ); } // override -#endif - - /** - * Function Is3DType - * returns true if the argument matches the type of model referred to - * by m_Shape3DName - */ - bool Is3DType( enum FILE3D_TYPE aShapeType ); - - const wxString& GetShape3DName( void ) - { - return m_Shape3DName; - } - - /** Get class name - * @return string "S3D_MASTER" - */ - virtual wxString GetClass() const - { - return wxT( "S3D_MASTER" ); - } - - /** - * Function GetShape3DFullFilename - * @return the full filename of the 3D shape, - * expanding environment variable (if any ) and/or adding default 3D path - * given by environment variable KISYS3DMOD - */ - const wxString GetShape3DFullFilename(); - - /** - * Function GetShape3DExtension - * @return the extension of the filename of the 3D shape, - */ - const wxString GetShape3DExtension(); - - /** - * Function SetShape3DName - * @param aShapeName = file name of the data file relative to the 3D shape - * - * Set the filename of the 3D shape, and depending on the file extention - * (vrl, x3d, idf ) the type of file. - */ - void SetShape3DName( const wxString& aShapeName ); - - /** - * Function getBBox Model Space Bouding Box - * @return return the model space bouding box - */ - CBBOX &getBBox(); - - /** - * Function getFastAABBox - * @return return the Axis Align Bounding Box of the other bouding boxes - */ - CBBOX &getFastAABBox(); - -private: - void calcBBox(); - CBBOX m_BBox; ///< Model oriented Bouding Box - CBBOX m_fastAABBox; ///< Axis Align Bounding Box that contain the other bounding boxes -}; - - -/* Describes a complex 3D */ -class STRUCT_3D_SHAPE : public EDA_ITEM -{ -public: - S3D_VERTEX* m_3D_Coord; - int* m_3D_CoordIndex; - int m_3D_Points; - -public: - STRUCT_3D_SHAPE( EDA_ITEM* aParent ); - ~STRUCT_3D_SHAPE(); - - STRUCT_3D_SHAPE* Next() const { return (STRUCT_3D_SHAPE*) Pnext; } - STRUCT_3D_SHAPE* Back() const { return (STRUCT_3D_SHAPE*) Pback; } - -#if defined(DEBUG) - void Show( int nestLevel, std::ostream& os ) const { ShowDummy( os ); } // override -#endif -}; - -#endif // STRUCT_3D_H diff --git a/3d-viewer/3d_toolbar.cpp b/3d-viewer/3d_toolbar.cpp deleted file mode 100644 index 29184f8b1a..0000000000 --- a/3d-viewer/3d_toolbar.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2013 Wayne Stambaugh - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_toolbar.cpp - */ - -#include - -#include <3d_viewer.h> -#include -#include -#include <3d_viewer_id.h> - - -void EDA_3D_FRAME::ReCreateMainToolbar() -{ - if( m_mainToolBar != NULL ) - { - // Simple update to the list of old files. - SetToolbars(); - return; - } - - m_mainToolBar = new wxAuiToolBar( this, ID_H_TOOLBAR, wxDefaultPosition, wxDefaultSize, - wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_HORZ_LAYOUT ); - - // Set up toolbar - m_mainToolBar->AddTool( ID_RELOAD3D_BOARD, wxEmptyString, - KiBitmap( import3d_xpm ), _( "Reload board" ) ); - - m_mainToolBar->AddSeparator(); - - m_mainToolBar->AddTool( ID_TOOL_SCREENCOPY_TOCLIBBOARD, wxEmptyString, - KiBitmap( copy_button_xpm ), - _( "Copy 3D image to clipboard" ) ); - - m_mainToolBar->AddSeparator(); - - m_mainToolBar->AddTool( ID_TOOL_SET_VISIBLE_ITEMS, wxEmptyString, - KiBitmap( read_setup_xpm ), - _( "Set display options, and some layers visibility" ) ); - m_mainToolBar->AddSeparator(); - - m_mainToolBar->AddTool( ID_ZOOM_IN, wxEmptyString, KiBitmap( zoom_in_xpm ), - _( "Zoom in" ) ); - - m_mainToolBar->AddTool( ID_ZOOM_OUT, wxEmptyString, KiBitmap( zoom_out_xpm ), - _( "Zoom out" ) ); - - m_mainToolBar->AddTool( ID_ZOOM_REDRAW, wxEmptyString, - KiBitmap( zoom_redraw_xpm ), - _( "Redraw view" ) ); - - m_mainToolBar->AddTool( ID_ZOOM_PAGE, wxEmptyString, KiBitmap( zoom_fit_in_page_xpm ), - _( "Fit in page" ) ); - - m_mainToolBar->AddSeparator(); - m_mainToolBar->AddTool( ID_ROTATE3D_X_NEG, wxEmptyString, - KiBitmap( rotate_neg_x_xpm ), - _( "Rotate X <-" ) ); - - m_mainToolBar->AddTool( ID_ROTATE3D_X_POS, wxEmptyString, - KiBitmap( rotate_pos_x_xpm ), - _( "Rotate X ->" ) ); - - m_mainToolBar->AddSeparator(); - m_mainToolBar->AddTool( ID_ROTATE3D_Y_NEG, wxEmptyString, - KiBitmap( rotate_neg_y_xpm ), - _( "Rotate Y <-" ) ); - - m_mainToolBar->AddTool( ID_ROTATE3D_Y_POS, wxEmptyString, - KiBitmap( rotate_pos_y_xpm ), - _( "Rotate Y ->" ) ); - - m_mainToolBar->AddSeparator(); - m_mainToolBar->AddTool( ID_ROTATE3D_Z_NEG, wxEmptyString, - KiBitmap( rotate_neg_z_xpm ), - _( "Rotate Z <-" ) ); - - m_mainToolBar->AddTool( ID_ROTATE3D_Z_POS, wxEmptyString, - KiBitmap( rotate_pos_z_xpm ), - _( "Rotate Z ->" ) ); - - m_mainToolBar->AddSeparator(); - m_mainToolBar->AddTool( ID_MOVE3D_LEFT, wxEmptyString, KiBitmap( left_xpm ), - _( "Move left" ) ); - - m_mainToolBar->AddTool( ID_MOVE3D_RIGHT, wxEmptyString, KiBitmap( right_xpm ), - _( "Move right" ) ); - - m_mainToolBar->AddTool( ID_MOVE3D_UP, wxEmptyString, KiBitmap( up_xpm ), - _( "Move up" ) ); - - m_mainToolBar->AddTool( ID_MOVE3D_DOWN, wxEmptyString, KiBitmap( down_xpm ), - _( "Move down" ) ); - - m_mainToolBar->AddSeparator(); - m_mainToolBar->AddTool( ID_ORTHO, wxEmptyString, KiBitmap( ortho_xpm ), - _( "Enable/Disable orthographic projection" ), - wxITEM_CHECK ); - - m_mainToolBar->Realize(); -} - - -void EDA_3D_FRAME::CreateMenuBar() -{ - wxMenuBar* menuBar = new wxMenuBar; - wxMenu* fileMenu = new wxMenu; - wxMenu* prefsMenu = new wxMenu; - - menuBar->Append( fileMenu, _( "&File" ) ); - - AddMenuItem( fileMenu, ID_MENU_SCREENCOPY_PNG, - _( "Create Image (PNG format)" ), - KiBitmap( export_xpm ) ); - AddMenuItem( fileMenu, ID_MENU_SCREENCOPY_JPEG, - _( "Create Image (JPEG format)" ), - KiBitmap( export_xpm ) ); - - fileMenu->AppendSeparator(); - AddMenuItem( fileMenu, ID_TOOL_SCREENCOPY_TOCLIBBOARD, - _( "Copy 3D Image to Clipboard" ), - KiBitmap( copy_button_xpm ) ); - - fileMenu->AppendSeparator(); - AddMenuItem( fileMenu, wxID_EXIT, - _( "&Exit" ), - KiBitmap( exit_xpm ) ); - - menuBar->Append( prefsMenu, _( "&Preferences" ) ); - - AddMenuItem( prefsMenu, ID_MENU3D_MOUSEWHEEL_PANNING, - _( "Use Touchpad to Pan" ), - KiBitmap( tools_xpm ), wxITEM_CHECK ); - - prefsMenu->AppendSeparator(); - - AddMenuItem( prefsMenu, ID_MENU3D_REALISTIC_MODE, - _( "Realistic Mode" ), - KiBitmap( use_3D_copper_thickness_xpm ), wxITEM_CHECK ); - - wxMenu * renderOptionsMenu = new wxMenu; - AddMenuItem( prefsMenu, renderOptionsMenu, ID_MENU3D_COLOR, - _( "Render Options" ), KiBitmap( tools_xpm ) ); - - AddMenuItem( renderOptionsMenu, ID_MENU3D_FL_RENDER_SHADOWS, - _( "Render Shadows" ), - KiBitmap( green_xpm ), wxITEM_CHECK ); - - AddMenuItem( renderOptionsMenu, ID_MENU3D_FL_RENDER_SHOW_HOLES_IN_ZONES, - _( "Show Holes in Zones" ), - _( "Holes inside a copper layer copper zones are shown, " - "but the calculation time is longer" ), - KiBitmap( green_xpm ), wxITEM_CHECK ); - - AddMenuItem( renderOptionsMenu, ID_MENU3D_FL_RENDER_TEXTURES, - _( "Render Textures" ), - _( "Apply a grid/cloud textures to board, solder mask and silk screen" ), - KiBitmap( green_xpm ), wxITEM_CHECK ); - - AddMenuItem( renderOptionsMenu, ID_MENU3D_FL_RENDER_SMOOTH_NORMALS, - _( "Render Smooth Normals" ), - KiBitmap( green_xpm ), wxITEM_CHECK ); - - AddMenuItem( renderOptionsMenu, ID_MENU3D_FL_RENDER_USE_MODEL_NORMALS, - _( "Use Model Normals" ), - KiBitmap( green_xpm ), wxITEM_CHECK ); - - AddMenuItem( renderOptionsMenu, ID_MENU3D_FL_RENDER_MATERIAL, - _( "Render Material Properties" ), - KiBitmap( green_xpm ), wxITEM_CHECK ); - - AddMenuItem( renderOptionsMenu, ID_MENU3D_FL_RENDER_SHOW_MODEL_BBOX, - _( "Show Model Bounding Boxes" ), - KiBitmap( green_xpm ), wxITEM_CHECK ); - - prefsMenu->AppendSeparator(); - - // Add submenu set Colors - wxMenu * setColorMenu = new wxMenu; - AddMenuItem( prefsMenu, setColorMenu, ID_MENU3D_COLOR, - _( "Choose Colors" ), KiBitmap( palette_xpm ) ); - - wxMenu * setBgColorMenu = new wxMenu; - AddMenuItem( setColorMenu, setBgColorMenu, ID_MENU3D_BGCOLOR, - _( "Background Color" ), KiBitmap( palette_xpm ) ); - - AddMenuItem( setBgColorMenu, ID_MENU3D_BGCOLOR_TOP_SELECTION, - _( "Background Top Color" ), KiBitmap( setcolor_3d_bg_xpm ) ); - - AddMenuItem( setBgColorMenu, ID_MENU3D_BGCOLOR_BOTTOM_SELECTION, - _( "Background Bottom Color" ), KiBitmap( setcolor_3d_bg_xpm ) ); - - AddMenuItem( setColorMenu, ID_MENU3D_SILKSCREEN_COLOR_SELECTION, - _( "Silkscreen Color" ), KiBitmap( setcolor_silkscreen_xpm ) ); - - AddMenuItem( setColorMenu, ID_MENU3D_SOLDERMASK_COLOR_SELECTION, - _( "Solder Mask Color" ), KiBitmap( setcolor_soldermask_xpm ) ); - - AddMenuItem( setColorMenu, ID_MENU3D_SOLDERPASTE_COLOR_SELECTION, - _( "Solder Paste Color" ), KiBitmap( setcolor_solderpaste_xpm ) ); - - AddMenuItem( setColorMenu, ID_MENU3D_COPPER_COLOR_SELECTION, - _( "Copper/Surface Finish Color" ), KiBitmap( setcolor_copper_xpm ) ); - - AddMenuItem( setColorMenu, ID_MENU3D_PCB_BODY_COLOR_SELECTION, - _( "Board Body Color" ), KiBitmap( setcolor_board_body_xpm ) ); - - AddMenuItem( prefsMenu, ID_MENU3D_AXIS_ONOFF, - _( "Show 3D &Axis" ), KiBitmap( axis3d_front_xpm ), wxITEM_CHECK ); - - // Creates grid menu - wxMenu * gridlistMenu = new wxMenu; - AddMenuItem( prefsMenu, gridlistMenu, ID_MENU3D_GRID, - _( "3D Grid" ), KiBitmap( grid_xpm ) ); - gridlistMenu->AppendCheckItem( ID_MENU3D_GRID_NOGRID, _( "No 3D Grid" ), wxEmptyString ); - gridlistMenu->AppendCheckItem( ID_MENU3D_GRID_10_MM, _( "3D Grid 10 mm" ), wxEmptyString ); - gridlistMenu->AppendCheckItem( ID_MENU3D_GRID_5_MM, _( "3D Grid 5 mm" ), wxEmptyString ); - gridlistMenu->AppendCheckItem( ID_MENU3D_GRID_2P5_MM, _( "3D Grid 2.5 mm" ), wxEmptyString ); - gridlistMenu->AppendCheckItem( ID_MENU3D_GRID_1_MM, _( "3D Grid 1 mm" ), wxEmptyString ); - - // If the grid is on, check the corresponding menuitem showing the grid size - if( IsEnabled( FL_GRID ) ) - { - gridlistMenu->Check( ID_MENU3D_GRID_10_MM, GetPrm3DVisu().m_3D_Grid == 10.0 ); - gridlistMenu->Check( ID_MENU3D_GRID_5_MM, GetPrm3DVisu().m_3D_Grid == 5.0 ); - gridlistMenu->Check( ID_MENU3D_GRID_2P5_MM, GetPrm3DVisu().m_3D_Grid == 2.5 ); - gridlistMenu->Check( ID_MENU3D_GRID_1_MM, GetPrm3DVisu().m_3D_Grid == 1.0 ); - } - else - gridlistMenu->Check( ID_MENU3D_GRID_NOGRID, true ); - - prefsMenu->AppendSeparator(); - - AddMenuItem( prefsMenu, ID_MENU3D_SHOW_BOARD_BODY, - _( "Show Board Bod&y" ), KiBitmap( use_3D_copper_thickness_xpm ), wxITEM_CHECK ); - - AddMenuItem( prefsMenu, ID_MENU3D_USE_COPPER_THICKNESS, - _( "Show Copper &Thickness" ), KiBitmap( use_3D_copper_thickness_xpm ), wxITEM_CHECK ); - - AddMenuItem( prefsMenu, ID_MENU3D_MODULE_ONOFF, - _( "Show 3D M&odels" ), KiBitmap( shape_3d_xpm ), wxITEM_CHECK ); - - AddMenuItem( prefsMenu, ID_MENU3D_ZONE_ONOFF, - _( "Show Zone &Filling" ), KiBitmap( add_zone_xpm ), wxITEM_CHECK ); - - prefsMenu->AppendSeparator(); - - wxMenu * layersMenu = new wxMenu; - AddMenuItem( prefsMenu, layersMenu, ID_MENU3D_LAYERS, - _( "Show &Layers" ), KiBitmap( tools_xpm ) ); - - AddMenuItem( layersMenu, ID_MENU3D_ADHESIVE_ONOFF, - _( "Show &Adhesive Layers" ), KiBitmap( tools_xpm ), wxITEM_CHECK ); - - AddMenuItem( layersMenu, ID_MENU3D_SILKSCREEN_ONOFF, - _( "Show &Silkscreen Layers" ), KiBitmap( add_text_xpm ), wxITEM_CHECK ); - - AddMenuItem( layersMenu, ID_MENU3D_SOLDER_MASK_ONOFF, - _( "Show Solder &Mask Layers" ), KiBitmap( pads_mask_layers_xpm ), wxITEM_CHECK ); - - AddMenuItem( layersMenu, ID_MENU3D_SOLDER_PASTE_ONOFF, - _( "Show Solder &Paste Layers" ), KiBitmap( pads_mask_layers_xpm ), wxITEM_CHECK ); - - // Other layers are not "board" layers, and are not shown in realistic mode - // These menus will be disabled in in realistic mode - AddMenuItem( layersMenu, ID_MENU3D_COMMENTS_ONOFF, - _( "Show &Comments and Drawing Layers" ), KiBitmap( edit_sheet_xpm ), wxITEM_CHECK ); - - AddMenuItem( layersMenu, ID_MENU3D_ECO_ONOFF, - _( "Show &Eco Layers" ), KiBitmap( edit_sheet_xpm ), wxITEM_CHECK ); - - SetMenuBar( menuBar ); - SetMenuBarOptionsState(); -} - - -void EDA_3D_FRAME::SetMenuBarOptionsState() -{ - wxMenuBar* menuBar = GetMenuBar(); - - if( menuBar == NULL ) - return; - - wxMenuItem* item; - // Set the state of toggle menus according to the current display options - item = menuBar->FindItem( ID_MENU3D_MOUSEWHEEL_PANNING ); - item->Check( GetPrm3DVisu().GetFlag( FL_MOUSEWHEEL_PANNING ) ); - - item = menuBar->FindItem( ID_MENU3D_REALISTIC_MODE ); - item->Check( GetPrm3DVisu().IsRealisticMode() ); - item = menuBar->FindItem( ID_MENU3D_COMMENTS_ONOFF ); - item->Enable( !GetPrm3DVisu().IsRealisticMode() ); - item = menuBar->FindItem( ID_MENU3D_ECO_ONOFF ); - item->Enable( !GetPrm3DVisu().IsRealisticMode() ); - - item = menuBar->FindItem( ID_MENU3D_FL_RENDER_SHADOWS ); - item->Check( GetPrm3DVisu().GetFlag( FL_RENDER_SHADOWS ) ); - - item = menuBar->FindItem( ID_MENU3D_FL_RENDER_SHADOWS ); - item->Check( GetPrm3DVisu().GetFlag( FL_RENDER_SHADOWS ) ); - - item = menuBar->FindItem( ID_MENU3D_FL_RENDER_SHOW_HOLES_IN_ZONES ); - item->Check( GetPrm3DVisu().GetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES ) ); - - item = menuBar->FindItem( ID_MENU3D_FL_RENDER_TEXTURES ); - item->Check( GetPrm3DVisu().GetFlag( FL_RENDER_TEXTURES ) ); - - item = menuBar->FindItem( ID_MENU3D_FL_RENDER_SMOOTH_NORMALS ); - item->Check( GetPrm3DVisu().GetFlag( FL_RENDER_SMOOTH_NORMALS ) ); - - item = menuBar->FindItem( ID_MENU3D_FL_RENDER_USE_MODEL_NORMALS ); - item->Check( GetPrm3DVisu().GetFlag( FL_RENDER_USE_MODEL_NORMALS ) ); - - item = menuBar->FindItem( ID_MENU3D_FL_RENDER_MATERIAL ); - item->Check( GetPrm3DVisu().GetFlag( FL_RENDER_MATERIAL ) ); - - item = menuBar->FindItem( ID_MENU3D_FL_RENDER_SHOW_MODEL_BBOX ); - item->Check( GetPrm3DVisu().GetFlag( FL_RENDER_SHOW_MODEL_BBOX ) ); - - item = menuBar->FindItem( ID_MENU3D_SHOW_BOARD_BODY ); - item->Check( GetPrm3DVisu().GetFlag( FL_SHOW_BOARD_BODY ) ); - - item = menuBar->FindItem( ID_MENU3D_USE_COPPER_THICKNESS ); - item->Check( GetPrm3DVisu().GetFlag( FL_USE_COPPER_THICKNESS ) ); - - item = menuBar->FindItem( ID_MENU3D_MODULE_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_MODULE ) ); - - item = menuBar->FindItem( ID_MENU3D_ZONE_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_ZONE ) ); - - item = menuBar->FindItem( ID_MENU3D_AXIS_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_AXIS ) ); - - item = menuBar->FindItem( ID_MENU3D_ADHESIVE_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_ADHESIVE ) ); - - item = menuBar->FindItem( ID_MENU3D_SILKSCREEN_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_SILKSCREEN ) ); - - item = menuBar->FindItem( ID_MENU3D_SOLDER_MASK_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_SOLDERMASK ) ); - - item = menuBar->FindItem( ID_MENU3D_SOLDER_PASTE_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_SOLDERPASTE ) ); - - item = menuBar->FindItem( ID_MENU3D_COMMENTS_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_COMMENTS ) ); - - item = menuBar->FindItem( ID_MENU3D_ECO_ONOFF ); - item->Check( GetPrm3DVisu().GetFlag( FL_ECO )); -} - - -void EDA_3D_FRAME::SetToolbars() -{ -} diff --git a/3d-viewer/3d_types.h b/3d-viewer/3d_types.h deleted file mode 100644 index d49ab66334..0000000000 --- a/3d-viewer/3d_types.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file 3d_types.h - */ - -#ifndef _3D_TYPES_H_ -#define _3D_TYPES_H_ - -#define GLM_FORCE_RADIANS -#include -#include // for IU_PER_MILS - - -/** - * @note For historical reasons the 3D modeling unit is 0.1 inch - * 1 3Dunit = 2.54 mm = 0.1 inch = 100 mils - */ -#define UNITS3D_TO_UNITSPCB (IU_PER_MILS * 100) - - -/** - * scaling factor for 3D shape offset ( S3D_MASTER::m_MatPosition member ) - * Was in inches in legacy version, and, due to a mistake, still in inches - * in .kicad_pcb files (which are using mm) - * so this scaling convert file units (inch) to 3D units (0.1 inch), only - * for S3D_MASTER::m_MatPosition parameter - */ -#define SCALE_3D_CONV 10 - - -// S3D_VERTEX manages a opengl 3D coordinate (3 float numbers: x,y,z coordinates) -// float are widely used in opengl functions. -// they are used here in coordinates which are also used in opengl functions. -#define S3D_VERTEX glm::vec3 - - -// S3DPOINT manages a set of 3 double values (x,y,z ) -// It is used for values which are not directly used in opengl functions. -// It is used in dialogs, or when reading/writing files for instance -class S3DPOINT -{ -public: - double x, y, z; - -public: - S3DPOINT() - { - x = y = z = 0.0; - } - - S3DPOINT( double px, double py, double pz) - { - x = px; - y = py; - z = pz; - } -}; - - -/** - * Class S3DPOINT_VALUE_CTRL - * displays a S3DPOINT for editing (in dialogs). A S3DPOINT is a triplet of values - * Values can be scale, rotation, offset... - */ -class S3DPOINT_VALUE_CTRL -{ -private: - wxTextCtrl* m_XValueCtrl, * m_YValueCtrl, * m_ZValueCtrl; - -public: - S3DPOINT_VALUE_CTRL( wxWindow* parent, wxBoxSizer* BoxSizer ); - - ~S3DPOINT_VALUE_CTRL(); - - /** - * Function GetValue - * @return the 3D point in internal units. - */ - S3DPOINT GetValue(); - void SetValue( S3DPOINT a3Dpoint ); - void Enable( bool enbl ); - void SetToolTip( const wxString& text ); -}; - -#endif // 3D_TYPES_H diff --git a/3d-viewer/3d_viewer.h b/3d-viewer/3d_viewer.h index 7898fe0ce9..eac693a635 100644 --- a/3d-viewer/3d_viewer.h +++ b/3d-viewer/3d_viewer.h @@ -1,9 +1,10 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * + * Copyright (C) 2015-2016 Mario Luzeiro * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2011 Wayne Stambaugh - * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,21 +25,13 @@ */ /** - * @file 3d_viewer.h + * @file 3d_viewer.h + * @brief Declaration of the eda_3d_viewer class */ -#ifndef __3D_VIEWER_H__ -#define __3D_VIEWER_H__ +#ifndef _3D_VIEWER_H_ +#define _3D_VIEWER_H_ -#include - -#if !wxUSE_GLCANVAS -#error Please build wxWidgets with Opengl support (./configure --with-opengl) -#endif - -#include <3d_struct.h> -#include -#include /// A variable name whose value holds the path of 3D shape files. /// Currently an environment variable, eventually a project variable. @@ -48,158 +41,5 @@ /// its subdirectory. #define LIB3D_FOLDER wxT( "packages3d" ) -class EDA_3D_CANVAS; -class PCB_BASE_FRAME; -class wxColourData; -#define KICAD_DEFAULT_3D_DRAWFRAME_STYLE (wxDEFAULT_FRAME_STYLE | wxWANTS_CHARS) - -#define VIEWER3D_FRAMENAME wxT( "Viewer3DFrameName" ) - - -class EDA_3D_FRAME : public KIWAY_PLAYER -{ -private: - EDA_3D_CANVAS* m_canvas; - bool m_reloadRequest; - wxString m_defaultFileName; /// Filename to propose for screenshot - - /// Tracks whether to use Orthographic or Perspective projection - bool m_ortho; - -public: - EDA_3D_FRAME( KIWAY* aKiway, PCB_BASE_FRAME* aParent, const wxString& aTitle, - long style = KICAD_DEFAULT_3D_DRAWFRAME_STYLE ); - - ~EDA_3D_FRAME(); - - PCB_BASE_FRAME* Parent() const { return (PCB_BASE_FRAME*)GetParent(); } - - BOARD* GetBoard(); - - /** - * Function ReloadRequest - * must be called when reloading data from Pcbnew is needed - * mainly after edition of the board or footprint being displayed. - * mainly for the module editor. - */ - void ReloadRequest( ) - { - m_reloadRequest = true; - } - - /** - * Function NewDisplay - * Rebuild the display list. - * must be called when 3D opengl data is modified - * @param aGlList = the list to rebuild. - * if 0 (default) all lists are rebuilt - */ - void NewDisplay( int aGlList = 0 ); - - void SetDefaultFileName(const wxString &aFn) { m_defaultFileName = aFn; } - const wxString &GetDefaultFileName() const { return m_defaultFileName; } - - /// Toggles orthographic projection on and off - void ToggleOrtho(){ m_ortho = !m_ortho ; Refresh(true);}; - - /// @return the orthographic projection flag - bool ModeIsOrtho() { return m_ortho ;}; - - /** @return the INFO3D_VISU which contains the current parameters - * to draw the 3D view og the board - */ - INFO3D_VISU& GetPrm3DVisu() const; - - /** - * @return true if aItem must be displayed - * @param aItem = an item of DISPLAY3D_FLG enum - */ - bool IsEnabled( DISPLAY3D_FLG aItem ) const; - - -private: - // Event handlers: - void Exit3DFrame( wxCommandEvent& event ); - void OnCloseWindow( wxCloseEvent& Event ); - void Process_Special_Functions( wxCommandEvent& event ); - void On3DGridSelection( wxCommandEvent& event ); - void Process_Zoom( wxCommandEvent& event ); - void OnActivate( wxActivateEvent& event ); - void Install_3D_ViewOptionDialog( wxCommandEvent& event ); - - // initialisation - void CreateMenuBar(); - void SetMenuBarOptionsState(); // Set the state of toggle menus according - // to the current display options - void ReCreateMainToolbar(); - void SetToolbars(); - - void LoadSettings( wxConfigBase* aCfg ); // overload virtual - void SaveSettings( wxConfigBase* aCfg ); // overload virtual - - // Other functions - void OnLeftClick( wxDC* DC, const wxPoint& MousePos ); - void OnRightClick( const wxPoint& MousePos, wxMenu* PopMenu ); - void OnKeyEvent( wxKeyEvent& event ); - double BestZoom(); - void RedrawActiveWindow( wxDC* DC, bool EraseBg ); - - /** - * Function Set3DColorFromUser - * Get a S3D_COLOR from a wx colour dialog - * @param aColor is the S3D_COLOR to change - * @param aTitle is the title displayed in the colordialog selector - * @param aPredefinedColors is a reference to a wxColourData - * which contains a few predefined colors - * if it is NULL, no predefined colors are used - * @return true if a new color is chosen, false if - * no change or aborted by user - */ - bool Set3DColorFromUser( S3D_COLOR &aColor, const wxString& aTitle, - wxColourData* aPredefinedColors = NULL ); - - /** - * Function Set3DSolderMaskColorFromUser - * Set the solder mask color from a set of colors - * @return true if a new color is chosen, false if - * no change or aborted by user - */ - bool Set3DSolderMaskColorFromUser(); - - /** - * Function Set3DSolderPasteColorFromUser - * Set the solder mask color from a set of colors - * @return true if a new color is chosen, false if - * no change or aborted by user - */ - bool Set3DSolderPasteColorFromUser(); - - /** - * Function Set3DCopperColorFromUser - * Set the copper color from a set of colors - * @return true if a new color is chosen, false if - * no change or aborted by user - */ - bool Set3DCopperColorFromUser(); - - /** - * Function Set3DBoardBodyBodyColorFromUser - * Set the copper color from a set of colors - * @return true if a new color is chosen, false if - * no change or aborted by user - */ - bool Set3DBoardBodyColorFromUser(); - - /** - * Function Set3DSilkScreenColorFromUser - * Set the silkscreen color from a set of colors - * @return true if a new color is chosen, false if - * no change or aborted by user - */ - bool Set3DSilkScreenColorFromUser(); - - DECLARE_EVENT_TABLE() -}; - -#endif /* __3D_VIEWER_H__ */ +#endif // _3D_VIEWER_H_ diff --git a/3d-viewer/3d_viewer/3d_toolbar.cpp b/3d-viewer/3d_viewer/3d_toolbar.cpp new file mode 100644 index 0000000000..fe71f4c570 --- /dev/null +++ b/3d-viewer/3d_viewer/3d_toolbar.cpp @@ -0,0 +1,513 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2013 Wayne Stambaugh + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file 3d_toolbar.cpp + */ + +#include + +#include +#include <3d_canvas/cinfo3d_visu.h> +#include +#include <3d_viewer_id.h> + + +void EDA_3D_VIEWER::ReCreateMainToolbar() +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::ReCreateMainToolbar" ) ); + + if( m_mainToolBar != NULL ) + { + // Simple update to the list of old files. + SetToolbars(); + return; + } + + m_mainToolBar = new wxAuiToolBar( this, ID_H_TOOLBAR, wxDefaultPosition, wxDefaultSize, + wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_HORZ_LAYOUT ); + + // Set up toolbar + m_mainToolBar->AddTool( ID_RELOAD3D_BOARD, wxEmptyString, + KiBitmap( import3d_xpm ), _( "Reload board" ) ); + + m_mainToolBar->AddSeparator(); + + m_mainToolBar->AddTool( ID_TOOL_SCREENCOPY_TOCLIBBOARD, wxEmptyString, + KiBitmap( copy_button_xpm ), + _( "Copy 3D image to clipboard" ) ); + + m_mainToolBar->AddSeparator(); + + m_mainToolBar->AddTool( ID_RENDER_CURRENT_VIEW, wxEmptyString, + KiBitmap( render_mode_xpm ), + _( "Render current view using Raytracing" ) ); + + m_mainToolBar->AddSeparator(); + + m_mainToolBar->AddTool( ID_ZOOM_IN, wxEmptyString, KiBitmap( zoom_in_xpm ), + _( "Zoom in" ) ); + + m_mainToolBar->AddTool( ID_ZOOM_OUT, wxEmptyString, KiBitmap( zoom_out_xpm ), + _( "Zoom out" ) ); + + m_mainToolBar->AddTool( ID_ZOOM_REDRAW, wxEmptyString, + KiBitmap( zoom_redraw_xpm ), + _( "Redraw view" ) ); + + m_mainToolBar->AddTool( ID_ZOOM_PAGE, wxEmptyString, KiBitmap( zoom_fit_in_page_xpm ), + _( "Fit in page" ) ); + + m_mainToolBar->AddSeparator(); + m_mainToolBar->AddTool( ID_ROTATE3D_X_NEG, wxEmptyString, + KiBitmap( rotate_neg_x_xpm ), + _( "Rotate X <-" ) ); + + m_mainToolBar->AddTool( ID_ROTATE3D_X_POS, wxEmptyString, + KiBitmap( rotate_pos_x_xpm ), + _( "Rotate X ->" ) ); + + m_mainToolBar->AddSeparator(); + m_mainToolBar->AddTool( ID_ROTATE3D_Y_NEG, wxEmptyString, + KiBitmap( rotate_neg_y_xpm ), + _( "Rotate Y <-" ) ); + + m_mainToolBar->AddTool( ID_ROTATE3D_Y_POS, wxEmptyString, + KiBitmap( rotate_pos_y_xpm ), + _( "Rotate Y ->" ) ); + + m_mainToolBar->AddSeparator(); + m_mainToolBar->AddTool( ID_ROTATE3D_Z_NEG, wxEmptyString, + KiBitmap( rotate_neg_z_xpm ), + _( "Rotate Z <-" ) ); + + m_mainToolBar->AddTool( ID_ROTATE3D_Z_POS, wxEmptyString, + KiBitmap( rotate_pos_z_xpm ), + _( "Rotate Z ->" ) ); + + m_mainToolBar->AddSeparator(); + m_mainToolBar->AddTool( ID_MOVE3D_LEFT, wxEmptyString, KiBitmap( left_xpm ), + _( "Move left" ) ); + + m_mainToolBar->AddTool( ID_MOVE3D_RIGHT, wxEmptyString, KiBitmap( right_xpm ), + _( "Move right" ) ); + + m_mainToolBar->AddTool( ID_MOVE3D_UP, wxEmptyString, KiBitmap( up_xpm ), + _( "Move up" ) ); + + m_mainToolBar->AddTool( ID_MOVE3D_DOWN, wxEmptyString, KiBitmap( down_xpm ), + _( "Move down" ) ); + + m_mainToolBar->AddSeparator(); + m_mainToolBar->AddTool( ID_ORTHO, wxEmptyString, KiBitmap( ortho_xpm ), + _( "Enable/Disable orthographic projection" ), + wxITEM_CHECK ); + + m_mainToolBar->Realize(); +} + + +void EDA_3D_VIEWER::CreateMenuBar() +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::CreateMenuBar" ) ); + + wxMenuBar* menuBar = new wxMenuBar; + wxMenu* fileMenu = new wxMenu; + wxMenu* prefsMenu = new wxMenu; + wxMenu* helpMenu = new wxMenu; + + menuBar->Append( fileMenu, _( "&File" ) ); + + AddMenuItem( fileMenu, ID_MENU_SCREENCOPY_PNG, + _( "Create Image (png format)" ), + KiBitmap( export_xpm ) ); + + AddMenuItem( fileMenu, ID_MENU_SCREENCOPY_JPEG, + _( "Create Image (jpeg format)" ), + KiBitmap( export_xpm ) ); + + fileMenu->AppendSeparator(); + AddMenuItem( fileMenu, ID_TOOL_SCREENCOPY_TOCLIBBOARD, + _( "Copy 3D Image to Clipboard" ), + KiBitmap( copy_button_xpm ) ); + + fileMenu->AppendSeparator(); + AddMenuItem( fileMenu, wxID_EXIT, + _( "&Exit" ), + KiBitmap( exit_xpm ) ); + + menuBar->Append( prefsMenu, _( "&Preferences" ) ); + + AddMenuItem( prefsMenu, ID_MENU3D_MOUSEWHEEL_PANNING, + _( "Use Touchpad to Pan" ), + KiBitmap( tools_xpm ), wxITEM_CHECK ); + + prefsMenu->AppendSeparator(); + + AddMenuItem( prefsMenu, ID_MENU3D_REALISTIC_MODE, + _( "Realistic Mode" ), + KiBitmap( use_3D_copper_thickness_xpm ), wxITEM_CHECK ); + + wxMenu * renderEngineList = new wxMenu; + AddMenuItem( prefsMenu, renderEngineList, ID_MENU3D_ENGINE, + _( "Render Engine" ), KiBitmap( render_mode_xpm ) ); + + renderEngineList->AppendRadioItem( ID_MENU3D_ENGINE_OPENGL_LEGACY, + _( "OpenGL" ), + wxEmptyString ); + + renderEngineList->AppendRadioItem( ID_MENU3D_ENGINE_RAYTRACING, + _( "Raytracing" ), + wxEmptyString ); + + renderEngineList->Check( ID_MENU3D_ENGINE_OPENGL_LEGACY, + m_settings.RenderEngineGet() == RENDER_ENGINE_OPENGL_LEGACY ); + + renderEngineList->Check( ID_MENU3D_ENGINE_RAYTRACING, + m_settings.RenderEngineGet() == RENDER_ENGINE_RAYTRACING ); + + wxMenu * renderOptionsMenu = new wxMenu; + AddMenuItem( prefsMenu, renderOptionsMenu, ID_MENU3D_FL, + _( "Render Options" ), KiBitmap( options_3drender_xpm ) ); + + AddMenuItem( renderOptionsMenu, ID_MENU3D_FL_RENDER_SHOW_HOLES_IN_ZONES, + _( "Show Holes in Zones" ), + _( "Holes inside a copper layer copper zones are shown, " + "but the calculation time is longer" ), + KiBitmap( green_xpm ), wxITEM_CHECK ); + + wxMenu * materialsList = new wxMenu; + AddMenuItem( renderOptionsMenu, materialsList, ID_MENU3D_FL_RENDER_MATERIAL, + _( "Material Properties" ), KiBitmap( color_materials_xpm ) ); + + materialsList->AppendRadioItem( ID_MENU3D_FL_RENDER_MATERIAL_MODE_NORMAL, + _( "Use all properties" ), + _( "Use all material properties from each 3D model file" ) ); + + materialsList->AppendRadioItem( ID_MENU3D_FL_RENDER_MATERIAL_MODE_DIFFUSE_ONLY, + _( "Use diffuse only" ), + _( "Use only the diffuse color property from model 3D model file " ) ); + + materialsList->AppendRadioItem( ID_MENU3D_FL_RENDER_MATERIAL_MODE_CAD_MODE, + _( "CAD color style" ), + _( "Use a CAD color style based on the diffuse color of the material" ) ); + + // Add specific preferences for OpenGL + // ///////////////////////////////////////////////////////////////////////// + wxMenu * renderOptionsMenu_OPENGL = new wxMenu; + + AddMenuItem( renderOptionsMenu, renderOptionsMenu_OPENGL, ID_MENU3D_FL_OPENGL, + _( "OpenGL Options" ), KiBitmap( tools_xpm ) ); + + AddMenuItem( renderOptionsMenu_OPENGL, ID_MENU3D_FL_OPENGL_RENDER_COPPER_THICKNESS, + _( "Show Copper Thickness" ), + _( "Shows the copper thickness on copper layers (slower loading)"), + KiBitmap( use_3D_copper_thickness_xpm ), wxITEM_CHECK ); + + AddMenuItem( renderOptionsMenu_OPENGL, ID_MENU3D_FL_OPENGL_RENDER_SHOW_MODEL_BBOX, + _( "Show Model Bounding Boxes" ), + KiBitmap( green_xpm ), wxITEM_CHECK ); + + + // Add specific preferences for Raytracing + // ///////////////////////////////////////////////////////////////////////// + wxMenu * renderOptionsMenu_RAYTRACING = new wxMenu; + AddMenuItem( renderOptionsMenu, renderOptionsMenu_RAYTRACING, ID_MENU3D_FL_RAYTRACING, + _( "Raytracing Options" ), KiBitmap( tools_xpm ) ); + + AddMenuItem( renderOptionsMenu_RAYTRACING, ID_MENU3D_FL_RAYTRACING_RENDER_SHADOWS, + _( "Render Shadows" ), + KiBitmap( green_xpm ), wxITEM_CHECK ); + + AddMenuItem( renderOptionsMenu_RAYTRACING, ID_MENU3D_FL_RAYTRACING_BACKFLOOR, + _( "Add floor" ), + _( "Adds a floor plane below the board (slow)"), + KiBitmap( green_xpm ), wxITEM_CHECK ); + + AddMenuItem( renderOptionsMenu_RAYTRACING, ID_MENU3D_FL_RAYTRACING_REFRACTIONS, + _( "Refractions" ), + _( "Render materials with refractions properties on final render (slow)"), + KiBitmap( green_xpm ), wxITEM_CHECK ); + + AddMenuItem( renderOptionsMenu_RAYTRACING, ID_MENU3D_FL_RAYTRACING_REFLECTIONS, + _( "Reflections" ), + _( "Render materials with reflections properties on final render (slow)"), + KiBitmap( green_xpm ), wxITEM_CHECK ); + + AddMenuItem( renderOptionsMenu_RAYTRACING, ID_MENU3D_FL_RAYTRACING_ANTI_ALIASING, + _( "Anti-aliasing" ), + _( "Render with improoved quality on final render (slow)"), + KiBitmap( green_xpm ), wxITEM_CHECK ); + + AddMenuItem( renderOptionsMenu_RAYTRACING, ID_MENU3D_FL_RAYTRACING_POST_PROCESSING, + _( "Post-processing" ), + _( "Apply Screen Space Ambient Occlusion and Global Illumination reflections on final render (slow)"), + KiBitmap( green_xpm ), wxITEM_CHECK ); + + prefsMenu->AppendSeparator(); + + + // Colors, axis and grid elements + // ///////////////////////////////////////////////////////////////////////// + + // Add submenu set Colors + wxMenu * setColorMenu = new wxMenu; + AddMenuItem( prefsMenu, setColorMenu, ID_MENU3D_COLOR, + _( "Choose Colors" ), KiBitmap( palette_xpm ) ); + + wxMenu * setBgColorMenu = new wxMenu; + AddMenuItem( setColorMenu, setBgColorMenu, ID_MENU3D_BGCOLOR, + _( "Background Color" ), KiBitmap( palette_xpm ) ); + + AddMenuItem( setBgColorMenu, ID_MENU3D_BGCOLOR_TOP_SELECTION, + _( "Background Top Color" ), KiBitmap( setcolor_3d_bg_xpm ) ); + + AddMenuItem( setBgColorMenu, ID_MENU3D_BGCOLOR_BOTTOM_SELECTION, + _( "Background Bottom Color" ), KiBitmap( setcolor_3d_bg_xpm ) ); + + AddMenuItem( setColorMenu, ID_MENU3D_SILKSCREEN_COLOR_SELECTION, + _( "Silkscreen Color" ), KiBitmap( setcolor_silkscreen_xpm ) ); + + AddMenuItem( setColorMenu, ID_MENU3D_SOLDERMASK_COLOR_SELECTION, + _( "Solder Mask Color" ), KiBitmap( setcolor_soldermask_xpm ) ); + + AddMenuItem( setColorMenu, ID_MENU3D_SOLDERPASTE_COLOR_SELECTION, + _( "Solder Paste Color" ), KiBitmap( setcolor_solderpaste_xpm ) ); + + AddMenuItem( setColorMenu, ID_MENU3D_COPPER_COLOR_SELECTION, + _( "Copper/Surface Finish Color" ), KiBitmap( setcolor_copper_xpm ) ); + + AddMenuItem( setColorMenu, ID_MENU3D_PCB_BODY_COLOR_SELECTION, + _( "Board Body Color" ), KiBitmap( setcolor_board_body_xpm ) ); + + AddMenuItem( prefsMenu, ID_MENU3D_AXIS_ONOFF, + _( "Show 3D &Axis" ), KiBitmap( axis3d_front_xpm ), wxITEM_CHECK ); + + + // Creates grid menu + // ///////////////////////////////////////////////////////////////////////// + + wxMenu * gridlistMenu = new wxMenu; + AddMenuItem( prefsMenu, gridlistMenu, ID_MENU3D_GRID, + _( "3D Grid" ), KiBitmap( grid_xpm ) ); + gridlistMenu->AppendRadioItem( ID_MENU3D_GRID_NOGRID, _( "No 3D Grid" ), wxEmptyString ); + gridlistMenu->AppendRadioItem( ID_MENU3D_GRID_10_MM, _( "3D Grid 10 mm" ), wxEmptyString ); + gridlistMenu->AppendRadioItem( ID_MENU3D_GRID_5_MM, _( "3D Grid 5 mm" ), wxEmptyString ); + gridlistMenu->AppendRadioItem( ID_MENU3D_GRID_2P5_MM, _( "3D Grid 2.5 mm" ), wxEmptyString ); + gridlistMenu->AppendRadioItem( ID_MENU3D_GRID_1_MM, _( "3D Grid 1 mm" ), wxEmptyString ); + + // If the grid is on, check the corresponding menuitem showing the grid size + if( m_settings.GridGet() != GRID3D_NONE ) + { + gridlistMenu->Check( ID_MENU3D_GRID_10_MM, m_settings.GridGet() == GRID3D_10MM ); + gridlistMenu->Check( ID_MENU3D_GRID_5_MM, m_settings.GridGet() == GRID3D_5MM ); + gridlistMenu->Check( ID_MENU3D_GRID_2P5_MM, m_settings.GridGet() == GRID3D_2P5MM ); + gridlistMenu->Check( ID_MENU3D_GRID_1_MM, m_settings.GridGet() == GRID3D_1MM ); + } + else + gridlistMenu->Check( ID_MENU3D_GRID_NOGRID, true ); + + + // Display elements options + // ///////////////////////////////////////////////////////////////////////// + prefsMenu->AppendSeparator(); + + AddMenuItem( prefsMenu, ID_MENU3D_SHOW_BOARD_BODY, + _( "Show Board Bod&y" ), KiBitmap( use_3D_copper_thickness_xpm ), wxITEM_CHECK ); + + AddMenuItem( prefsMenu, ID_MENU3D_ZONE_ONOFF, + _( "Show Zone &Filling" ), KiBitmap( add_zone_xpm ), wxITEM_CHECK ); + + wxMenu * moduleAttributes = new wxMenu; + AddMenuItem( prefsMenu, moduleAttributes, ID_MENU3D_MODULE_ONOFF, + _( "Show 3D M&odels" ), KiBitmap( shape_3d_xpm ) ); + moduleAttributes->AppendCheckItem( ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_NORMAL, + _( "Normal" ), + _( "Footprint Properties -> Attributes -> Normal (eg: THT parts)" ) ); + + moduleAttributes->AppendCheckItem( ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_NORMAL_INSERT, + _( "Normal+Insert" ), + _( "Footprint Properties -> Attributes -> Normal+Insert (eg: SMD parts)" ) ); + + moduleAttributes->AppendCheckItem( ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_VIRTUAL, + _( "Virtual" ), + _( "Footprint Properties -> Attributes -> Virtual (eg: edge connectors, test points, mechanical parts)" ) ); + + // Layer options + // ///////////////////////////////////////////////////////////////////////// + prefsMenu->AppendSeparator(); + + wxMenu * layersMenu = new wxMenu; + AddMenuItem( prefsMenu, layersMenu, ID_MENU3D_LAYERS, + _( "Show &Layers" ), KiBitmap( tools_xpm ) ); + + AddMenuItem( layersMenu, ID_MENU3D_ADHESIVE_ONOFF, + _( "Show &Adhesive Layers" ), KiBitmap( tools_xpm ), wxITEM_CHECK ); + + AddMenuItem( layersMenu, ID_MENU3D_SILKSCREEN_ONOFF, + _( "Show &Silkscreen Layers" ), KiBitmap( add_text_xpm ), wxITEM_CHECK ); + + AddMenuItem( layersMenu, ID_MENU3D_SOLDER_MASK_ONOFF, + _( "Show Solder &Mask Layers" ), KiBitmap( pads_mask_layers_xpm ), wxITEM_CHECK ); + + AddMenuItem( layersMenu, ID_MENU3D_SOLDER_PASTE_ONOFF, + _( "Show Solder &Paste Layers" ), KiBitmap( pads_mask_layers_xpm ), wxITEM_CHECK ); + + // Other layers are not "board" layers, and are not shown in realistic mode + // These menus will be disabled in in realistic mode + AddMenuItem( layersMenu, ID_MENU3D_COMMENTS_ONOFF, + _( "Show &Comments and Drawings Layers" ), KiBitmap( edit_sheet_xpm ), wxITEM_CHECK ); + + AddMenuItem( layersMenu, ID_MENU3D_ECO_ONOFF, + _( "Show &Eco Layers" ), KiBitmap( edit_sheet_xpm ), wxITEM_CHECK ); + + // Reset options + // ///////////////////////////////////////////////////////////////////////// + prefsMenu->AppendSeparator(); + + AddMenuItem( prefsMenu, ID_MENU3D_RESET_DEFAULTS, + _( "Reset to default settings" ), + KiBitmap( tools_xpm ) ); + + // Help menu + // ///////////////////////////////////////////////////////////////////////// + menuBar->Append( helpMenu, _( "&Help" ) ); + + AddMenuItem( helpMenu, ID_MENU3D_HELP_HOTKEY_SHOW_CURRENT_LIST, + _( "&List Hotkeys" ), + _( "Displays the current hotkeys list and corresponding commands" ), + KiBitmap( hotkeys_xpm ) ); + + SetMenuBar( menuBar ); + SetMenuBarOptionsState(); +} + + +void EDA_3D_VIEWER::SetMenuBarOptionsState() +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::SetMenuBarOptionsState" ) ); + + wxMenuBar* menuBar = GetMenuBar(); + + if( menuBar == NULL ) + { + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::SetMenuBarOptionsState menuBar == NULL" ) ); + + return; + } + + wxMenuItem* item; + // Set the state of toggle menus according to the current display options + item = menuBar->FindItem( ID_MENU3D_MOUSEWHEEL_PANNING ); + item->Check( m_settings.GetFlag( FL_MOUSEWHEEL_PANNING ) ); + + item = menuBar->FindItem( ID_MENU3D_REALISTIC_MODE ); + item->Check( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ); + item = menuBar->FindItem( ID_MENU3D_COMMENTS_ONOFF ); + item->Enable( !m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ); + item = menuBar->FindItem( ID_MENU3D_ECO_ONOFF ); + item->Enable( !m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ); + + item = menuBar->FindItem( ID_MENU3D_FL_RENDER_SHOW_HOLES_IN_ZONES ); + item->Check( m_settings.GetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES ) ); + + item = menuBar->FindItem( ID_MENU3D_FL_RENDER_MATERIAL_MODE_NORMAL ); + item->Check( m_settings.MaterialModeGet() == MATERIAL_MODE_NORMAL ); + + item = menuBar->FindItem( ID_MENU3D_FL_RENDER_MATERIAL_MODE_DIFFUSE_ONLY ); + item->Check( m_settings.MaterialModeGet() == MATERIAL_MODE_DIFFUSE_ONLY ); + + item = menuBar->FindItem( ID_MENU3D_FL_RENDER_MATERIAL_MODE_CAD_MODE ); + item->Check( m_settings.MaterialModeGet() == MATERIAL_MODE_CAD_MODE ); + + // OpenGL + item = menuBar->FindItem( ID_MENU3D_FL_OPENGL_RENDER_COPPER_THICKNESS ); + item->Check( m_settings.GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) ); + + item = menuBar->FindItem( ID_MENU3D_FL_OPENGL_RENDER_SHOW_MODEL_BBOX ); + item->Check( m_settings.GetFlag( FL_RENDER_OPENGL_SHOW_MODEL_BBOX ) ); + + // Raytracing + item = menuBar->FindItem( ID_MENU3D_FL_RAYTRACING_RENDER_SHADOWS ); + item->Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_SHADOWS ) ); + + item = menuBar->FindItem( ID_MENU3D_FL_RAYTRACING_BACKFLOOR ); + item->Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_BACKFLOOR ) ); + + item = menuBar->FindItem( ID_MENU3D_FL_RAYTRACING_REFRACTIONS ); + item->Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_REFRACTIONS ) ); + + item = menuBar->FindItem( ID_MENU3D_FL_RAYTRACING_REFLECTIONS ); + item->Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_REFLECTIONS ) ); + + item = menuBar->FindItem( ID_MENU3D_FL_RAYTRACING_POST_PROCESSING ); + item->Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ); + + item = menuBar->FindItem( ID_MENU3D_FL_RAYTRACING_ANTI_ALIASING ); + item->Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) ); + + + item = menuBar->FindItem( ID_MENU3D_SHOW_BOARD_BODY ); + item->Check( m_settings.GetFlag( FL_SHOW_BOARD_BODY ) ); + + item = menuBar->FindItem( ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_NORMAL ); + item->Check( m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL ) ); + + item = menuBar->FindItem( ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_NORMAL_INSERT ); + item->Check( m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT ) ); + + item = menuBar->FindItem( ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_VIRTUAL ); + item->Check( m_settings.GetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL ) ); + + item = menuBar->FindItem( ID_MENU3D_ZONE_ONOFF ); + item->Check( m_settings.GetFlag( FL_ZONE ) ); + + item = menuBar->FindItem( ID_MENU3D_AXIS_ONOFF ); + item->Check( m_settings.GetFlag( FL_AXIS ) ); + + item = menuBar->FindItem( ID_MENU3D_ADHESIVE_ONOFF ); + item->Check( m_settings.GetFlag( FL_ADHESIVE ) ); + + item = menuBar->FindItem( ID_MENU3D_SILKSCREEN_ONOFF ); + item->Check( m_settings.GetFlag( FL_SILKSCREEN ) ); + + item = menuBar->FindItem( ID_MENU3D_SOLDER_MASK_ONOFF ); + item->Check( m_settings.GetFlag( FL_SOLDERMASK ) ); + + item = menuBar->FindItem( ID_MENU3D_SOLDER_PASTE_ONOFF ); + item->Check( m_settings.GetFlag( FL_SOLDERPASTE ) ); + + item = menuBar->FindItem( ID_MENU3D_COMMENTS_ONOFF ); + item->Check( m_settings.GetFlag( FL_COMMENTS ) ); + + item = menuBar->FindItem( ID_MENU3D_ECO_ONOFF ); + item->Check( m_settings.GetFlag( FL_ECO )); +} + + +void EDA_3D_VIEWER::SetToolbars() +{ +} diff --git a/3d-viewer/dialogs/dialog_3D_view_option.cpp b/3d-viewer/3d_viewer/dialogs/dialog_3D_view_option.cpp similarity index 75% rename from 3d-viewer/dialogs/dialog_3D_view_option.cpp rename to 3d-viewer/3d_viewer/dialogs/dialog_3D_view_option.cpp index a355a5747c..39c143a57d 100644 --- a/3d-viewer/dialogs/dialog_3D_view_option.cpp +++ b/3d-viewer/3d_viewer/dialogs/dialog_3D_view_option.cpp @@ -23,17 +23,17 @@ */ #include "dialog_3D_view_option_base.h" -#include <3d_viewer.h> -#include +#include <3d_viewer/eda_3d_viewer.h> +#include <3d_canvas/cinfo3d_visu.h> class DIALOG_3D_VIEW_OPTIONS : public DIALOG_3D_VIEW_OPTIONS_BASE { public: - DIALOG_3D_VIEW_OPTIONS( EDA_3D_FRAME* parent ); + explicit DIALOG_3D_VIEW_OPTIONS( EDA_3D_VIEWER* parent ); private: - EDA_3D_FRAME* m_parent; - INFO3D_VISU & m_3Dprms; + EDA_3D_VIEWER* m_parent; + CINFO3D_VISU & m_3Dprms; void initDialog(); @@ -41,11 +41,10 @@ private: void OnShowAllClick( wxCommandEvent& event ); void OnShowNoneClick( wxCommandEvent& event ); void OnOKClick( wxCommandEvent& event ); - void OnCheckRealisticMode( wxCommandEvent& event ); }; -void EDA_3D_FRAME::Install_3D_ViewOptionDialog( wxCommandEvent& event ) +void EDA_3D_VIEWER::Install3DViewOptionDialog( wxCommandEvent& event ) { DIALOG_3D_VIEW_OPTIONS dlg( this ); @@ -57,8 +56,8 @@ void EDA_3D_FRAME::Install_3D_ViewOptionDialog( wxCommandEvent& event ) } -DIALOG_3D_VIEW_OPTIONS::DIALOG_3D_VIEW_OPTIONS( EDA_3D_FRAME* parent ) - :DIALOG_3D_VIEW_OPTIONS_BASE( parent ), m_3Dprms( g_Parm_3D_Visu ) +DIALOG_3D_VIEW_OPTIONS::DIALOG_3D_VIEW_OPTIONS( EDA_3D_VIEWER* parent ) + :DIALOG_3D_VIEW_OPTIONS_BASE( parent ), m_3Dprms( parent->GetSettings() ) { m_parent = parent; @@ -73,7 +72,6 @@ DIALOG_3D_VIEW_OPTIONS::DIALOG_3D_VIEW_OPTIONS( EDA_3D_FRAME* parent ) void DIALOG_3D_VIEW_OPTIONS::initDialog() { - m_bitmapRealisticMode->SetBitmap( KiBitmap( use_3D_copper_thickness_xpm ) ); m_bitmapCuThickness->SetBitmap( KiBitmap( use_3D_copper_thickness_xpm ) ); m_bitmap3Dshapes->SetBitmap( KiBitmap( shape_3d_xpm ) ); m_bitmapAreas->SetBitmap( KiBitmap( add_zone_xpm ) ); @@ -85,34 +83,21 @@ void DIALOG_3D_VIEW_OPTIONS::initDialog() m_bitmapECO->SetBitmap( KiBitmap( edit_sheet_xpm ) ); // Check/uncheck checkboxes - m_checkBoxRealisticMode->SetValue( m_3Dprms.GetFlag( FL_USE_REALISTIC_MODE ) ); - m_checkBoxCuThickness->SetValue( m_3Dprms.GetFlag( FL_USE_COPPER_THICKNESS ) ); - m_checkBox3Dshapes->SetValue( m_3Dprms.GetFlag( FL_MODULE ) ); m_checkBoxAreas->SetValue( m_3Dprms.GetFlag( FL_ZONE ) ); m_checkBoxSilkscreen->SetValue( m_3Dprms.GetFlag( FL_SILKSCREEN ) ); m_checkBoxSolderMask->SetValue( m_3Dprms.GetFlag( FL_SOLDERMASK ) ); m_checkBoxSolderpaste->SetValue( m_3Dprms.GetFlag( FL_SOLDERPASTE ) ); m_checkBoxAdhesive->SetValue( m_3Dprms.GetFlag( FL_ADHESIVE ) ); - m_checkBoxComments->SetValue( m_3Dprms.GetFlag( FL_COMMENTS ) ); m_checkBoxECO->SetValue( m_3Dprms.GetFlag( FL_ECO ) ); - bool enable = !m_3Dprms.GetFlag( FL_USE_REALISTIC_MODE ); - m_checkBoxComments->Enable( enable ); - m_checkBoxECO->Enable( enable ); } -void DIALOG_3D_VIEW_OPTIONS::OnCheckRealisticMode( wxCommandEvent& event ) -{ - bool enable = !m_checkBoxRealisticMode->GetValue(); - m_checkBoxComments->Enable( enable ); - m_checkBoxECO->Enable( enable ); -} void DIALOG_3D_VIEW_OPTIONS::OnShowAllClick( wxCommandEvent& event ) { bool state = true; m_checkBoxCuThickness->SetValue( state ); - m_checkBox3Dshapes->SetValue( state ); + //m_checkBox3Dshapes->SetValue( state ); m_checkBoxAreas->SetValue( state ); m_checkBoxSilkscreen->SetValue( state ); m_checkBoxSolderMask->SetValue( state ); @@ -127,7 +112,7 @@ void DIALOG_3D_VIEW_OPTIONS::OnShowNoneClick( wxCommandEvent& event ) { bool state = false; m_checkBoxCuThickness->SetValue( state ); - m_checkBox3Dshapes->SetValue( state ); + //m_checkBox3Dshapes->SetValue( state ); m_checkBoxAreas->SetValue( state ); m_checkBoxSilkscreen->SetValue( state ); m_checkBoxSolderMask->SetValue( state ); @@ -140,9 +125,6 @@ void DIALOG_3D_VIEW_OPTIONS::OnShowNoneClick( wxCommandEvent& event ) void DIALOG_3D_VIEW_OPTIONS::OnOKClick( wxCommandEvent& event ) { - m_3Dprms.SetFlag( FL_USE_REALISTIC_MODE, m_checkBoxRealisticMode->GetValue() ); - m_3Dprms.SetFlag( FL_USE_COPPER_THICKNESS, m_checkBoxCuThickness->GetValue() ); - m_3Dprms.SetFlag( FL_MODULE, m_checkBox3Dshapes->GetValue() ); m_3Dprms.SetFlag( FL_ZONE, m_checkBoxAreas->GetValue() ); m_3Dprms.SetFlag( FL_SILKSCREEN, m_checkBoxSilkscreen->GetValue() ); m_3Dprms.SetFlag( FL_SOLDERMASK, m_checkBoxSolderMask->GetValue() ); diff --git a/3d-viewer/dialogs/dialog_3D_view_option_base.cpp b/3d-viewer/3d_viewer/dialogs/dialog_3D_view_option_base.cpp similarity index 100% rename from 3d-viewer/dialogs/dialog_3D_view_option_base.cpp rename to 3d-viewer/3d_viewer/dialogs/dialog_3D_view_option_base.cpp diff --git a/3d-viewer/dialogs/dialog_3D_view_option_base.fbp b/3d-viewer/3d_viewer/dialogs/dialog_3D_view_option_base.fbp similarity index 100% rename from 3d-viewer/dialogs/dialog_3D_view_option_base.fbp rename to 3d-viewer/3d_viewer/dialogs/dialog_3D_view_option_base.fbp diff --git a/3d-viewer/dialogs/dialog_3D_view_option_base.h b/3d-viewer/3d_viewer/dialogs/dialog_3D_view_option_base.h similarity index 100% rename from 3d-viewer/dialogs/dialog_3D_view_option_base.h rename to 3d-viewer/3d_viewer/dialogs/dialog_3D_view_option_base.h diff --git a/3d-viewer/3d_viewer/eda_3d_viewer.cpp b/3d-viewer/3d_viewer/eda_3d_viewer.cpp new file mode 100644 index 0000000000..462c70b037 --- /dev/null +++ b/3d-viewer/3d_viewer/eda_3d_viewer.cpp @@ -0,0 +1,1227 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file eda_3d_viewer.cpp + * @brief Implements a 3d viewer windows GUI + */ + +#include "eda_3d_viewer.h" +#include "../3d_viewer_id.h" +#include +#include +#include +#include +#include +#include "../common_ogl/cogl_att_list.h" +#include +#include + +/** + * Trace mask used to enable or disable the trace output of this class. + * The debug output can be turned on by setting the WXTRACE environment variable to + * "KI_TRACE_EDA_3D_VIEWER". See the wxWidgets documentation on wxLogTrace for + * more information. + */ +const wxChar * EDA_3D_VIEWER::m_logTrace = wxT( "KI_TRACE_EDA_3D_VIEWER" ); + + +// Key to store 3D Viewer config + +static const wxChar keyBgColor_Red[] = wxT( "BgColor_Red" ); +static const wxChar keyBgColor_Green[] = wxT( "BgColor_Green" ); +static const wxChar keyBgColor_Blue[] = wxT( "BgColor_Blue" ); + +static const wxChar keyBgColor_Red_Top[] = wxT( "BgColor_Red_Top" ); +static const wxChar keyBgColor_Green_Top[] = wxT( "BgColor_Green_Top" ); +static const wxChar keyBgColor_Blue_Top[] = wxT( "BgColor_Blue_Top" ); + +static const wxChar keySMaskColor_Red[] = wxT( "SMaskColor_Red" ); +static const wxChar keySMaskColor_Green[] = wxT( "SMaskColor_Green" ); +static const wxChar keySMaskColor_Blue[] = wxT( "SMaskColor_Blue" ); + +static const wxChar keySPasteColor_Red[] = wxT( "SPasteColor_Red" ); +static const wxChar keySPasteColor_Green[] = wxT( "SPasteColor_Green" ); +static const wxChar keySPasteColor_Blue[] = wxT( "SPasteColor_Blue" ); + +static const wxChar keySilkColor_Red[] = wxT( "SilkColor_Red" ); +static const wxChar keySilkColor_Green[] = wxT( "SilkColor_Green" ); +static const wxChar keySilkColor_Blue[] = wxT( "SilkColor_Blue" ); + +static const wxChar keyCopperColor_Red[] = wxT( "CopperColor_Red" ); +static const wxChar keyCopperColor_Green[] = wxT( "CopperColor_Green" ); +static const wxChar keyCopperColor_Blue[] = wxT( "CopperColor_Blue" ); + +static const wxChar keyBoardBodyColor_Red[] = wxT( "BoardBodyColor_Red" ); +static const wxChar keyBoardBodyColor_Green[] = wxT( "BoardBodyColor_Green" ); +static const wxChar keyBoardBodyColor_Blue[] = wxT( "BoardBodyColor_Blue" ); + +static const wxChar keyMousewheelPanning[] = wxT( "MousewheelPAN3D" ); + +static const wxChar keyShowRealisticMode[] = wxT( "ShowRealisticMode" ); +static const wxChar keyRenderEngine[] = wxT( "RenderEngine" ); +static const wxChar keyRenderRemoveHoles[] = wxT( "Render_RemoveHoles" ); +//static const wxChar keyRenderTextures[] = wxT( "Render_Textures" ); +static const wxChar keyRenderMaterial[] = wxT( "Render_Material" ); + +static const wxChar keyRenderOGL_ShowCopperTck[]= wxT( "Render_OGL_ShowCopperThickness" ); +static const wxChar keyRenderOGL_ShowModelBBox[]= wxT( "Render_OGL_ShowModelBoudingBoxes" ); + +static const wxChar keyRenderRAY_Shadows[] = wxT( "Render_RAY_Shadows" ); +static const wxChar keyRenderRAY_Backfloor[] = wxT( "Render_RAY_Backfloor" ); +static const wxChar keyRenderRAY_Refractions[] = wxT( "Render_RAY_Refractions" ); +static const wxChar keyRenderRAY_Reflections[] = wxT( "Render_RAY_Reflections" ); +static const wxChar keyRenderRAY_PostProcess[] = wxT( "Render_RAY_PostProcess" ); +static const wxChar keyRenderRAY_AAliasing[] = wxT( "Render_RAY_AntiAliasing" ); + +static const wxChar keyShowAxis[] = wxT( "ShowAxis" ); +static const wxChar keyShowGrid[] = wxT( "ShowGrid3D" ); +static const wxChar keyShowZones[] = wxT( "ShowZones" ); +static const wxChar keyShowFootprints_Normal[] = wxT( "ShowFootprints_Normal" ); +static const wxChar keyShowFootprints_Insert[] = wxT( "ShowFootprints_Insert" ); +static const wxChar keyShowFootprints_Virtual[] = wxT( "ShowFootprints_Virtual" ); +static const wxChar keyShowAdhesiveLayers[] = wxT( "ShowAdhesiveLayers" ); +static const wxChar keyShowSilkScreenLayers[] = wxT( "ShowSilkScreenLayers" ); +static const wxChar keyShowSolderMaskLayers[] = wxT( "ShowSolderMasLayers" ); +static const wxChar keyShowSolderPasteLayers[] = wxT( "ShowSolderPasteLayers" ); +static const wxChar keyShowCommentsLayer[] = wxT( "ShowCommentsLayers" ); +static const wxChar keyShowBoardBody[] = wxT( "ShowBoardBody" ); +static const wxChar keyShowEcoLayers[] = wxT( "ShowEcoLayers" ); + + +BEGIN_EVENT_TABLE( EDA_3D_VIEWER, EDA_BASE_FRAME ) + + EVT_ACTIVATE( EDA_3D_VIEWER::OnActivate ) + EVT_SET_FOCUS( EDA_3D_VIEWER::OnSetFocus ) + + EVT_TOOL_RANGE( ID_ZOOM_IN, + ID_ZOOM_REDRAW, + EDA_3D_VIEWER::ProcessZoom ) + + EVT_TOOL_RANGE( ID_START_COMMAND_3D, + ID_MENU_COMMAND_END, + EDA_3D_VIEWER::Process_Special_Functions ) + + EVT_MENU( wxID_EXIT, + EDA_3D_VIEWER::Exit3DFrame ) + + EVT_MENU_RANGE( ID_MENU3D_GRID, + ID_MENU3D_GRID_END, + EDA_3D_VIEWER::On3DGridSelection ) + + EVT_MENU_RANGE( ID_MENU3D_ENGINE, + ID_MENU3D_ENGINE_END, + EDA_3D_VIEWER::OnRenderEngineSelection ) + + EVT_CLOSE( EDA_3D_VIEWER::OnCloseWindow ) + + EVT_UPDATE_UI_RANGE( ID_START_COMMAND_3D, + ID_MENU_COMMAND_END, + EDA_3D_VIEWER::OnUpdateMenus ) + +END_EVENT_TABLE() + + +EDA_3D_VIEWER::EDA_3D_VIEWER( KIWAY *aKiway, + PCB_BASE_FRAME *aParent, + const wxString &aTitle, + long style ) : + + KIWAY_PLAYER( aKiway, + aParent, + FRAME_PCB_DISPLAY3D, + aTitle, + wxDefaultPosition, + wxDefaultSize, + style, + VIEWER3D_FRAMENAME ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::EDA_3D_VIEWER %s" ), aTitle ); + + m_canvas = NULL; + m_defaultFileName = ""; + + // Give it an icon + wxIcon icon; + icon.CopyFromBitmap( KiBitmap( icon_3d_xpm ) ); + SetIcon( icon ); + + LoadSettings( config() ); + SetSize( m_FramePos.x, m_FramePos.y, m_FrameSize.x, m_FrameSize.y ); + + // Create the status line + static const int status_dims[4] = { -1, 130, 130, 170 }; + + wxStatusBar *status_bar = CreateStatusBar( DIM( status_dims ) ); + SetStatusWidths( DIM( status_dims ), status_dims ); + + CreateMenuBar(); + ReCreateMainToolbar(); + + m_canvas = new EDA_3D_CANVAS( this, + COGL_ATT_LIST::GetAttributesList( true ), + aParent->GetBoard(), + m_settings, + Prj().Get3DCacheManager() ); + + if( m_canvas ) + m_canvas->SetStatusBar( status_bar ); + + m_auimgr.SetManagedWindow( this ); + + EDA_PANEINFO horiztb; + horiztb.HorizontalToolbarPane(); + + m_auimgr.AddPane( m_mainToolBar, + wxAuiPaneInfo( horiztb ).Name( wxT( "m_mainToolBar" ) ).Top() ); + + if( m_canvas ) + m_auimgr.AddPane( m_canvas, + wxAuiPaneInfo().Name( wxT( "DrawFrame" ) ).CentrePane() ); + + m_auimgr.Update(); + + m_mainToolBar->EnableTool( ID_RENDER_CURRENT_VIEW, + (m_settings.RenderEngineGet() == RENDER_ENGINE_OPENGL_LEGACY) ); + + // Fixes bug in Windows (XP and possibly others) where the canvas requires the focus + // in order to receive mouse events. Otherwise, the user has to click somewhere on + // the canvas before it will respond to mouse wheel events. + if( m_canvas ) + m_canvas->SetFocus(); +} + + +EDA_3D_VIEWER::~EDA_3D_VIEWER() +{ + m_auimgr.UnInit(); + + // m_canvas delete will be called by wxWidget manager + //delete m_canvas; + //m_canvas = 0; +} + +void EDA_3D_VIEWER::ReloadRequest() +{ + // This will schedule a request to load later + if( m_canvas ) + m_canvas->ReloadRequest( GetBoard(), Prj().Get3DCacheManager() ); + + // This function is used by moduleframe.cpp + // while editing the pcb board, so it will not redraw to not slow the pcbnew + //m_canvas->Refresh(); +} + + +void EDA_3D_VIEWER::Exit3DFrame( wxCommandEvent &event ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::Exit3DFrame" ) ); + + Close( true ); +} + + +void EDA_3D_VIEWER::OnCloseWindow( wxCloseEvent &event ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::OnCloseWindow" ) ); + + if( m_canvas ) + m_canvas->Close(); + + // m_canvas delete will be called by wxWidget manager + //delete m_canvas; + //m_canvas = 0; + + Destroy(); + event.Skip( true ); +} + +#define ROT_ANGLE 10.0 + +void EDA_3D_VIEWER::Process_Special_Functions( wxCommandEvent &event ) +{ + int id = event.GetId(); + bool isChecked = event.IsChecked(); + + wxLogTrace( m_logTrace, + wxT( "EDA_3D_VIEWER::Process_Special_Functions id:%d isChecked:%d" ), + id, isChecked ); + + if( m_canvas == NULL ) + return; + + switch( id ) + { + case ID_RENDER_CURRENT_VIEW: + m_canvas->RenderRaytracingRequest(); + break; + + case ID_RELOAD3D_BOARD: + ReloadRequest(); + m_canvas->Request_refresh(); + break; + + case ID_ROTATE3D_X_POS: + m_settings.CameraGet().RotateX( glm::radians(ROT_ANGLE) ); + m_canvas->Request_refresh(); + break; + + case ID_ROTATE3D_X_NEG: + m_settings.CameraGet().RotateX( -glm::radians(ROT_ANGLE) ); + m_canvas->Request_refresh(); + break; + + case ID_ROTATE3D_Y_POS: + m_settings.CameraGet().RotateY( glm::radians(ROT_ANGLE) ); + m_canvas->Request_refresh(); + break; + + case ID_ROTATE3D_Y_NEG: + m_settings.CameraGet().RotateY( -glm::radians(ROT_ANGLE) ); + m_canvas->Request_refresh(); + break; + + case ID_ROTATE3D_Z_POS: + m_settings.CameraGet().RotateZ( glm::radians(ROT_ANGLE) ); + m_canvas->Request_refresh(); + break; + + case ID_ROTATE3D_Z_NEG: + m_settings.CameraGet().RotateZ( -glm::radians(ROT_ANGLE) ); + m_canvas->Request_refresh(); + break; + + case ID_MOVE3D_LEFT: + m_canvas->SetView3D( WXK_LEFT ); + return; + + case ID_MOVE3D_RIGHT: + m_canvas->SetView3D( WXK_RIGHT ); + return; + + case ID_MOVE3D_UP: + m_canvas->SetView3D( WXK_UP ); + return; + + case ID_MOVE3D_DOWN: + m_canvas->SetView3D( WXK_DOWN ); + return; + + case ID_ORTHO: + m_settings.CameraGet().ToggleProjection(); + m_canvas->Request_refresh(); + return; + + case ID_TOOL_SCREENCOPY_TOCLIBBOARD: + case ID_MENU_SCREENCOPY_PNG: + case ID_MENU_SCREENCOPY_JPEG: + takeScreenshot( event ); + return; + + case ID_MENU3D_BGCOLOR_BOTTOM_SELECTION: + if( Set3DColorFromUser( m_settings.m_BgColorBot, _( "Background Color, Bottom" ) ) ) + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_BGCOLOR_TOP_SELECTION: + if( Set3DColorFromUser( m_settings.m_BgColorTop, _( "Background Color, Top" ) ) ) + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_SILKSCREEN_COLOR_SELECTION: + Set3DSilkScreenColorFromUser(); + return; + + case ID_MENU3D_SOLDERMASK_COLOR_SELECTION: + Set3DSolderMaskColorFromUser(); + return; + + case ID_MENU3D_SOLDERPASTE_COLOR_SELECTION: + Set3DSolderPasteColorFromUser(); + return; + + case ID_MENU3D_COPPER_COLOR_SELECTION: + Set3DCopperColorFromUser(); + break; + + case ID_MENU3D_PCB_BODY_COLOR_SELECTION: + Set3DBoardBodyColorFromUser(); + break; + + case ID_MENU3D_MOUSEWHEEL_PANNING: + m_settings.SetFlag( FL_MOUSEWHEEL_PANNING, isChecked ); + break; + + case ID_MENU3D_REALISTIC_MODE: + m_settings.SetFlag( FL_USE_REALISTIC_MODE, isChecked ); + SetMenuBarOptionsState(); + ReloadRequest( ); + return; + + case ID_MENU3D_FL_RENDER_SHOW_HOLES_IN_ZONES: + m_settings.SetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES, isChecked ); + ReloadRequest(); + return; + + case ID_MENU3D_FL_RENDER_MATERIAL_MODE_NORMAL: + m_settings.MaterialModeSet( MATERIAL_MODE_NORMAL ); + ReloadRequest( ); + return; + + case ID_MENU3D_FL_RENDER_MATERIAL_MODE_DIFFUSE_ONLY: + m_settings.MaterialModeSet( MATERIAL_MODE_DIFFUSE_ONLY ); + ReloadRequest( ); + return; + + case ID_MENU3D_FL_RENDER_MATERIAL_MODE_CAD_MODE: + m_settings.MaterialModeSet( MATERIAL_MODE_CAD_MODE ); + ReloadRequest( ); + return; + + case ID_MENU3D_FL_OPENGL_RENDER_COPPER_THICKNESS: + m_settings.SetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS, isChecked ); + ReloadRequest(); + return; + + case ID_MENU3D_FL_OPENGL_RENDER_SHOW_MODEL_BBOX: + m_settings.SetFlag( FL_RENDER_OPENGL_SHOW_MODEL_BBOX, isChecked ); + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_FL_RAYTRACING_RENDER_SHADOWS: + m_settings.SetFlag( FL_RENDER_RAYTRACING_SHADOWS, isChecked ); + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_FL_RAYTRACING_BACKFLOOR: + m_settings.SetFlag( FL_RENDER_RAYTRACING_BACKFLOOR, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_FL_RAYTRACING_REFRACTIONS: + m_settings.SetFlag( FL_RENDER_RAYTRACING_REFRACTIONS, isChecked ); + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_FL_RAYTRACING_REFLECTIONS: + m_settings.SetFlag( FL_RENDER_RAYTRACING_REFLECTIONS, isChecked ); + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_FL_RAYTRACING_POST_PROCESSING: + m_settings.SetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING, isChecked ); + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_FL_RAYTRACING_ANTI_ALIASING: + m_settings.SetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING, isChecked ); + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_SHOW_BOARD_BODY: + m_settings.SetFlag( FL_SHOW_BOARD_BODY, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_AXIS_ONOFF: + m_settings.SetFlag( FL_AXIS, isChecked ); + m_canvas->Request_refresh(); + return; + + case ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_NORMAL: + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_NORMAL, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_NORMAL_INSERT: + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_VIRTUAL: + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_ZONE_ONOFF: + m_settings.SetFlag( FL_ZONE, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_ADHESIVE_ONOFF: + m_settings.SetFlag( FL_ADHESIVE, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_SILKSCREEN_ONOFF: + m_settings.SetFlag( FL_SILKSCREEN, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_SOLDER_MASK_ONOFF: + m_settings.SetFlag( FL_SOLDERMASK, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_SOLDER_PASTE_ONOFF: + m_settings.SetFlag( FL_SOLDERPASTE, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_COMMENTS_ONOFF: + m_settings.SetFlag( FL_COMMENTS, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_ECO_ONOFF: + m_settings.SetFlag( FL_ECO, isChecked ); + ReloadRequest( ); + return; + + case ID_MENU3D_RESET_DEFAULTS: + { + // Reload settings with a dummy config, so it will load the defaults + wxConfig *fooconfig = new wxConfig("FooBarApp"); + LoadSettings( fooconfig ); + delete fooconfig; + + // Refresh menu option state + SetMenuBarOptionsState(); + + // Tell canvas that we (may) changed the render engine + RenderEngineChanged(); + + ReloadRequest(); + } + return; + + case ID_MENU3D_HELP_HOTKEY_SHOW_CURRENT_LIST: + { + DisplayHotKeys(); + } + return; + + default: + wxLogMessage( wxT( "EDA_3D_VIEWER::Process_Special_Functions() error: unknown command %d" ), id ); + return; + } +} + + +void EDA_3D_VIEWER::On3DGridSelection( wxCommandEvent &event ) +{ + int id = event.GetId(); + + wxASSERT( id < ID_MENU3D_GRID_END ); + wxASSERT( id > ID_MENU3D_GRID ); + + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::On3DGridSelection id:%d" ), id ); + + switch( id ) + { + case ID_MENU3D_GRID_NOGRID: + m_settings.GridSet( GRID3D_NONE ); + break; + + case ID_MENU3D_GRID_10_MM: + m_settings.GridSet( GRID3D_10MM ); + break; + + case ID_MENU3D_GRID_5_MM: + m_settings.GridSet( GRID3D_5MM ); + break; + + case ID_MENU3D_GRID_2P5_MM: + m_settings.GridSet( GRID3D_2P5MM ); + break; + + case ID_MENU3D_GRID_1_MM: + m_settings.GridSet( GRID3D_1MM ); + break; + + default: + wxLogMessage( wxT( "EDA_3D_VIEWER::On3DGridSelection() error: unknown command %d" ), id ); + return; + } + + if( m_canvas ) + m_canvas->Request_refresh(); +} + + +void EDA_3D_VIEWER::OnRenderEngineSelection( wxCommandEvent &event ) +{ + int id = event.GetId(); + + wxASSERT( id < ID_MENU3D_ENGINE_END ); + wxASSERT( id > ID_MENU3D_ENGINE ); + + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::OnRenderEngineSelection id:%d" ), id ); + + const RENDER_ENGINE old_engine = m_settings.RenderEngineGet(); + + switch( id ) + { + case ID_MENU3D_ENGINE_OPENGL_LEGACY: + if( old_engine != RENDER_ENGINE_OPENGL_LEGACY ) + m_settings.RenderEngineSet( RENDER_ENGINE_OPENGL_LEGACY ); + break; + + case ID_MENU3D_ENGINE_RAYTRACING: + if( old_engine != RENDER_ENGINE_RAYTRACING ) + m_settings.RenderEngineSet( RENDER_ENGINE_RAYTRACING ); + break; + + default: + wxLogMessage( wxT( "EDA_3D_VIEWER::OnRenderEngineSelection() error: unknown command %d" ), id ); + return; + } + + if( old_engine != m_settings.RenderEngineGet() ) + { + RenderEngineChanged(); + } +} + + +void EDA_3D_VIEWER::OnUpdateMenus(wxUpdateUIEvent &event) +{ + //!TODO: verify how many times this event is called and check if that is OK + // to have it working this way + SetMenuBarOptionsState(); +} + + +void EDA_3D_VIEWER::ProcessZoom( wxCommandEvent &event ) +{ + int id = event.GetId(); + + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::ProcessZoom id:%d" ), id ); + + if( m_canvas == NULL ) + return; + + switch( id ) + { + case ID_ZOOM_PAGE: + m_canvas->SetView3D( WXK_HOME ); + break; + + case ID_ZOOM_IN: + m_canvas->SetView3D( WXK_F1 ); + break; + + case ID_ZOOM_OUT: + m_canvas->SetView3D( WXK_F2 ); + break; + + case ID_ZOOM_REDRAW: + m_canvas->Request_refresh(); + break; + + default: + wxLogMessage( wxT( "EDA_3D_VIEWER::ProcessZoom() error: unknown command %d" ), id ); + return; + } + + m_canvas->DisplayStatus(); +} + + +void EDA_3D_VIEWER::OnActivate( wxActivateEvent &event ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::OnActivate" ) ); + + if( m_canvas ) + { + // Reload data if 3D frame shows a board, + // because it can be changed since last frame activation + if( m_canvas->IsReloadRequestPending() ) + m_canvas->Request_refresh(); + + // Activates again the focus of the canvas so it will catch mouse and key events + m_canvas->SetFocus(); + } + + event.Skip(); // required under wxMAC +} + + +void EDA_3D_VIEWER::OnSetFocus(wxFocusEvent &event) +{ + // Activates again the focus of the canvas so it will catch mouse and key events + if( m_canvas ) + m_canvas->SetFocus(); + + event.Skip(); +} + + +void EDA_3D_VIEWER::LoadSettings( wxConfigBase *aCfg ) +{ + EDA_BASE_FRAME::LoadSettings( aCfg ); + + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::LoadSettings" ) ); + + aCfg->Read( keyBgColor_Red, &m_settings.m_BgColorBot.r, 0.4 ); + aCfg->Read( keyBgColor_Green, &m_settings.m_BgColorBot.g, 0.4 ); + aCfg->Read( keyBgColor_Blue, &m_settings.m_BgColorBot.b, 0.5 ); + + aCfg->Read( keyBgColor_Red_Top, &m_settings.m_BgColorTop.r, 0.8 ); + aCfg->Read( keyBgColor_Green_Top, &m_settings.m_BgColorTop.g, 0.8 ); + aCfg->Read( keyBgColor_Blue_Top, &m_settings.m_BgColorTop.b, 0.9 ); + + // m_SolderMaskColor default value = dark grey-green + aCfg->Read( keySMaskColor_Red, &m_settings.m_SolderMaskColor.r, 100.0 * 0.2 / 255.0 ); + aCfg->Read( keySMaskColor_Green, &m_settings.m_SolderMaskColor.g, 255.0 * 0.2 / 255.0 ); + aCfg->Read( keySMaskColor_Blue, &m_settings.m_SolderMaskColor.b, 180.0 * 0.2 / 255.0 ); + + // m_SolderPasteColor default value = light grey + aCfg->Read( keySPasteColor_Red, &m_settings.m_SolderPasteColor.r, 128.0 /255.0 ); + aCfg->Read( keySPasteColor_Green, &m_settings.m_SolderPasteColor.g, 128.0 /255.0 ); + aCfg->Read( keySPasteColor_Blue, &m_settings.m_SolderPasteColor.b, 128.0 /255.0 ); + + // m_SilkScreenColor default value = white + aCfg->Read( keySilkColor_Red, &m_settings.m_SilkScreenColor.r, 0.9 ); + aCfg->Read( keySilkColor_Green, &m_settings.m_SilkScreenColor.g, 0.9 ); + aCfg->Read( keySilkColor_Blue, &m_settings.m_SilkScreenColor.b, 0.9 ); + + // m_CopperColor default value = gold + aCfg->Read( keyCopperColor_Red, &m_settings.m_CopperColor.r, 255.0 * 0.7 / 255.0 ); + aCfg->Read( keyCopperColor_Green, &m_settings.m_CopperColor.g, 223.0 * 0.7 / 255.0 ); + aCfg->Read( keyCopperColor_Blue, &m_settings.m_CopperColor.b, 0.0 /255.0 ); + + // m_BoardBodyColor default value = FR4, in realistic mode + aCfg->Read( keyBoardBodyColor_Red, &m_settings.m_BoardBodyColor.r, 51.0 / 255.0 ); + aCfg->Read( keyBoardBodyColor_Green, &m_settings.m_BoardBodyColor.g, 43.0 / 255.0 ); + aCfg->Read( keyBoardBodyColor_Blue, &m_settings.m_BoardBodyColor.b, 22.0 /255.0 ); + + + bool tmp; + aCfg->Read( keyMousewheelPanning, &tmp, false ); + m_settings.SetFlag( FL_MOUSEWHEEL_PANNING, tmp ); + + aCfg->Read( keyShowRealisticMode, &tmp, true ); + m_settings.SetFlag( FL_USE_REALISTIC_MODE, tmp ); + + aCfg->Read( keyRenderRemoveHoles, &tmp, true ); + m_settings.SetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES, tmp ); + + // OpenGL options + aCfg->Read( keyRenderOGL_ShowCopperTck, &tmp, true ); + m_settings.SetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS, tmp ); + + aCfg->Read( keyRenderOGL_ShowModelBBox, &tmp, false ); + m_settings.SetFlag( FL_RENDER_OPENGL_SHOW_MODEL_BBOX, tmp ); + + // Raytracing options + aCfg->Read( keyRenderRAY_Shadows, &tmp, true ); + m_settings.SetFlag( FL_RENDER_RAYTRACING_SHADOWS, tmp ); + + aCfg->Read( keyRenderRAY_Backfloor, &tmp, true ); + m_settings.SetFlag( FL_RENDER_RAYTRACING_BACKFLOOR, tmp ); + + aCfg->Read( keyRenderRAY_Refractions, &tmp, true ); + m_settings.SetFlag( FL_RENDER_RAYTRACING_REFRACTIONS, tmp ); + + aCfg->Read( keyRenderRAY_Reflections, &tmp, true ); + m_settings.SetFlag( FL_RENDER_RAYTRACING_REFLECTIONS, tmp ); + + aCfg->Read( keyRenderRAY_PostProcess, &tmp, true ); + m_settings.SetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING, tmp ); + + aCfg->Read( keyRenderRAY_AAliasing, &tmp, true ); + m_settings.SetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING, tmp ); + + + aCfg->Read( keyShowAxis, &tmp, true ); + m_settings.SetFlag( FL_AXIS, tmp ); + + aCfg->Read( keyShowFootprints_Normal, &tmp, true ); + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_NORMAL, tmp ); + + aCfg->Read( keyShowFootprints_Insert, &tmp, true ); + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT, tmp ); + + aCfg->Read( keyShowFootprints_Virtual, &tmp, true ); + m_settings.SetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL, tmp ); + + aCfg->Read( keyShowZones, &tmp, true ); + m_settings.SetFlag( FL_ZONE, tmp ); + + aCfg->Read( keyShowAdhesiveLayers, &tmp, true ); + m_settings.SetFlag( FL_ADHESIVE, tmp ); + + aCfg->Read( keyShowSilkScreenLayers, &tmp, true ); + m_settings.SetFlag( FL_SILKSCREEN, tmp ); + + aCfg->Read( keyShowSolderMaskLayers, &tmp, true ); + m_settings.SetFlag( FL_SOLDERMASK, tmp ); + + aCfg->Read( keyShowSolderPasteLayers, &tmp, true ); + m_settings.SetFlag( FL_SOLDERPASTE, tmp ); + + aCfg->Read( keyShowCommentsLayer, &tmp, true ); + m_settings.SetFlag( FL_COMMENTS, tmp ); + + aCfg->Read( keyShowEcoLayers, &tmp, true ); + m_settings.SetFlag( FL_ECO, tmp ); + + aCfg->Read( keyShowBoardBody, &tmp, true ); + m_settings.SetFlag( FL_SHOW_BOARD_BODY, tmp ); + + int tmpi; + aCfg->Read( keyShowGrid, &tmpi, (int)GRID3D_NONE ); + m_settings.GridSet( (GRID3D_TYPE)tmpi ); + + aCfg->Read( keyRenderEngine, &tmpi, (int)RENDER_ENGINE_OPENGL_LEGACY ); + m_settings.RenderEngineSet( (RENDER_ENGINE)tmpi ); + + aCfg->Read( keyRenderMaterial, &tmpi, (int)MATERIAL_MODE_NORMAL ); + m_settings.MaterialModeSet( (MATERIAL_MODE)tmpi ); +} + + +void EDA_3D_VIEWER::SaveSettings( wxConfigBase *aCfg ) +{ + EDA_BASE_FRAME::SaveSettings( aCfg ); + + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::SaveSettings" ) ); + + aCfg->Write( keyBgColor_Red, m_settings.m_BgColorBot.r ); + aCfg->Write( keyBgColor_Green, m_settings.m_BgColorBot.g ); + aCfg->Write( keyBgColor_Blue, m_settings.m_BgColorBot.b ); + + aCfg->Write( keyBgColor_Red_Top, m_settings.m_BgColorTop.r ); + aCfg->Write( keyBgColor_Green_Top, m_settings.m_BgColorTop.g ); + aCfg->Write( keyBgColor_Blue_Top, m_settings.m_BgColorTop.b ); + + aCfg->Write( keySMaskColor_Red, m_settings.m_SolderMaskColor.r ); + aCfg->Write( keySMaskColor_Green, m_settings.m_SolderMaskColor.g ); + aCfg->Write( keySMaskColor_Blue, m_settings.m_SolderMaskColor.b ); + + aCfg->Write( keySPasteColor_Red, m_settings.m_SolderPasteColor.r ); + aCfg->Write( keySPasteColor_Green, m_settings.m_SolderPasteColor.g ); + aCfg->Write( keySPasteColor_Blue, m_settings.m_SolderPasteColor.b ); + + aCfg->Write( keySilkColor_Red, m_settings.m_SilkScreenColor.r ); + aCfg->Write( keySilkColor_Green, m_settings.m_SilkScreenColor.g ); + aCfg->Write( keySilkColor_Blue, m_settings.m_SilkScreenColor.b ); + + aCfg->Write( keyCopperColor_Red, m_settings.m_CopperColor.r ); + aCfg->Write( keyCopperColor_Green, m_settings.m_CopperColor.g ); + aCfg->Write( keyCopperColor_Blue, m_settings.m_CopperColor.b ); + + aCfg->Write( keyBoardBodyColor_Red, m_settings.m_BoardBodyColor.r ); + aCfg->Write( keyBoardBodyColor_Green, m_settings.m_BoardBodyColor.g ); + aCfg->Write( keyBoardBodyColor_Blue, m_settings.m_BoardBodyColor.b ); + + aCfg->Write( keyShowRealisticMode, m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ); + + aCfg->Write( keyMousewheelPanning, m_settings.GetFlag( FL_MOUSEWHEEL_PANNING ) ); + + aCfg->Write( keyRenderEngine, (int)m_settings.RenderEngineGet() ); + + aCfg->Write( keyRenderRemoveHoles, m_settings.GetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES ) ); + + aCfg->Write( keyRenderMaterial, (int)m_settings.MaterialModeGet() ); + + // OpenGL options + aCfg->Write( keyRenderOGL_ShowCopperTck,m_settings.GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) ); + aCfg->Write( keyRenderOGL_ShowModelBBox,m_settings.GetFlag( FL_RENDER_OPENGL_SHOW_MODEL_BBOX ) ); + + // Raytracing options + aCfg->Write( keyRenderRAY_Shadows, m_settings.GetFlag( FL_RENDER_RAYTRACING_SHADOWS ) ); + aCfg->Write( keyRenderRAY_Backfloor, m_settings.GetFlag( FL_RENDER_RAYTRACING_BACKFLOOR ) ); + aCfg->Write( keyRenderRAY_Refractions, m_settings.GetFlag( FL_RENDER_RAYTRACING_REFRACTIONS ) ); + aCfg->Write( keyRenderRAY_Reflections, m_settings.GetFlag( FL_RENDER_RAYTRACING_REFLECTIONS ) ); + aCfg->Write( keyRenderRAY_PostProcess, m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) ); + aCfg->Write( keyRenderRAY_AAliasing, m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) ); + + aCfg->Write( keyShowAxis, m_settings.GetFlag( FL_AXIS ) ); + aCfg->Write( keyShowGrid, (int)m_settings.GridGet() ); + + aCfg->Write( keyShowFootprints_Normal, m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL ) ); + aCfg->Write( keyShowFootprints_Virtual, m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT ) ); + aCfg->Write( keyShowFootprints_Virtual, m_settings.GetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL ) ); + + aCfg->Write( keyShowZones, m_settings.GetFlag( FL_ZONE ) ); + aCfg->Write( keyShowAdhesiveLayers, m_settings.GetFlag( FL_ADHESIVE ) ); + aCfg->Write( keyShowSilkScreenLayers, m_settings.GetFlag( FL_SILKSCREEN ) ); + aCfg->Write( keyShowSolderMaskLayers, m_settings.GetFlag( FL_SOLDERMASK ) ); + aCfg->Write( keyShowSolderPasteLayers, m_settings.GetFlag( FL_SOLDERPASTE ) ); + aCfg->Write( keyShowCommentsLayer, m_settings.GetFlag( FL_COMMENTS ) ); + aCfg->Write( keyShowEcoLayers, m_settings.GetFlag( FL_ECO ) ); + aCfg->Write( keyShowBoardBody, m_settings.GetFlag( FL_SHOW_BOARD_BODY ) ); +} + + +void EDA_3D_VIEWER::OnLeftClick( wxDC *DC, const wxPoint &MousePos ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::OnLeftClick" ) ); + // Do nothing +} + + +void EDA_3D_VIEWER::OnRightClick( const wxPoint &MousePos, wxMenu *PopMenu ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::OnRightClick" ) ); + // Do nothing +} + + +void EDA_3D_VIEWER::RedrawActiveWindow( wxDC *DC, bool EraseBg ) +{ + wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER::RedrawActiveWindow" ) ); + // Do nothing +} + + +void EDA_3D_VIEWER::takeScreenshot( wxCommandEvent& event ) +{ + wxString fullFileName; + bool fmt_is_jpeg = false; + + if( event.GetId() == ID_MENU_SCREENCOPY_JPEG ) + fmt_is_jpeg = true; + + if( event.GetId() != ID_TOOL_SCREENCOPY_TOCLIBBOARD ) + { + // Remember path between saves during this session only. + static wxFileName fn; + const wxString file_ext = fmt_is_jpeg ? wxT( "jpg" ) : wxT( "png" ); + const wxString mask = wxT( "*." ) + file_ext; + + // First time path is set to the project path. + if( !fn.IsOk() ) + fn = Parent()->Prj().GetProjectFullName(); + + fn.SetExt( file_ext ); + + fullFileName = EDA_FILE_SELECTOR( _( "3D Image File Name:" ), fn.GetPath(), + m_defaultFileName, file_ext, mask, this, + wxFD_SAVE | wxFD_OVERWRITE_PROMPT, true ); + + if( fullFileName.IsEmpty() ) + return; + + fn = fullFileName; + + // Be sure the screen area destroyed by the file dialog is redrawn + // before making a screen copy. + // Without this call, under Linux the screen refresh is made to late. + wxYield(); + } + + // Be sure we have the latest 3D view (remember 3D view is buffered) + Refresh(); + wxYield(); + + // Build image from the 3D buffer + wxWindowUpdateLocker noUpdates( this ); + + wxImage screenshotImage; + + if( m_canvas ) + m_canvas->GetScreenshot( screenshotImage ); + + if( event.GetId() == ID_TOOL_SCREENCOPY_TOCLIBBOARD ) + { + wxBitmap bitmap( screenshotImage ); + + if( wxTheClipboard->Open() ) + { + wxBitmapDataObject* dobjBmp = new wxBitmapDataObject( bitmap ); + + if( !wxTheClipboard->SetData( dobjBmp ) ) + wxMessageBox( _( "Failed to copy image to clipboard" ) ); + + wxTheClipboard->Flush(); /* the data in clipboard will stay + * available after the application exits */ + wxTheClipboard->Close(); + } + } + else + { + if( !screenshotImage.SaveFile( fullFileName, + fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) ) + wxMessageBox( _( "Can't save file" ) ); + + screenshotImage.Destroy(); + } + +} + + +void EDA_3D_VIEWER::RenderEngineChanged() +{ + if( m_canvas ) + m_canvas->RenderEngineChanged(); + + m_mainToolBar->EnableTool( ID_RENDER_CURRENT_VIEW, + (m_settings.RenderEngineGet() == RENDER_ENGINE_OPENGL_LEGACY) ); + + m_mainToolBar->Refresh(); +} + + +bool EDA_3D_VIEWER::Set3DColorFromUser( SFVEC3D &aColor, const wxString& aTitle, + wxColourData* aPredefinedColors ) +{ + wxColour newcolor, oldcolor; + + oldcolor.Set( KiROUND( aColor.r * 255 ), + KiROUND( aColor.g * 255 ), + KiROUND( aColor.b * 255 ) ); + + wxColourData emptyColorSet; // Provides a empty predefined set of colors + // if no color set available to avoid use of an + // old color set + + if( aPredefinedColors == NULL ) + aPredefinedColors = &emptyColorSet; + + newcolor = wxGetColourFromUser( this, oldcolor, aTitle, aPredefinedColors ); + + if( !newcolor.IsOk() ) // Cancel command + return false; + + if( newcolor != oldcolor ) + { + aColor.r = (double) newcolor.Red() / 255.0; + aColor.g = (double) newcolor.Green() / 255.0; + aColor.b = (double) newcolor.Blue() / 255.0; + } + + return true; +} + + +bool EDA_3D_VIEWER::Set3DSilkScreenColorFromUser() +{ + wxColourData definedColors; + unsigned int i = 0; + + definedColors.SetCustomColour( i++, wxColour( 241, 241, 241 ) ); // White + definedColors.SetCustomColour( i++, wxColour( 4, 18, 21 ) ); // Dark + + for(; i < wxColourData::NUM_CUSTOM;) + { + definedColors.SetCustomColour( i++, wxColour( 0, 0, 0 ) ); + } + + bool change = Set3DColorFromUser( m_settings.m_SilkScreenColor, + _( "Silk Screen Color" ), + &definedColors ); + + if( change ) + NewDisplay(); + + return change; +} + + +bool EDA_3D_VIEWER::Set3DSolderMaskColorFromUser() +{ + wxColourData definedColors; + unsigned int i = 0; + + definedColors.SetCustomColour( i++, wxColour( 20, 51, 36 ) ); // Green + definedColors.SetCustomColour( i++, wxColour( 91, 168, 12 ) ); // Light Green + definedColors.SetCustomColour( i++, wxColour( 13, 104, 11 ) ); // Saturated Green + definedColors.SetCustomColour( i++, wxColour(181, 19, 21 ) ); // Red + definedColors.SetCustomColour( i++, wxColour(239, 53, 41 ) ); // Red Light Orange + definedColors.SetCustomColour( i++, wxColour(210, 40, 14 ) ); // Red 2 + definedColors.SetCustomColour( i++, wxColour( 2, 59, 162 ) ); // Blue + definedColors.SetCustomColour( i++, wxColour( 54, 79, 116 ) ); // Light blue 1 + definedColors.SetCustomColour( i++, wxColour( 61, 85, 130 ) ); // Light blue 2 + definedColors.SetCustomColour( i++, wxColour( 21, 70, 80 ) ); // Green blue (dark) + definedColors.SetCustomColour( i++, wxColour( 11, 11, 11 ) ); // Black + definedColors.SetCustomColour( i++, wxColour( 245, 245,245 ) ); // White + definedColors.SetCustomColour( i++, wxColour(119, 31, 91 ) ); // Purple + definedColors.SetCustomColour( i++, wxColour( 32, 2, 53 ) ); // Purple Dark + + for(; i < wxColourData::NUM_CUSTOM;) + { + definedColors.SetCustomColour( i++, wxColour( 0, 0, 0 ) ); + } + + bool change = Set3DColorFromUser( m_settings.m_SolderMaskColor, + _( "Solder Mask Color" ), + &definedColors ); + + if( change ) + NewDisplay(); + + return change; +} + + +bool EDA_3D_VIEWER::Set3DCopperColorFromUser() +{ + wxColourData definedColors; + unsigned int i = 0; + + definedColors.SetCustomColour( i++, wxColour( 184, 115, 50) ); // Copper + definedColors.SetCustomColour( i++, wxColour( 191, 155, 58) ); // Gold + definedColors.SetCustomColour( i++, wxColour( 213, 213, 213) ); // Silver + definedColors.SetCustomColour( i++, wxColour( 160, 160, 160) ); // tin + + for(; i < wxColourData::NUM_CUSTOM;) + { + definedColors.SetCustomColour( i++, wxColour( 0, 0, 0 ) ); + } + + bool change = Set3DColorFromUser( m_settings.m_CopperColor, + _( "Copper Color" ), + &definedColors ); + + if( change ) + NewDisplay(); + + return change; +} + + +bool EDA_3D_VIEWER::Set3DBoardBodyColorFromUser() +{ + wxColourData definedColors; + unsigned int i = 0; + + definedColors.SetCustomColour( i++, wxColour( 51, 43, 22 ) ); // FR4 natural, dark + definedColors.SetCustomColour( i++, wxColour( 109, 116, 75 ) ); // FR4 natural + definedColors.SetCustomColour( i++, wxColour( 78, 14, 5 ) ); // brown/red + definedColors.SetCustomColour( i++, wxColour( 146, 99, 47 ) ); // brown 1 + definedColors.SetCustomColour( i++, wxColour( 160, 123, 54 ) ); // brown 2 + definedColors.SetCustomColour( i++, wxColour( 146, 99, 47 ) ); // brown 3 + definedColors.SetCustomColour( i++, wxColour( 63, 126, 71 ) ); // green 1 + definedColors.SetCustomColour( i++, wxColour( 117, 122, 90 ) ); // green 2 + + for(; i < wxColourData::NUM_CUSTOM;) + { + definedColors.SetCustomColour( i++, wxColour( 0, 0, 0 ) ); + } + + bool change = Set3DColorFromUser( m_settings.m_BoardBodyColor, + _( "Board Body Color" ), + &definedColors ); + + if( change ) + NewDisplay(); + + return change; +} + + +bool EDA_3D_VIEWER::Set3DSolderPasteColorFromUser() +{ + wxColourData definedColors; + unsigned int i = 0; + + definedColors.SetCustomColour( i++, wxColour( 128, 128, 128 ) ); // grey + definedColors.SetCustomColour( i++, wxColour( 213, 213, 213 ) ); // Silver + definedColors.SetCustomColour( i++, wxColour( 90, 90, 90 ) ); // grey 2 + + for(; i < wxColourData::NUM_CUSTOM;) + { + definedColors.SetCustomColour( i++, wxColour( 0, 0, 0 ) ); + } + + bool change = Set3DColorFromUser( m_settings.m_SolderPasteColor, + _( "Solder Paste Color" ), + &definedColors ); + + if( change ) + NewDisplay(); + + return change; +} + + +// Define 3D Viewer Hotkeys +// !TODO: this is used just for help menu, the structured are not used yet in the viewer +static EDA_HOTKEY Hk3D_PivotCenter( _HKI( "Center pivot rotation (Middle mouse click)" ), 0, WXK_SPACE ); +static EDA_HOTKEY Hk3D_MoveLeft( _HKI( "Move board Left" ), 0, WXK_LEFT ); +static EDA_HOTKEY Hk3D_MoveRight( _HKI( "Move board Right" ), 0, WXK_RIGHT ); +static EDA_HOTKEY Hk3D_MoveUp( _HKI( "Move board Up" ), 0, WXK_UP ); +static EDA_HOTKEY Hk3D_MoveDown( _HKI( "Move board Down" ), 0, WXK_DOWN ); +static EDA_HOTKEY Hk3D_HomeView( _HKI( "Home view" ), 0, WXK_HOME ); +static EDA_HOTKEY Hk3D_ResetView( _HKI( "Reset view" ), 0, 'R' ); + +static EDA_HOTKEY Hk3D_ViewFront( _HKI( "View Top" ), 0, 'y' ); +static EDA_HOTKEY Hk3D_ViewBack( _HKI( "View Bot" ), 0, 'Y' ); +static EDA_HOTKEY Hk3D_ViewLeft( _HKI( "View Left" ), 0, 'X' ); +static EDA_HOTKEY Hk3D_ViewRight( _HKI( "View Right" ), 0, 'x' ); +static EDA_HOTKEY Hk3D_ViewTop( _HKI( "View Top" ), 0, 'z' ); +static EDA_HOTKEY Hk3D_ViewBot( _HKI( "View Bot" ), 0, 'Z' ); + +static EDA_HOTKEY Hk3D_Rotate45axisZ( _HKI( "Rotate 45 degrees over Z axis" ), 0, WXK_TAB ); +static EDA_HOTKEY Hk3D_ZoomIn( _HKI( "Zoom in " ), 0, WXK_F1 ); +static EDA_HOTKEY Hk3D_ZoomOut( _HKI( "Zoom out" ), 0, WXK_F2 ); +static EDA_HOTKEY Hk3D_AttributesTHT( _HKI( "Toggle 3D models with attributes Normal (eg: THT)" ), 0, 'T' ); +static EDA_HOTKEY Hk3D_AttributesSMD( _HKI( "Toggle 3D models with attributes Normal+Insert (eg: SMD)" ), 0, 'S' ); +static EDA_HOTKEY Hk3D_AttributesVirtual( _HKI( "Toggle 3D models with attributes Virtual" ), 0, 'V' ); + +static wxString viewer3DSectionTitle( _HKI( "Viewer 3D" ) ); + +// List of hotkey descriptors for the 3D Viewer only +// !TODO: this is used just for help menu, the structured are not used yet in the viewer +static EDA_HOTKEY* viewer3d_Hotkey_List[] = +{ + &Hk3D_PivotCenter, + &Hk3D_MoveLeft, + &Hk3D_MoveRight, + &Hk3D_MoveUp, + &Hk3D_MoveDown, + &Hk3D_HomeView, + &Hk3D_ResetView, + &Hk3D_ViewFront, + &Hk3D_ViewBack, + &Hk3D_ViewLeft, + &Hk3D_ViewRight, + &Hk3D_ViewTop, + &Hk3D_ViewBot, + &Hk3D_Rotate45axisZ, + &Hk3D_ZoomIn, + &Hk3D_ZoomOut, + &Hk3D_AttributesTHT, + &Hk3D_AttributesSMD, + &Hk3D_AttributesVirtual, + NULL +}; + + +// list of sections and corresponding hotkey list for the 3D Viewer +// (used to list current hotkeys) +struct EDA_HOTKEY_CONFIG g_3DViewer_Hokeys_Descr[] = +{ + { &g_CommonSectionTag, viewer3d_Hotkey_List, &viewer3DSectionTitle }, + { NULL, NULL, NULL } +}; + + +void EDA_3D_VIEWER::DisplayHotKeys() +{ + DisplayHotkeyList( this, g_3DViewer_Hokeys_Descr ); +} diff --git a/3d-viewer/3d_viewer/eda_3d_viewer.h b/3d-viewer/3d_viewer/eda_3d_viewer.h new file mode 100644 index 0000000000..dafa7d11d1 --- /dev/null +++ b/3d-viewer/3d_viewer/eda_3d_viewer.h @@ -0,0 +1,235 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2011 Wayne Stambaugh + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file eda_3d_viewer.h + * @brief Declaration of the eda_3d_viewer class + */ + +#ifndef EDA_3D_VIEWER_H +#define EDA_3D_VIEWER_H + + +#include "../3d_canvas/cinfo3d_visu.h" +#include "../3d_canvas/eda_3d_canvas.h" +#include +#include + + +#define KICAD_DEFAULT_3D_DRAWFRAME_STYLE (wxDEFAULT_FRAME_STYLE | wxWANTS_CHARS) + +#define VIEWER3D_FRAMENAME wxT( "Viewer3DFrameName" ) + +/** + * Class EDA_3D_VIEWER + * Create and handle a window for the 3d viewer connected to a Kiway and a pcbboard + */ +class EDA_3D_VIEWER : public KIWAY_PLAYER +{ + + public: + + EDA_3D_VIEWER( KIWAY *aKiway, + PCB_BASE_FRAME *aParent, + const wxString &aTitle, + long style = KICAD_DEFAULT_3D_DRAWFRAME_STYLE ); + + ~EDA_3D_VIEWER(); + + PCB_BASE_FRAME* Parent() const { return (PCB_BASE_FRAME*)GetParent(); } + + BOARD* GetBoard() { return Parent()->GetBoard(); } + + void ReloadRequest(); + + // !TODO: review this function + // !TODO: this need a way to tell what changed to the reload will only + // change the things on a need base + void NewDisplay( int dummy = 0 ) { ReloadRequest(); } + + /** + * Function SetDefaultFileName + * Set the default file name (eg: to be suggested to a screenshot) + * @param aFn = file name to assign + */ + void SetDefaultFileName( const wxString &aFn ) { m_defaultFileName = aFn; } + + /** + * Function GetDefaultFileName + * @return the default suggested file name + */ + const wxString &GetDefaultFileName() const { return m_defaultFileName; } + + /** + * Function GetSettings + * @return current settings + */ + CINFO3D_VISU &GetSettings() { return m_settings; } + + /** + * Function Set3DColorFromUser + * Get a SFVEC3D from a wx colour dialog + * @param aColor is the SFVEC3D to change + * @param aTitle is the title displayed in the colordialog selector + * @param aPredefinedColors is a reference to a wxColourData + * which contains a few predefined colors + * if it is NULL, no predefined colors are used + * @return true if a new color is chosen, false if + * no change or aborted by user + */ + bool Set3DColorFromUser( SFVEC3D &aColor, const wxString& aTitle, + wxColourData* aPredefinedColors = NULL ); + + /** + * Function Set3DSolderMaskColorFromUser + * Set the solder mask color from a set of colors + * @return true if a new color is chosen, false if + * no change or aborted by user + */ + bool Set3DSolderMaskColorFromUser(); + + /** + * Function Set3DSolderPasteColorFromUser + * Set the solder mask color from a set of colors + * @return true if a new color is chosen, false if + * no change or aborted by user + */ + bool Set3DSolderPasteColorFromUser(); + + /** + * Function Set3DCopperColorFromUser + * Set the copper color from a set of colors + * @return true if a new color is chosen, false if + * no change or aborted by user + */ + bool Set3DCopperColorFromUser(); + + /** + * Function Set3DBoardBodyBodyColorFromUser + * Set the copper color from a set of colors + * @return true if a new color is chosen, false if + * no change or aborted by user + */ + bool Set3DBoardBodyColorFromUser(); + + /** + * Function Set3DSilkScreenColorFromUser + * Set the silkscreen color from a set of colors + * @return true if a new color is chosen, false if + * no change or aborted by user + */ + bool Set3DSilkScreenColorFromUser(); + + private: + /** + * @brief Exit3DFrame - Called when user press the File->Exit + * @param event + */ + void Exit3DFrame( wxCommandEvent &event ); + + void OnCloseWindow( wxCloseEvent &event ); + + void Process_Special_Functions( wxCommandEvent &event ); + + void On3DGridSelection( wxCommandEvent &event ); + + void OnRenderEngineSelection( wxCommandEvent &event ); + + void OnUpdateMenus(wxUpdateUIEvent& event); + + void ProcessZoom( wxCommandEvent &event ); + + void OnActivate( wxActivateEvent &event ); + + void OnSetFocus( wxFocusEvent &event ); + + void Install3DViewOptionDialog( wxCommandEvent &event ); + + void CreateMenuBar(); + + void DisplayHotKeys(); + + /** + * Set the state of toggle menus according to the current display options + */ + void SetMenuBarOptionsState(); + + void ReCreateMainToolbar(); + + void SetToolbars(); + + void SaveSettings( wxConfigBase *aCfg ); + + void LoadSettings( wxConfigBase *aCfg ); + + void OnLeftClick( wxDC *DC, const wxPoint &MousePos ); + + void OnRightClick( const wxPoint &MousePos, wxMenu *PopMenu ); + + void RedrawActiveWindow( wxDC *DC, bool EraseBg ); + + /** + * Function TakeScreenshot + * Create a Screenshot of the current 3D view. + * Output file format is png or jpeg, or image is copied to the clipboard + */ + void takeScreenshot( wxCommandEvent& event ); + + /** + * @brief RenderEngineChanged - Update toolbar icon and call canvas RenderEngineChanged + */ + void RenderEngineChanged(); + + DECLARE_EVENT_TABLE(); + + private: + + /** + * Filename to propose for save a screenshot + */ + wxString m_defaultFileName; + + /** + * The canvas where the openGL context will be rendered + */ + EDA_3D_CANVAS *m_canvas; + + /** + * Store all the settings and options to be used by the renders + */ + CINFO3D_VISU m_settings; + + /** + * Trace mask used to enable or disable the trace output of this class. + * The debug output can be turned on by setting the WXTRACE environment variable to + * "KI_TRACE_EDA_3D_VIEWER". See the wxWidgets documentation on wxLogTrace for + * more information. + */ + static const wxChar *m_logTrace; + +}; + +#endif // EDA_3D_VIEWER_H diff --git a/3d-viewer/3d_viewer_id.h b/3d-viewer/3d_viewer_id.h index f30d16e225..15d1a249aa 100644 --- a/3d-viewer/3d_viewer_id.h +++ b/3d-viewer/3d_viewer_id.h @@ -39,9 +39,14 @@ enum id_3dview_frm ID_MENU3D_SOLDERPASTE_COLOR_SELECTION, ID_MENU3D_PCB_BODY_COLOR_SELECTION, ID_MENU3D_COPPER_COLOR_SELECTION, - ID_MENU3D_USE_COPPER_THICKNESS, ID_MENU3D_AXIS_ONOFF, + ID_MENU3D_MODULE_ONOFF, + ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_NORMAL, + ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_NORMAL_INSERT, + ID_MENU3D_MODULE_ONOFF_ATTRIBUTES_VIRTUAL, + ID_MENU3D_MODULE_ONOFF_END, + ID_MENU3D_ZONE_ONOFF, ID_MENU3D_LAYERS, ID_MENU3D_ADHESIVE_ONOFF, @@ -53,16 +58,39 @@ enum id_3dview_frm ID_MENU3D_SHOW_BOARD_BODY, ID_MENU3D_MOUSEWHEEL_PANNING, ID_MENU3D_REALISTIC_MODE, - ID_MENU3D_FL_RENDER_SHADOWS, - ID_MENU3D_FL_RENDER_SHOW_HOLES_IN_ZONES, - ID_MENU3D_FL_RENDER_TEXTURES, - ID_MENU3D_FL_RENDER_SMOOTH_NORMALS, - ID_MENU3D_FL_RENDER_USE_MODEL_NORMALS, - ID_MENU3D_FL_RENDER_MATERIAL, - ID_MENU3D_FL_RENDER_SHOW_MODEL_BBOX, - ID_END_COMMAND_3D, - ID_TOOL_SET_VISIBLE_ITEMS, + ID_MENU3D_FL, + ID_MENU3D_FL_RENDER_SHOW_HOLES_IN_ZONES, + + ID_MENU3D_FL_RENDER_MATERIAL, + ID_MENU3D_FL_RENDER_MATERIAL_MODE_NORMAL, + ID_MENU3D_FL_RENDER_MATERIAL_MODE_DIFFUSE_ONLY, + ID_MENU3D_FL_RENDER_MATERIAL_MODE_CAD_MODE, + + ID_MENU3D_FL_OPENGL, + ID_MENU3D_FL_OPENGL_RENDER_COPPER_THICKNESS, + ID_MENU3D_FL_OPENGL_RENDER_SHOW_MODEL_BBOX, + + ID_MENU3D_FL_RAYTRACING, + ID_MENU3D_FL_RAYTRACING_RENDER_SHADOWS, + ID_MENU3D_FL_RAYTRACING_BACKFLOOR, + ID_MENU3D_FL_RAYTRACING_REFRACTIONS, + ID_MENU3D_FL_RAYTRACING_REFLECTIONS, + ID_MENU3D_FL_RAYTRACING_POST_PROCESSING, + ID_MENU3D_FL_RAYTRACING_ANTI_ALIASING, + + ID_RENDER_CURRENT_VIEW, + + ID_MENU_SCREENCOPY_PNG, + ID_MENU_SCREENCOPY_JPEG, + ID_MENU_SCREENCOPY_TOCLIBBOARD, + + ID_MENU3D_RESET_DEFAULTS, + + // Help + ID_MENU3D_HELP_HOTKEY_SHOW_CURRENT_LIST, + + ID_MENU_COMMAND_END, ID_MENU3D_GRID, ID_MENU3D_GRID_NOGRID, @@ -72,9 +100,10 @@ enum id_3dview_frm ID_MENU3D_GRID_1_MM, ID_MENU3D_GRID_END, - ID_MENU_SCREENCOPY_PNG, - ID_MENU_SCREENCOPY_JPEG, - ID_MENU_SCREENCOPY_TOCLIBBOARD, + ID_MENU3D_ENGINE, + ID_MENU3D_ENGINE_OPENGL_LEGACY, + ID_MENU3D_ENGINE_RAYTRACING, + ID_MENU3D_ENGINE_END, ID_POPUP_3D_VIEW_START, ID_POPUP_ZOOMIN, @@ -89,5 +118,7 @@ enum id_3dview_frm ID_POPUP_MOVE3D_RIGHT, ID_POPUP_MOVE3D_UP, ID_POPUP_MOVE3D_DOWN, - ID_POPUP_3D_VIEW_END + ID_POPUP_3D_VIEW_END, + + ID_END_COMMAND_3D = ID_KICAD_3D_VIEWER_END, }; diff --git a/3d-viewer/CMakeLists.txt b/3d-viewer/CMakeLists.txt index 81b9e166a1..8875aea264 100644 --- a/3d-viewer/CMakeLists.txt +++ b/3d-viewer/CMakeLists.txt @@ -1,18 +1,21 @@ +#add_definitions(-DPRINT_STATISTICS_3D_VIEWER) add_definitions(-DPCBNEW) +set( CMAKE_C_FLAGS_RELEASE "-O3" ) +set( CMAKE_CXX_FLAGS_RELEASE "-O3" ) + include_directories(BEFORE ${INC_BEFORE}) include_directories( - textures ../pcbnew ../polygon 3d_canvas 3d_cache 3d_rendering 3d_viewer + ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/include/gal/opengl ${GLEW_INCLUDE_DIR} ${GLM_INCLUDE_DIR} - ${CMAKE_SOURCE_DIR} ${INC_AFTER} ) @@ -25,27 +28,6 @@ set( DIR_RAY_3D ${DIR_RAY}/shapes3D ) set( DIR_3D_PLUGINS ${CMAKE_SOURCE_DIR}/plugins/ldr ) set(3D-VIEWER_SRCS - dialogs/dialog_3D_view_option_base.cpp - dialogs/dialog_3D_view_option.cpp - 3d_aux.cpp - 3d_canvas.cpp - 3d_class.cpp - 3d_draw.cpp - 3d_draw_board_body.cpp - 3d_draw_basic_functions.cpp - 3d_draw_helper_functions.cpp - 3d_frame.cpp - 3d_material.cpp - 3d_mesh_model.cpp - 3d_read_mesh.cpp - 3d_toolbar.cpp - info3d_visu.cpp - trackball.cpp - vrmlmodelparser.cpp - vrml_aux.cpp - vrml_v1_modelparser.cpp - vrml_v2_modelparser.cpp - x3dmodelparser.cpp ${DIR_3D_PLUGINS}/pluginldr.cpp ${DIR_3D_PLUGINS}/3d/pluginldr3D.cpp 3d_cache/3d_cache_wrapper.cpp @@ -57,94 +39,76 @@ set(3D-VIEWER_SRCS ${DIR_DLG}/dlg_3d_pathconfig.cpp ${DIR_DLG}/dlg_select_3dmodel.cpp ${DIR_DLG}/panel_prev_model.cpp + ../polygon/poly2tri/common/shapes.cc + ../polygon/poly2tri/sweep/advancing_front.cc + ../polygon/poly2tri/sweep/cdt.cc + ../polygon/poly2tri/sweep/sweep.cc + ../polygon/poly2tri/sweep/sweep_context.cc + 3d_canvas/cinfo3d_visu.cpp + 3d_canvas/create_layer_items.cpp + 3d_canvas/create_layer_poly.cpp + 3d_canvas/eda_3d_canvas.cpp + 3d_canvas/eda_3d_canvas_pivot.cpp 3d_model_viewer/c3d_model_viewer.cpp 3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.cpp 3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.cpp 3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp 3d_rendering/3d_render_ogl_legacy/c3d_render_ogl_legacy.cpp 3d_rendering/3d_render_ogl_legacy/clayer_triangles.cpp + ${DIR_RAY_ACC}/caccelerator.cpp + ${DIR_RAY_ACC}/cbvh_packet_traversal.cpp + ${DIR_RAY_ACC}/cbvh_pbrt.cpp + ${DIR_RAY_ACC}/ccontainer.cpp + ${DIR_RAY_ACC}/ccontainer2d.cpp + ${DIR_RAY}/c3d_render_createscene.cpp + ${DIR_RAY}/c3d_render_raytracing.cpp + ${DIR_RAY}/cfrustum.cpp + ${DIR_RAY}/cmaterial.cpp + ${DIR_RAY}/mortoncodes.cpp ${DIR_RAY}/ray.cpp + ${DIR_RAY}/raypacket.cpp ${DIR_RAY_2D}/cbbox2d.cpp + ${DIR_RAY_2D}/cfilledcircle2d.cpp + ${DIR_RAY_2D}/citemlayercsg2d.cpp ${DIR_RAY_2D}/cobject2d.cpp + ${DIR_RAY_2D}/cpolygon2d.cpp + ${DIR_RAY_2D}/cpolygon4pts2d.cpp + ${DIR_RAY_2D}/cring2d.cpp ${DIR_RAY_2D}/croundsegment2d.cpp + ${DIR_RAY_2D}/ctriangle2d.cpp + ${DIR_RAY_2D}/edgeshrink.cpp ${DIR_RAY_3D}/cbbox.cpp ${DIR_RAY_3D}/cbbox_ray.cpp + ${DIR_RAY_3D}/ccylinder.cpp + ${DIR_RAY_3D}/cdummyblock.cpp + ${DIR_RAY_3D}/clayeritem.cpp + ${DIR_RAY_3D}/cobject.cpp + ${DIR_RAY_3D}/cplane.cpp + ${DIR_RAY_3D}/croundseg.cpp + ${DIR_RAY_3D}/ctriangle.cpp + 3d_rendering/buffers_debug.cpp + 3d_rendering/c3d_render_base.cpp 3d_rendering/ccamera.cpp + 3d_rendering/ccolorrgb.cpp 3d_rendering/cimage.cpp + 3d_rendering/cpostshader.cpp + 3d_rendering/cpostshader_ssao.cpp 3d_rendering/ctrack_ball.cpp + 3d_rendering/test_cases.cpp 3d_rendering/trackball.cpp + 3d_viewer/3d_toolbar.cpp + 3d_viewer/dialogs/dialog_3D_view_option.cpp + 3d_viewer/dialogs/dialog_3D_view_option_base.cpp + 3d_viewer/eda_3d_viewer.cpp common_ogl/cogl_att_list.cpp common_ogl/ogl_utils.cpp + 3d_fastmath.cpp + 3d_math.cpp ) add_library(3d-viewer STATIC ${3D-VIEWER_SRCS}) add_dependencies( 3d-viewer pcbcommon ) -option( GLM_ENABLE_SIMD_SSE2 "Enable SSE2 optimizations" OFF ) -option( GLM_ENABLE_SIMD_SSE3 "Enable SSE3 optimizations" OFF ) -option( GLM_ENABLE_SIMD_AVX "Enable AVX optimizations" OFF ) -option( GLM_ENABLE_SIMD_AVX2 "Enable AVX2 optimizations" OFF ) -option( GLM_FORCE_PURE "Force 'pure' instructions" OFF ) - -if(GLM_FORCE_PURE) - add_definitions(-DGLM_FORCE_PURE) - - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-mfpmath=387) - endif() -elseif(GLM_ENABLE_SIMD_AVX2) - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-mavx2) - elseif(GLM_USE_INTEL) - add_definitions(/QxAVX2) - elseif(MSVC) - add_definitions(/arch:AVX2) - endif() -elseif(GLM_ENABLE_SIMD_AVX) - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-mavx) - elseif(GLM_USE_INTEL) - add_definitions(/QxAVX) - elseif(MSVC) - add_definitions(/arch:AVX) - endif() -elseif(GLM_ENABLE_SIMD_SSE3) - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-msse3) - elseif(GLM_USE_INTEL) - add_definitions(/QxSSE3) - elseif(MSVC) - add_definitions(/arch:SSE2) # VC doesn't support /arch:SSE3 - endif() -elseif(GLM_ENABLE_SIMD_SSE2) - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-msse2) - elseif(GLM_USE_INTEL) - add_definitions(/QxSSE2) - elseif(MSVC) - if(NOT CMAKE_CL_64) - add_definitions(/arch:SSE2) - endif() - endif() -endif() - - -option( GLM_ENABLE_FAST_MATH "Enable fast math optimizations" OFF ) - -if(GLM_ENABLE_FAST_MATH) - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-ffast-math) - endif() - - if(MSVC) - add_definitions(/fp:fast) - endif() -elseif(NOT GLM_ENABLE_FAST_MATH) - if(MSVC) - add_definitions(/fp:precise) - endif() -endif() - target_link_libraries( 3d-viewer ${Boost_} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} kicad_3dsg ) add_subdirectory( 3d_cache ) diff --git a/3d-viewer/common_ogl/cogl_att_list.cpp b/3d-viewer/common_ogl/cogl_att_list.cpp index 2999e503ce..dc32810b6a 100644 --- a/3d-viewer/common_ogl/cogl_att_list.cpp +++ b/3d-viewer/common_ogl/cogl_att_list.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro + * Copyright (C) 2015-2016 Mario Luzeiro * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or @@ -28,7 +28,7 @@ */ #include "cogl_att_list.h" - +#include /** * Attributes list to be passed to a wxGLCanvas creation. @@ -41,78 +41,92 @@ * Only WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_STEREO are such boolean * attributes. */ -const int COGL_ATT_LIST::m_openGL_AttributesList[] = { +const int COGL_ATT_LIST::m_openGL_attributes_list[] = { + // Boolean attributes (using itself at padding): - WX_GL_RGBA, WX_GL_RGBA, + + // 0 1 + WX_GL_RGBA, WX_GL_RGBA, + // 2 3 WX_GL_DOUBLEBUFFER, WX_GL_DOUBLEBUFFER, + // Normal attributes with values: + + // 4 5 WX_GL_DEPTH_SIZE, 16, - WX_GL_STENCIL_SIZE, 1, - WX_GL_SAMPLE_BUFFERS, 1, // Enable multisampling support (antialiasing). + // 6 7 + WX_GL_STENCIL_SIZE, 8, + + + // This ones need to be the last in the list (as the tags will set to 0 if AA fails) + + // 8 9 WX_GL_SAMPLES, 0, // Disable AA for the start. - 0, 0 // NULL termination + //10 11 + WX_GL_SAMPLE_BUFFERS, 1, // Enable multisampling support (antialiasing). + + 0, 0 // NULL termination }; +#define ATT_WX_GL_SAMPLES_OFFSET 8 +#define ATT_WX_GL_SAMPLES_OFFSET_DATA 9 +#define ATT_WX_GL_SAMPLE_BUFFERS_OFFSET 10 +#define ATT_WX_GL_SAMPLE_BUFFERS_DATA 11 -int COGL_ATT_LIST::m_openGL_AttributesList_toUse[ - DIM( COGL_ATT_LIST::m_openGL_AttributesList ) ] = { 0 }; +int COGL_ATT_LIST::m_openGL_attributes_list_to_use[ + DIM( COGL_ATT_LIST::m_openGL_attributes_list ) ] = { 0 }; const int *COGL_ATT_LIST::GetAttributesList( bool aUseAntiAliasing ) { - memcpy( m_openGL_AttributesList_toUse, - m_openGL_AttributesList, - sizeof( m_openGL_AttributesList_toUse ) ); + memcpy( m_openGL_attributes_list_to_use, + m_openGL_attributes_list, + sizeof( m_openGL_attributes_list_to_use ) ); if( aUseAntiAliasing ) { + // There is a bug on wxGLCanvas that makes IsDisplaySupported fail + // while testing for antialiasing. + // http://trac.wxwidgets.org/ticket/16909 + // this next code will only work after this bug is fixed + // + // On my experience (Mario) it was only working on Linux but failing on + // Windows, so there was no AA. + + // Check if the canvas supports multisampling. - if( wxGLCanvas::IsDisplaySupported( m_openGL_AttributesList_toUse ) ) + if( wxGLCanvas::IsDisplaySupported( m_openGL_attributes_list_to_use ) ) { // Check for possible sample sizes, start form the top. int maxSamples = 8; // Any higher doesn't change anything. - int samplesOffset = 0; - for( unsigned int ii = 0; - ii < DIM( m_openGL_AttributesList_toUse ); - ii += 2 ) - { - if( m_openGL_AttributesList_toUse[ii] == WX_GL_SAMPLES ) - { - samplesOffset = ii + 1; - break; - } - } + m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLES_OFFSET_DATA] = maxSamples; - m_openGL_AttributesList_toUse[samplesOffset] = maxSamples; - - for( ; maxSamples > 0 && - !wxGLCanvas::IsDisplaySupported( m_openGL_AttributesList_toUse ); + for( ; (maxSamples > 0) && + ( !wxGLCanvas::IsDisplaySupported( m_openGL_attributes_list_to_use ) ); maxSamples = maxSamples >> 1 ) { - m_openGL_AttributesList_toUse[samplesOffset] = maxSamples; + m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLES_OFFSET_DATA] = maxSamples; } } else + { + DBG( printf("GetAttributesList: AntiAliasing is not supported.\n") ); aUseAntiAliasing = false; + } } // Disable antialising if it failed or was not requested if( !aUseAntiAliasing ) { - // Disable multisampling - for( unsigned int ii = 0; - ii < DIM( m_openGL_AttributesList_toUse ); - ii += 2 ) - { - if( m_openGL_AttributesList_toUse[ii] == WX_GL_SAMPLE_BUFFERS ) - { - m_openGL_AttributesList_toUse[ii + 1] = 0; - break; - } - } + // Remove multisampling information + // (hoping that the GPU driver will decide what is best) + m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLES_OFFSET] = 0; + m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLES_OFFSET_DATA] = 0; + m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLE_BUFFERS_OFFSET] = 0; + m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLE_BUFFERS_DATA] = 0; } - return m_openGL_AttributesList_toUse; + return m_openGL_attributes_list_to_use; } diff --git a/3d-viewer/common_ogl/cogl_att_list.h b/3d-viewer/common_ogl/cogl_att_list.h index 8df580e882..209877ea2b 100644 --- a/3d-viewer/common_ogl/cogl_att_list.h +++ b/3d-viewer/common_ogl/cogl_att_list.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,7 +30,6 @@ #ifndef _COGL_ATT_LIST_H #define _COGL_ATT_LIST_H -//#include "plugins/3dapi/xv3d_types.h" #include #include @@ -46,7 +45,8 @@ class COGL_ATT_LIST public: /** * Get a list of attributes to pass to wxGLCanvas - * @param aUseAntiAliasing = if true try to initialize (if is supported) the list with anti aliasing capabilities + * @param aUseAntiAliasing = if true try to initialize (if is supported) the + * list with anti aliasing capabilities * @return a list of options to be passed in the creation of a EDA_3D_CANVAS class */ static const int *GetAttributesList( bool aUseAntiAliasing ); @@ -63,12 +63,12 @@ private: * Only WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_STEREO are such boolean * attributes. */ - static const int m_openGL_AttributesList[]; + static const int m_openGL_attributes_list[]; /** * Attributes list that was (eventualy) changed and are passed to creation */ - static int m_openGL_AttributesList_toUse[]; + static int m_openGL_attributes_list_to_use[]; }; #endif // _COGL_ATT_LIST_H diff --git a/3d-viewer/common_ogl/ogl_utils.cpp b/3d-viewer/common_ogl/ogl_utils.cpp index 7e8e6395c7..4d19c97eca 100644 --- a/3d-viewer/common_ogl/ogl_utils.cpp +++ b/3d-viewer/common_ogl/ogl_utils.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,6 +31,7 @@ #include "openGL_includes.h" #include "ogl_utils.h" + void OGL_GetScreenshot( wxImage &aDstImage ) { struct viewport_params @@ -43,8 +44,11 @@ void OGL_GetScreenshot( wxImage &aDstImage ) glGetIntegerv( GL_VIEWPORT, (GLint*) &viewport ); - unsigned char* pixelbuffer = (unsigned char*) malloc( viewport.x * viewport.y * 3 ); - unsigned char* alphabuffer = (unsigned char*) malloc( viewport.x * viewport.y ); + unsigned char* pixelbuffer = (unsigned char*) malloc( viewport.x * + viewport.y * 3 ); + + // Alphabuffer was causing some transparency problems on some systems (Windows) + // unsigned char* alphabuffer = (unsigned char*) malloc( viewport.x * viewport.y ); glPixelStorei( GL_PACK_ALIGNMENT, 1 ); glReadBuffer( GL_BACK_LEFT ); @@ -53,9 +57,9 @@ void OGL_GetScreenshot( wxImage &aDstImage ) viewport.x, viewport.y, GL_RGB, GL_UNSIGNED_BYTE, pixelbuffer ); - glReadPixels( viewport.originX, viewport.originY, - viewport.x, viewport.y, - GL_ALPHA, GL_UNSIGNED_BYTE, alphabuffer ); + // glReadPixels( viewport.originX, viewport.originY, + // viewport.x, viewport.y, + // GL_ALPHA, GL_UNSIGNED_BYTE, alphabuffer ); // "Sets the image data without performing checks. // The data given must have the size (width*height*3) @@ -63,7 +67,8 @@ void OGL_GetScreenshot( wxImage &aDstImage ) // If static_data is false, after this call the pointer to the data is owned // by the wxImage object, that will be responsible for deleting it." aDstImage.SetData( pixelbuffer, viewport.x, viewport.y, false ); - aDstImage.SetAlpha( alphabuffer, false ); + + //aDstImage.SetAlpha( alphabuffer, false ); aDstImage = aDstImage.Mirror( false ); } @@ -71,13 +76,16 @@ void OGL_GetScreenshot( wxImage &aDstImage ) GLuint OGL_LoadTexture( const CIMAGE &aImage ) { - unsigned char* rgbaBuffer = (unsigned char*) malloc( aImage.GetWidth() * aImage.GetHeight() * 4 ); + unsigned char* rgbaBuffer = (unsigned char*) malloc( aImage.GetWidth() * + aImage.GetHeight() * 4 ); unsigned char* dst = rgbaBuffer; - unsigned char* ori = aImage.GetBuffer(); + const unsigned char* ori = aImage.GetBuffer(); + for( unsigned int i = 0; i < (aImage.GetWidth() * aImage.GetHeight()); ++i ) { unsigned char v = *ori; + ori++; dst[0] = v; @@ -88,26 +96,42 @@ GLuint OGL_LoadTexture( const CIMAGE &aImage ) } GLuint texture; - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - glPixelStorei (GL_PACK_ALIGNMENT, 4); + glPixelStorei( GL_UNPACK_ALIGNMENT, 4 ); + glPixelStorei( GL_PACK_ALIGNMENT, 4 ); glGenTextures( 1, &texture ); glBindTexture( GL_TEXTURE_2D, texture ); - //gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGBA, aImage.GetWidth(), aImage.GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, rgbaBuffer ); + /*gluBuild2DMipmaps( GL_TEXTURE_2D, + GL_RGBA, + aImage.GetWidth(), + aImage.GetHeight(), + GL_RGBA, + GL_UNSIGNED_BYTE, + rgbaBuffer );*/ - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, aImage.GetWidth(), aImage.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaBuffer ); + glTexImage2D( GL_TEXTURE_2D, + 0, + GL_RGBA, + aImage.GetWidth(), + aImage.GetHeight(), + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + rgbaBuffer ); //glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glBindTexture( GL_TEXTURE_2D, 0 ); + glFinish(); + free( rgbaBuffer ); return texture; @@ -117,13 +141,62 @@ GLuint OGL_LoadTexture( const CIMAGE &aImage ) void OGL_SetMaterial( const SMATERIAL & aMaterial ) { const SFVEC4F ambient = SFVEC4F( aMaterial.m_Ambient, 1.0f ); - const SFVEC4F diffuse = SFVEC4F( aMaterial.m_Diffuse - aMaterial.m_Transparency, 1.0f ); + const SFVEC4F diffuse = SFVEC4F( aMaterial.m_Diffuse, 1.0f - + aMaterial.m_Transparency ); const SFVEC4F specular = SFVEC4F( aMaterial.m_Specular, 1.0f ); const SFVEC4F emissive = SFVEC4F( aMaterial.m_Emissive, 1.0f ); + const float shininess = 128.0f * ( (aMaterial.m_Shininess > 1.0f)? + 1.0f: + aMaterial.m_Shininess ); + glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r ); glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r ); glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r ); - glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 128.0f * ((aMaterial.m_Shininess > 1.0f)?1.0f:aMaterial.m_Shininess) ); + glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, shininess ); +} + + +void OGL_SetDiffuseOnlyMaterial( const SFVEC3F &aMaterialDiffuse ) +{ + const SFVEC4F ambient = SFVEC4F( 0.2f, 0.2f, 0.2f, 1.0f ); + const SFVEC4F diffuse = SFVEC4F( aMaterialDiffuse, 1.0f ); + const SFVEC4F specular = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f ); + const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f ); + + glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r ); + glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r ); + glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r ); + glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r ); + glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 0.0f ); +} + + +void OGL_DrawBackground( const SFVEC3F &aTopColor, const SFVEC3F &aBotColor ) +{ + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glDisable( GL_LIGHTING ); + glDisable( GL_COLOR_MATERIAL ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + glDisable( GL_ALPHA_TEST ); + + glBegin( GL_QUADS ); + glColor4f( aTopColor.x, aTopColor.y, aTopColor.z, 1.0f ); + glVertex2f( -1.0, 1.0 ); // Top left corner + + glColor4f( aBotColor.x, aBotColor.y, aBotColor.z, 1.0f ); + glVertex2f( -1.0,-1.0 ); // bottom left corner + glVertex2f( 1.0,-1.0 ); // bottom right corner + + glColor4f( aTopColor.x, aTopColor.y, aTopColor.z, 1.0f ); + glVertex2f( 1.0, 1.0 ); // top right corner + glEnd(); } diff --git a/3d-viewer/common_ogl/ogl_utils.h b/3d-viewer/common_ogl/ogl_utils.h index 638921692f..13c72ac31d 100644 --- a/3d-viewer/common_ogl/ogl_utils.h +++ b/3d-viewer/common_ogl/ogl_utils.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,7 +24,7 @@ /** * @file ogl_utils.h - * @brief + * @brief implements generic openGL functions that are common to any openGL target */ #ifndef OGL_UTILS_H_ @@ -32,13 +32,44 @@ #include #include "3d_rendering/cimage.h" -#include "plugins/3dapi/c3dmodel.h" +#include -void OGL_SetMaterial(const SMATERIAL & aMaterial); +/** + * @brief OGL_SetMaterial - Set OpenGL materials + * @param aMaterial: a material structure with parameters to set + */ +void OGL_SetMaterial( const SMATERIAL & aMaterial ); + + +/** + * @brief OGL_SetDiffuseOnlyMaterial - sets only the diffuse color and keep other + * parameters with default values + * @param aMaterialDiffuse: the diffese color to assign to material properties + */ +void OGL_SetDiffuseOnlyMaterial( const SFVEC3F &aMaterialDiffuse ); + + +/** + * @brief OGL_LoadTexture - generate a new OpenGL texture + * @param aImage: a image to generate the texture from + * @return the OpenGL texture index created + */ GLuint OGL_LoadTexture( const CIMAGE &aImage ); + +/** + * @brief OGL_GetScreenshot - got the pixel data of current OpenGL image + * @param aDstImage: the output image. the image must be destroyed to free the data + */ void OGL_GetScreenshot( wxImage &aDstImage ); +/** + * @brief OGL_DrawBackground + * @param aTopColor + * @param aBotColor + */ +void OGL_DrawBackground( const SFVEC3F &aTopColor, const SFVEC3F &aBotColor ); + #endif // OGL_UTILS_H_ diff --git a/3d-viewer/common_ogl/openGL_includes.h b/3d-viewer/common_ogl/openGL_includes.h index a59d218bb7..17c06f1768 100644 --- a/3d-viewer/common_ogl/openGL_includes.h +++ b/3d-viewer/common_ogl/openGL_includes.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,7 +30,7 @@ #ifndef OPENGL_INCLUDES_H #define OPENGL_INCLUDES_H -#include +#include #ifdef __APPLE__ #include diff --git a/3d-viewer/credits.txt b/3d-viewer/credits.txt new file mode 100644 index 0000000000..a447e89f0f --- /dev/null +++ b/3d-viewer/credits.txt @@ -0,0 +1,229 @@ +Credits +======= + +The 3D-viewer contains parts, source code or adaptations or implementations that should give the following aknowledge: + + +cbvh_packet_traversal.cpp +------------------------------- + +Contains an implementation of the algorithm described in the paper: +"Ray Tracing Deformable Scenes Using Dynamic Bounding Volume Hierarchies" +http://www.cs.cmu.edu/afs/cs/academic/class/15869-f11/www/readings/wald07_packetbvh.pdf +by INGO WALD, SOLOMON BOULOS, and PETER SHIRLEY from University of Utah + +Contains an implementation of the algorithm described in the paper: +"Large Ray Packets for Real-time Whitted Ray Tracing" +http://cseweb.ucsd.edu/~ravir/whitted.pdf +by Ryan Overbeck1, Ravi Ramamoorthi from Columbia University and William R. Mark from +Intel Corporation and University of Texas at Austin + + + +cbvh_pbrt.h +----------- + +Contains a BVH implementation from the source code of the book +"Physically Based Rendering" (v2 and v3) http://www.pbrt.org/ +Copyright(c) 1998-2015 Matt Pharr, Greg Humphreys, and Wenzel Jakob. +LICENSE: https://github.com/mmp/pbrt-v3/blob/master/LICENSE.txt + + + +cbbox2d.cpp +----------- + +bool CBBOX2D::Intersects( const SFVEC2F &aCenter, float aRadiusSquared ) const +Contains an algorithm implementation based on the paper: +"On Faster Sphere-Box Overlap Testing" +http://www.mrtc.mdh.se/projects/3Dgraphics/paperF.pdf +Thomas Larsson, Tomas Akenine-Möller, and Eric Lengyel + + +bool CBBOX2D::Intersect( const RAY2D &aRay, float *t ) const +Contains an algorithm implementation based on the article: +http://tavianator.com/fast-branchless-raybounding-box-intersections/ +"FAST, BRANCHLESS RAY/BOUNDING BOX INTERSECTIONS", 2011 TAVIAN BARNES + + + +cfilledcircle2d.cpp, cring2d.cpp, croundseg.cpp +----------------------------------------------- + +bool CFILLEDCIRCLE2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut ) const +bool CRING2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut ) const +bool CROUNDSEG::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +Contains an algorithm based on: +http://cs665pd.googlecode.com/svn/trunk/photon/sphere.cpp +Steve Marschner's CS667 framework, email: srm at cs.cornell.edu + +TODO: ask author about the license of this source code. + + + +citemlayercsg2d.cpp +------------------- + +bool CITEMLAYERCSG2D::Intersect( const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut ) const +Based on ideas and implementation from +http://homepages.paradise.net.nz/nickamy/raytracer/raytracer.htm +Nick Chapman, email: nickamy@paradise.net.nz + + + +ctriangle2d.cpp +--------------- + +bool CTRIANGLE2D::IsPointInside( const SFVEC2F &aPoint ) const +Contains an algorithm based on: +http://totologic.blogspot.co.uk/2014/01/accurate-point-in-triangle-test.html +2014 Cédric Jules, email: flash dot cedric at Google mail service + + + +cbbox.cpp +--------- + +bool CBBOX::Intersect( const RAY &aRay, float *aOutHitt0, float *aOutHitt1 ) const +Contains an algorithm based on: +"Physically Based Rendering" (v2) http://www.pbrt.org/ +"Physical Based Ray Tracing" (by Matt Pharr and Greg Humphrey) + +https://github.com/mmp/pbrt-v2/blob/master/src/core/geometry.cpp#L68 +https://github.com/mmp/pbrt-v2/blob/master/src/accelerators/bvh.cpp#L126 +LICENSE: http://www.pbrt.org/LICENSE.txt + + + +cbbox_ray.cpp +------------- + +bool CBBOX::Intersect( const RAY &aRay, float *t ) const +Based on the source code from the paper: + +"This source code accompanies the Journal of Graphics Tools paper: + + "Fast Ray / Axis-Aligned Bounding Box Overlap Tests using Ray Slopes" + by Martin Eisemann, Thorsten Grosch, Stefan Müller and Marcus Magnor + Computer Graphics Lab, TU Braunschweig, Germany and + University of Koblenz-Landau, Germany + + This source code is public domain, but please mention us if you use it." + + + +ccylinder.cpp +------------- + +bool CVCYLINDER::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +bool CVCYLINDER::IntersectP(const RAY &aRay , float aMaxDistance ) const +Based on the source code from: +http://www.cs.utah.edu/~lha/Code%206620%20/Ray4/Cylinder.cpp +Linh Khanh Ha, University of Utah, email: lha sci.utah.edu + +TODO: Ask author about the source code license + + + +ctriangle.cpp +------------- + +bool CTRIANGLE::Intersect( const RAY &aRay, HITINFO &aHitInfo ) const +Implements a triangle ray intersection based on article: +http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_7_Kd-Trees_and_More_Speed.shtml +by Jacco Bikker, that implement optimizations based on Ingo Wald's thesis. + + + +cfrustum.cpp +------------ + +bool CFRUSTUM::Intersect( const CBBOX &aBBox ) const +BAsed on the implementation from: +https://github.com/nslo/raytracer/blob/2c2e0ff4bbb6082e07804ec7cf0b92673b98dcb1/src/raytracer/geom_utils.cpp#L66 +Nathan Slobody + +TODO: Ask author about the source code license + + + +clight.h +-------- + +Class CPOINTLIGHT Point light based on tutorial: +http://ogldev.atspace.co.uk/www/tutorial20/tutorial20.html +Etay Meiri, email: ogldev1 gmail + + + +mortoncodes.cpp +--------------- + +Implementes Morton Codes base on the implementation of Fabian “ryg” Giesen +https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ + + + +ccamera.h +--------- + +Frustum structure is based on the tutorial: +http://www.lighthouse3d.com/tutorials/view-frustum-culling/ + + + +cpostshader_ssao.cpp +-------------------- + +SSAO based on an implementation by: +Benjamin Blundell https://github.com/OniDaito/CoffeeGL/blob/master/misc/ssao.frag + +Also based from a post by martinsh at: +http://www.gamedev.net/topic/556187-the-best-ssao-ive-seen/?view=findpost&p=4632208 + +Latter adapter for CPU shader implementation and add other features specific to KiCad implementation. + + +trackball.h, trackball.cpp +-------------------------- + +A virtual trackball implementation +Written by Gavin Bell for Silicon Graphics, November 1988. +Original code from: David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli +License are in the files trackball.h and trackball.cpp + + + +3d_fastmath.h +------------- + +This file contains some functions from the PBRT 3 source code. +https://github.com/mmp/pbrt-v3/blob/master/src/core/pbrt.h +"Physically Based Rendering" (v3) http://www.pbrt.org/ +Copyright(c) 1998-2015 Matt Pharr, Greg Humphreys, and Wenzel Jakob. +LICENSE: https://github.com/mmp/pbrt-v3/blob/master/LICENSE.txt + + + +3d_math.h +--------- + +inline SFVEC3F CosWeightedRandomHemisphereDirection( SFVEC3F n ) +Based on a post in: +https://pathtracing.wordpress.com/2011/03/03/cosine-weighted-hemisphere/ + +inline bool Refract( const SFVEC3F &aInVector, const SFVEC3F &aNormal, float aRin_over_Rout, SFVEC3F &aOutVector ) +https://github.com/mmp/pbrt-v3/blob/master/src/core/reflection.h +"Physically Based Rendering" (v3) http://www.pbrt.org/ +Copyright(c) 1998-2015 Matt Pharr, Greg Humphreys, and Wenzel Jakob. +LICENSE: https://github.com/mmp/pbrt-v3/blob/master/LICENSE.txt + + + +openmp_mutex.h +-------------- + +openMP mutex class based on: +http://bisqwit.iki.fi/story/howto/openmp/ +by Joel Yliluoma, email bisqwit iki.fi + diff --git a/3d-viewer/info3d_visu.cpp b/3d-viewer/info3d_visu.cpp deleted file mode 100644 index bb6cd54fc7..0000000000 --- a/3d-viewer/info3d_visu.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file info3d_visu.cpp - */ - -#include - -#include - -#include -#include - -#include <3d_viewer.h> -#include -#include - -// Thickness of copper -// TODO: define the actual copper thickness by user -#define COPPER_THICKNESS KiROUND( 0.035 * IU_PER_MM ) // for 35 um -#define TECH_LAYER_THICKNESS KiROUND( 0.04 * IU_PER_MM ) -#define EPOXY_THICKNESS KiROUND( 1.6 * IU_PER_MM ) // for 1.6 mm - - -/* INFO3D_VISU in an helper class to store parameters like scaling factors, - * board size, Z coordinates of layers ... - * to create the 3D GLList - */ -INFO3D_VISU::INFO3D_VISU() -{ - int ii; - - m_Beginx = m_Beginy = 0.0; // position of mouse - m_Zoom = 1.0; - m_3D_Grid = 10.0; // Grid value in mm - trackball( m_Quat, 0.0, 0.0, 0.0, 0.0 ); - - for( ii = 0; ii < 4; ii++ ) - m_Rot[ii] = 0.0; - - m_CopperLayersCount = 2; - m_BoardSettings = NULL; - m_copperThickness = 0; - m_epoxyThickness = 0; - m_nonCopperLayerThickness = 0; - m_BiuTo3Dunits = 1.0; - zpos_offset = 0.0; - - // default all special item layers Visible - m_drawFlags.set(); - - SetFlag( FL_GRID, false ); - SetFlag( FL_USE_COPPER_THICKNESS, false ); - SetFlag( FL_RENDER_SHADOWS, false ); - SetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES, false ); -} - - -INFO3D_VISU::~INFO3D_VISU() -{ -} - - -/* Initialize info 3D Parameters from aBoard - */ -void INFO3D_VISU::InitSettings( BOARD* aBoard ) -{ - // Calculates the board bounding box - // First, use only the board outlines - EDA_RECT bbbox = aBoard->ComputeBoundingBox( true ); - - // If no outlines, use the board with items - if( bbbox.GetWidth() == 0 && bbbox.GetHeight() == 0 ) - bbbox = aBoard->ComputeBoundingBox( false ); - - // Gives a non null size to avoid issues in zoom / scale calculations - if( bbbox.GetWidth() == 0 && bbbox.GetHeight() == 0 ) - bbbox.Inflate( Millimeter2iu( 10 ) ); - - m_BoardSettings = &aBoard->GetDesignSettings(); - - m_BoardSize = bbbox.GetSize(); - m_BoardPos = bbbox.Centre(); - - m_BoardPos.y = -m_BoardPos.y; - m_CopperLayersCount = aBoard->GetCopperLayerCount(); - - // Ensure the board has 2 sides for 3D views, because it is hard to find - // a *really* single side board in the true life... - if( m_CopperLayersCount < 2 ) - m_CopperLayersCount = 2; - - m_BiuTo3Dunits = 2.0 / std::max( m_BoardSize.x, m_BoardSize.y ); - - m_epoxyThickness = aBoard->GetDesignSettings().GetBoardThickness() * m_BiuTo3Dunits; - - // TODO use value defined by user (currently use default values by ctor - m_copperThickness = COPPER_THICKNESS * m_BiuTo3Dunits; - m_nonCopperLayerThickness = TECH_LAYER_THICKNESS * m_BiuTo3Dunits; - - // Init Z position of each layer - // calculate z position for each copper layer - // Z = 0 is the z position of the back (bottom) layer (layer id = 31) - // Z = m_epoxyThickness is the z position of the front (top) layer (layer id = 0) - // all unused copper layer z position are set to 0 - int layer; - int copper_layers_cnt = m_CopperLayersCount; - - for( layer = 0; layer < copper_layers_cnt; layer++ ) - { - m_layerZcoord[layer] = - m_epoxyThickness - (m_epoxyThickness * layer / (copper_layers_cnt - 1)); - } - - #define layerThicknessMargin 1.1 - double zpos_offset = m_nonCopperLayerThickness * layerThicknessMargin; - double zpos_copper_back = - layerThicknessMargin*m_copperThickness/2; - double zpos_copper_front = m_epoxyThickness + layerThicknessMargin*m_copperThickness/2; - - // Fill remaining unused copper layers and back layer zpos - // with 0 - for( ; layer < MAX_CU_LAYERS; layer++ ) - { - m_layerZcoord[layer] = 0; - } - - // calculate z position for each non copper layer - // Solder mask and Solder paste have the same Z position - for( int layer_id = MAX_CU_LAYERS; layer_id < LAYER_ID_COUNT; layer_id++ ) - { - double zpos; - - switch( layer_id ) - { - case B_Adhes: - zpos = zpos_copper_back - 3 * zpos_offset; - break; - - case F_Adhes: - zpos = zpos_copper_front + 3 * zpos_offset; - break; - - case B_Paste: - zpos = zpos_copper_back - 1 * zpos_offset; - break; - - case F_Paste: - zpos = zpos_copper_front + 1 * zpos_offset; - break; - - case B_Mask: - zpos = zpos_copper_back - 1 * zpos_offset; - break; - - case F_Mask: - zpos = zpos_copper_front + 1 * zpos_offset; - break; - - case B_SilkS: - zpos = zpos_copper_back - 2 * zpos_offset; - break; - - case F_SilkS: - zpos = zpos_copper_front + 2 * zpos_offset; - break; - - default: - zpos = zpos_copper_front + (layer_id - MAX_CU_LAYERS + 4) * zpos_offset; - break; - } - - m_layerZcoord[layer_id] = zpos; - } -} - -/* return the Z position of 3D shapes, in 3D Units - * aIsFlipped: true for modules on Front (top) layer, false - * if on back (bottom) layer - * Note: in draw functions, the copper has a thickness = m_copperThickness - * Vias and tracks are draw with the top side position = m_copperThickness/2 - * and the bottom side position = -m_copperThickness/2 from the Z layer position - */ -double INFO3D_VISU::GetModulesZcoord3DIU( bool aIsFlipped ) -{ - if( aIsFlipped ) - { - if( g_Parm_3D_Visu.GetFlag( FL_SOLDERPASTE ) ) - return m_layerZcoord[B_SilkS] - ( m_copperThickness / 2.0 ); - else - return m_layerZcoord[B_Paste] - ( m_copperThickness / 2.0 ); - } - else - { - if( g_Parm_3D_Visu.GetFlag( FL_SOLDERPASTE ) ) - return m_layerZcoord[F_SilkS] + ( m_copperThickness / 2.0 ); - else - return m_layerZcoord[F_Paste] + ( m_copperThickness / 2.0 ); - } -} - diff --git a/3d-viewer/info3d_visu.h b/3d-viewer/info3d_visu.h deleted file mode 100644 index 49258e9035..0000000000 --- a/3d-viewer/info3d_visu.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2011 Wayne Stambaugh - * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file info3d_visu.h - */ - -#ifndef __INFO3D_VISU_H__ -#define __INFO3D_VISU_H__ - -#include // Layers id definitions - -#ifdef __WXMAC__ -# ifdef __DARWIN__ -# include -# else -# include -# endif -#else -# include -#endif - -#include <3d_struct.h> - -#define m_ROTX m_Rot[0] -#define m_ROTY m_Rot[1] -#define m_ROTZ m_Rot[2] - -class BOARD_DESIGN_SETTINGS; - -/** Minor class to store a 3D color (R, G, B) 3 floats range 0 to 1.0 - */ -class S3D_COLOR -{ -public: - double m_Red, m_Green, m_Blue; -public: S3D_COLOR() - { - m_Red = m_Green = m_Blue = 0; - } -}; - -enum DISPLAY3D_FLG { - FL_AXIS=0, FL_MODULE, FL_ZONE, - FL_ADHESIVE, FL_SILKSCREEN, FL_SOLDERMASK, FL_SOLDERPASTE, - FL_COMMENTS, FL_ECO, - FL_GRID, - FL_USE_COPPER_THICKNESS, - FL_SHOW_BOARD_BODY, - FL_MOUSEWHEEL_PANNING, - FL_USE_REALISTIC_MODE, - FL_RENDER_SHADOWS, - FL_RENDER_SHOW_HOLES_IN_ZONES, - FL_RENDER_TEXTURES, - FL_RENDER_SMOOTH_NORMALS, - FL_RENDER_USE_MODEL_NORMALS, - FL_RENDER_MATERIAL, - FL_RENDER_SHOW_MODEL_BBOX, - FL_LAST -}; - -/** Helper class to handle information needed to display 3D board - */ -class INFO3D_VISU -{ -public: - double m_Beginx, m_Beginy; // position of mouse (used in drag commands) - double m_Quat[4]; // orientation of 3D view - double m_Rot[4]; // rotation parameters of 3D view - double m_Zoom; // 3D zoom value - double m_3D_Grid; // 3D grid value, in mm - S3D_COLOR m_BgColor; - S3D_COLOR m_BgColor_Top; - S3D_COLOR m_BoardBodyColor; // in realistic mode: FR4 board color - S3D_COLOR m_SolderMaskColor; // in realistic mode: solder mask color - S3D_COLOR m_SolderPasteColor; // in realistic mode: solder paste color - S3D_COLOR m_SilkScreenColor; // in realistic mode: SilkScreen color - S3D_COLOR m_CopperColor; // in realistic mode: copper color - wxPoint m_BoardPos; // center board actual position in board units - wxSize m_BoardSize; // board actual size in board units - int m_CopperLayersCount; // Number of copper layers actually used by the board - - const BOARD_DESIGN_SETTINGS* m_BoardSettings; // Link to current board design settings - - double m_BiuTo3Dunits; // Normalization scale to convert board - // internal units to 3D units - // to normalize 3D units between -1.0 and +1.0 - - double zpos_offset; - -private: - double m_layerZcoord[LAYER_ID_COUNT]; // Z position of each layer (normalized) - double m_copperThickness; // Copper thickness (normalized) - double m_epoxyThickness; // Epoxy thickness (normalized) - double m_nonCopperLayerThickness; // Non copper layers thickness - std::bitset m_drawFlags; // Enable/disable flags (see DISPLAY3D_FLG list) - -public: INFO3D_VISU(); - ~INFO3D_VISU(); - - // Accessors - bool GetFlag( DISPLAY3D_FLG aFlag ) const { return m_drawFlags[aFlag]; } - void SetFlag( DISPLAY3D_FLG aFlag, bool aState ) - { - m_drawFlags[aFlag] = aState; - } - - /** - * Initialize 3D Parameters depending on aBoard - * @param aBoard: the board to display - */ - void InitSettings( BOARD* aBoard ); - - /** - * @return the Z position of 3D shapes, in 3D Units - * @param aIsFlipped: true for modules on Front (top) layer, false - * if on back (bottom) layer - */ - double GetModulesZcoord3DIU( bool aIsFlipped ); - - /** - * @return the Z coordinate of the layer aLayer, in Board Internal Units - * @param aLayerId: the layer number - */ - int GetLayerZcoordBIU( int aLayerId ) - { - return KiROUND( m_layerZcoord[aLayerId] / m_BiuTo3Dunits ); - } - - /** - * @return the thickness (Z size) of the copper, in Board Internal Units - * note: the thickness (Z size) of the copper is not the thickness - * of the layer (the thickness of the layer is the epoxy thickness / layer count) - * - * Note: if m_drawFlags[FL_USE_COPPER_THICKNESS] is not set, - * and normal mode, returns 0 - */ - int GetCopperThicknessBIU() const - { - bool use_thickness = GetFlag( FL_USE_COPPER_THICKNESS ); - - return use_thickness ? - KiROUND( m_copperThickness / m_BiuTo3Dunits ) - : 0; - } - - /** - * function GetEpoxyThicknessBIU - * @return the thickness (Z size) of the epoxy board, in Board Internal Units - */ - int GetEpoxyThicknessBIU() const - { - return KiROUND( m_epoxyThickness / m_BiuTo3Dunits ); - } - - /** - * function GetNonCopperLayerThicknessBIU - * @return the thickness (Z size) of a technical layer, - * in Board Internal Units - * - * Note: if m_drawFlags[FL_USE_COPPER_THICKNESS] is not set, returns 0 - */ - int GetNonCopperLayerThicknessBIU() const - { - bool use_thickness = GetFlag( FL_USE_COPPER_THICKNESS ); - return use_thickness ? - KiROUND( m_nonCopperLayerThickness / m_BiuTo3Dunits ) - : 0; - } - - /** - * function GetNonCopperLayerThicknessBIU - * @return the thickness (Z size) of the copper or a technical layer, - * in Board Internal Units, depending on the layer id - * - * Note: if m_drawFlags[FL_USE_COPPER_THICKNESS] is not set, returns 0 - */ - int GetLayerObjectThicknessBIU( int aLayerId ) const - { - return IsCopperLayer( aLayerId ) ? - GetCopperThicknessBIU() : - GetNonCopperLayerThicknessBIU(); - } - - bool IsRealisticMode() { return GetFlag( FL_USE_REALISTIC_MODE ); } -}; - -extern INFO3D_VISU g_Parm_3D_Visu; - -#endif /* __INFO3D_VISU_H__ */ diff --git a/3d-viewer/modelparsers.h b/3d-viewer/modelparsers.h deleted file mode 100644 index 9bba6cedc8..0000000000 --- a/3d-viewer/modelparsers.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2013 Tuomas Vaherkoski - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file modelparsers.h - */ - -#ifndef MODELPARSERS_H -#define MODELPARSERS_H - -#include -#include -#include -#include -#include <3d_mesh_model.h> - -class S3D_MASTER; -class X3D_MODEL_PARSER; - -/** - * abstract class S3D_MODEL_PARSER - * Base class for 3D model parsers. - */ -class S3D_MODEL_PARSER -{ -public: - S3D_MODEL_PARSER( S3D_MASTER* aMaster ) : - master( aMaster ) - {} - - virtual ~S3D_MODEL_PARSER(){} - - S3D_MASTER* GetMaster() - { - return master; - } - - /** - * Function Create - * Factory method for creating concrete 3D model parsers - * Notice that the caller is responsible to delete created parser. - * - * @param aMaster is master object that the parser will fill. - * @param aExtension is file extension of the file you are going to parse. - */ - static S3D_MODEL_PARSER* Create( S3D_MASTER* aMaster, const wxString aExtension ); - - /** - * virtual Function - * Concrete parsers should implement this function - * @param aFilename = the full file name of the file to load - * @return true if as succeeded - */ - virtual bool Load( const wxString& aFilename ) - { - return false; - }; - - S3D_MESH_PTRS childs; - -private: - S3D_MASTER* master; -}; - - -class wxXmlNode; - -/** - * class X3D_MODEL_PARSER - * Implements parser for X3D file format (VRML2.0 successor) - * X3D files can be exported from eg. Blender */ -class X3D_MODEL_PARSER: public S3D_MODEL_PARSER -{ -public: - X3D_MODEL_PARSER( S3D_MASTER* aMaster ); - ~X3D_MODEL_PARSER(); - - bool Load( const wxString& aFilename ); - - typedef std::map< wxString, wxString > PROPERTY_MAP; - typedef std::vector< wxXmlNode* > NODE_LIST; - - /** - * Function GetChildsByName - * Searches all child nodes with aName. - * - * @param aParent is node to search from - * @param aName is the name of node you try to find - * @param aResult contains found nodes - */ - static void GetChildsByName( wxXmlNode* aParent, const wxString aName, NODE_LIST& aResult ); - - /** - * Function GetNodeProperties - * Collects all node properties to map. - * - * @param aNode is an XML node. - * @param aProps contains map of found properties. - */ - static void GetNodeProperties( wxXmlNode* aNode, PROPERTY_MAP& aProps ); - -private: - wxString m_Filename; - S3D_MESH_PTR m_model; - - std::vector< wxString > vrml_materials; - std::vector< wxString > vrml_points; - std::vector< wxString > vrml_coord_indexes; - - void readTransform( wxXmlNode* aTransformNode ); - void readMaterial( wxXmlNode* aMatNode ); - void readIndexedFaceSet( wxXmlNode* aFaceNode, PROPERTY_MAP& aTransfromProps ); - bool parseDoubleTriplet( const wxString& aData, S3D_VERTEX& aResult ); - - void rotate( S3D_VERTEX& aCoordinate, S3D_VERTEX& aRotAxis, double angle ); -}; - - -typedef std::map< std::string, std::vector< glm::vec3 > > VRML2_COORDINATE_MAP; -typedef std::map< std::string, S3D_MESH_PTR > VRML2_DEF_GROUP_MAP; - -/** - * class VRML2_MODEL_PARSER - * Parses - */ -class VRML2_MODEL_PARSER -{ -public: - VRML2_MODEL_PARSER( S3D_MODEL_PARSER* aModelParser ); - ~VRML2_MODEL_PARSER(); - - bool Load( const wxString& aFilename ); - - /** - * Function Load - * Load a VRML2 filename and apply a transformation to the root - * @param aFilename file name with path - * @param aTransformationModel a model with translation, rotation and scale to apply to default root - * @return bool - true if finnished with success - */ - bool Load( const wxString& aFilename, S3D_MESH_PTR aTransformationModel ); - - /** - * Return string representing VRML2 file in vrml2 format - * Function Load must be called before this function, otherwise empty - * data set is returned. - */ - wxString VRML2_representation(); - -private: - int loadFileModel( S3D_MESH_PTR transformationModel ); - int read_Transform(); - int read_DEF(); - int read_DEF_Coordinate(); - int read_Shape(); - int read_appearance(); - int read_Appearance(); - int read_material(); - int read_Material(); - int read_IndexedFaceSet(); - int read_IndexedLineSet(); - int read_Coordinate(); - int read_CoordinateDef(); - int read_Normal(); - int read_NormalIndex(); - int read_Color(); - int read_coordIndex(); - int read_colorIndex(); - int read_geometry(); - int read_IndexedFaceSet_USE(); - int read_Transform_USE(); - int read_Inline(); - - /** Function debug_enter - * Used in debug to increase a ' ' in the m_debugSpacer, - * should be called after the first debug comment in a function - */ - void debug_enter(); - - /** Function debug_exit - * Used in debug to decrease a ' ' in the m_debugSpacer, - * should be called before the last debug comment in a funtion before exit - */ - void debug_exit(); - - bool m_normalPerVertex; - bool colorPerVertex; - S3D_MESH_PTR m_model; ///< It stores the current model that the parsing is adding data - FILE* m_file; - wxFileName m_Filename; - VRML2_COORDINATE_MAP m_defCoordinateMap; - VRML2_DEF_GROUP_MAP m_defGroupMap; ///< Stores a list of labels for groups and meshs that will be used later by the USE keyword - S3D_MODEL_PARSER* m_ModelParser; - S3D_MASTER* m_Master; - wxString m_debugSpacer; ///< Used to give identation space - - int m_counter_DEF_GROUP; ///< Counts the number of DEF * GROUPS used - int m_counter_USE_GROUP; ///< Counts the number of USE * used, in the end, if m_counter_DEF_GROUP > 0 and m_counter_USE_GROUP == 0 then it will add the first group with childs - - bool m_discardLastGeometry; ///< If true, it should not store the latest loaded geometry (used to discard IndexedLineSet, but load it) -}; - - -/** - * class VRML1_MODEL_PARSER - * Parses - */ -class VRML1_MODEL_PARSER -{ -public: - VRML1_MODEL_PARSER( S3D_MODEL_PARSER* aModelParser ); - ~VRML1_MODEL_PARSER(); - - bool Load( const wxString& aFilename ); - - /** - * Return string representing VRML2 file in vrml2 format - * Function Load must be called before this function, otherwise empty - * data set is returned. - */ - wxString VRML2_representation(); - -private: - int read_separator(); - int readMaterial(); - int readCoordinate3(); - int readIndexedFaceSet(); - - int readMaterial_ambientColor(); - int readMaterial_diffuseColor(); - int readMaterial_emissiveColor(); - int readMaterial_specularColor(); - int readMaterial_shininess(); - int readMaterial_transparency(); - - int readCoordinate3_point(); - - int readIndexedFaceSet_coordIndex(); - int readIndexedFaceSet_materialIndex(); - - bool m_normalPerVertex; - bool colorPerVertex; - S3D_MESH_PTR m_model; - FILE* m_file; - wxString m_Filename; - S3D_MODEL_PARSER* m_ModelParser; - S3D_MASTER* m_Master; -}; - -/** - * class VRML_MODEL_PARSER - * Parses - */ -class VRML_MODEL_PARSER: public S3D_MODEL_PARSER -{ -public: - /** - * ctor: initialize a VRML file parser - * @param aMaster = a ref to a 3D footprint shape description to fill - * by the vrml file data - */ - VRML_MODEL_PARSER( S3D_MASTER* aMaster ); - ~VRML_MODEL_PARSER(); - - /** - * Function load - * Load a 3D file and build a S3D_MASTER shape. - * file has .vrml ext and can be VRML 1 or VRML 2 format - * @param aFilename = the full filename to read - * @param aVrmlunits_to_3Dunits = the csaling factor to convert the 3D file unit - * to our internal units. - */ - bool Load( const wxString& aFilename ); -}; - - -#endif // MODELPARSERS_H diff --git a/3d-viewer/openmp_mutex.h b/3d-viewer/openmp_mutex.h new file mode 100644 index 0000000000..1b78d6f6d9 --- /dev/null +++ b/3d-viewer/openmp_mutex.h @@ -0,0 +1,81 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Mario Luzeiro + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +/** + * @file openmp_mutex.h + * @brief a mutex for openmp got from the website: + * http://bisqwit.iki.fi/story/howto/openmp/ + * by Joel Yliluoma + */ + +#ifndef _OPENMP_MUTEX_H +#define _OPENMP_MUTEX_H + +#ifdef _OPENMP + +# include + +struct MutexType +{ + MutexType() { omp_init_lock( &lock ); } + ~MutexType() { omp_destroy_lock( &lock ); } + void Lock() { omp_set_lock( &lock ); } + void Unlock() { omp_unset_lock( &lock ); } + + MutexType( const MutexType& ) { omp_init_lock( &lock ); } + MutexType& operator= ( const MutexType& ) { return *this; } +public: + omp_lock_t lock; +}; + +#else + +/// A dummy mutex that doesn't actually exclude anything, +/// but as there is no parallelism either, no worries. +struct MutexType +{ + void Lock() {} + void Unlock() {} +}; +#endif + +/// An exception-safe scoped lock-keeper. +struct ScopedLock +{ + explicit ScopedLock( MutexType& m ) : mut( m ), locked( true ) { mut.Lock(); } + ~ScopedLock() { Unlock(); } + void Unlock() { if( !locked ) return; locked = false; mut.Unlock(); } + void LockAgain() { if( locked ) return; mut.Lock(); locked = true; } + +private: + MutexType& mut; + bool locked; + +private: // prevent copying the scoped lock. + void operator=(const ScopedLock&); + ScopedLock(const ScopedLock&); +}; + +#endif // _OPENMP_MUTEX_H diff --git a/3d-viewer/textures/text_pcb.h b/3d-viewer/textures/text_pcb.h deleted file mode 100644 index cdfab7bf62..0000000000 --- a/3d-viewer/textures/text_pcb.h +++ /dev/null @@ -1,3675 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/* GIMP RGBA C-Source image dump (text_pcb.c) */ - -static const struct { - unsigned int width; - unsigned int height; - unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ - unsigned char pixel_data[128 * 128 * 4 + 1]; -} text_pcb = { - 128, 128, 4, - "\370\370\370\374\365\365\365\372\371\371\371\375\365\365\365\372\371\371" - "\371\375\366\366\366\373\370\370\370\374\366\366\366\373\361\361\361\371" - "\365\365\365\372\366\366\366\373\373\373\373\375\371\371\371\374\373\373" - "\373\375\371\371\371\374\375\375\375\376\365\365\365\373\373\373\373\375" - "\366\366\366\373\364\364\364\372\364\364\364\372\363\363\363\372\365\365" - "\365\372\363\363\363\372\366\366\366\373\373\373\373\375\375\375\375\376" - "\374\374\374\376\372\372\372\375\375\375\375\377\373\373\373\376\372\372" - "\372\375\365\365\365\372\363\363\363\372\373\373\373\375\372\372\372\375" - "\364\364\364\372\373\373\373\375\377\377\377\377\377\377\377\377\377\377" - "\377\377\377\377\377\377\366\366\366\373\372\372\372\375\373\373\373\375" - "\371\371\371\374\364\364\364\372\375\375\375\377\364\364\364\372\363\363" - "\363\371\374\374\374\376\370\370\370\374\372\372\372\375\372\372\372\375" - "\371\371\371\375\372\372\372\375\366\366\366\373\377\377\377\377\373\373" - "\373\376\366\366\366\373\364\364\364\372\365\365\365\373\364\364\364\372" - "\363\363\363\371\365\365\365\373\364\364\364\372\362\362\362\371\371\371" - "\371\375\366\366\366\373\372\372\372\375\374\374\374\376\372\372\372\375" - "\374\374\374\376\365\365\365\373\364\364\364\372\370\370\370\374\362\362" - "\362\371\363\363\363\371\366\366\366\373\362\362\362\371\367\367\367\373" - "\365\365\365\372\364\364\364\372\365\365\365\372\367\367\367\373\375\375" - "\375\377\372\372\372\375\371\371\371\375\371\371\371\375\370\370\370\374" - "\370\370\370\374\366\366\366\373\364\364\364\372\365\365\365\373\365\365" - "\365\373\365\365\365\372\362\362\362\371\371\371\371\374\370\370\370\374" - "\370\370\370\374\377\377\377\377\370\370\370\374\374\374\374\376\370\370" - "\370\374\366\366\366\373\367\367\367\373\364\364\364\372\357\357\357\370" - "\366\366\366\373\370\370\370\374\377\377\377\377\372\372\372\375\366\366" - "\366\373\371\371\371\374\375\375\375\377\373\373\373\376\366\366\366\373" - "\374\374\374\376\370\370\370\374\367\367\367\373\362\362\362\371\364\364" - "\364\372\370\370\370\374\364\364\364\372\366\366\366\373\375\375\375\376" - "\372\372\372\375\372\372\372\375\364\364\364\372\365\365\365\372\373\373" - "\373\375\371\371\371\374\367\367\367\373\370\370\370\374\370\370\370\374" - "\366\366\366\373\354\354\354\366\356\356\356\367\365\365\365\372\373\373" - "\373\375\372\372\372\375\374\374\374\376\376\376\376\377\375\375\375\377" - "\373\373\373\375\367\367\367\373\370\370\370\374\363\363\363\371\363\363" - "\363\371\364\364\364\372\363\363\363\372\364\364\364\372\366\366\366\373" - "\372\372\372\375\373\373\373\375\377\377\377\377\377\377\377\377\371\371" - "\371\375\375\375\375\377\370\370\370\374\373\373\373\376\365\365\365\373" - "\370\370\370\374\371\371\371\374\375\375\375\377\365\365\365\373\375\375" - "\375\377\374\374\374\376\370\370\370\374\376\376\376\377\373\373\373\375" - "\372\372\372\375\373\373\373\376\366\366\366\373\364\364\364\372\366\366" - "\366\373\365\365\365\373\366\366\366\373\363\363\363\371\370\370\370\374" - "\376\376\376\377\371\371\371\374\371\371\371\375\363\363\363\372\377\377" - "\377\377\366\366\366\373\364\364\364\372\362\362\362\371\366\366\366\373" - "\363\363\363\371\365\365\365\373\365\365\365\372\365\365\365\373\364\364" - "\364\372\366\366\366\373\371\371\371\374\371\371\371\374\373\373\373\376" - "\365\365\365\373\365\365\365\372\366\366\366\373\366\366\366\373\365\365" - "\365\373\367\367\367\373\360\360\360\370\364\364\364\372\364\364\364\372" - "\372\372\372\375\370\370\370\374\365\365\365\373\374\374\374\376\370\370" - "\370\374\370\370\370\374\376\376\376\377\366\366\366\373\372\372\372\375" - "\364\364\364\372\375\375\375\376\375\375\375\376\366\366\366\373\367\367" - "\367\373\364\364\364\372\364\364\364\372\367\367\367\373\366\366\366\373" - "\375\375\375\376\374\374\374\376\371\371\371\375\375\375\375\376\371\371" - "\371\375\374\374\374\376\367\367\367\373\364\364\364\372\366\366\366\373" - "\365\365\365\372\370\370\370\374\363\363\363\371\364\364\364\372\361\361" - "\361\371\374\374\374\376\370\370\370\374\363\363\363\371\365\365\365\372" - "\374\374\374\376\366\366\366\373\364\364\364\372\367\367\367\373\366\366" - "\366\373\356\356\356\367\363\363\363\372\364\364\364\372\364\364\364\372" - "\371\371\371\374\371\371\371\374\373\373\373\376\374\374\374\376\365\365" - "\365\372\375\375\375\376\376\376\376\377\375\375\375\376\373\373\373\375" - "\370\370\370\374\366\366\366\373\356\356\356\367\352\352\352\365\355\355" - "\355\367\364\364\364\372\371\371\371\375\374\374\374\376\371\371\371\375" - "\372\372\372\375\374\374\374\376\372\372\372\375\365\365\365\372\366\366" - "\366\373\364\364\364\372\365\365\365\373\364\364\364\372\366\366\366\373" - "\364\364\364\372\365\365\365\373\371\371\371\374\373\373\373\375\376\376" - "\376\377\377\377\377\377\370\370\370\374\374\374\374\376\373\373\373\376" - "\370\370\370\374\372\372\372\375\374\374\374\376\371\371\371\374\370\370" - "\370\374\370\370\370\374\373\373\373\375\375\375\375\376\377\377\377\377" - "\373\373\373\375\374\374\374\376\370\370\370\374\367\367\367\373\371\371" - "\371\375\364\364\364\372\366\366\366\373\370\370\370\374\366\366\366\373" - "\366\366\366\373\370\370\370\374\375\375\375\376\367\367\367\373\373\373" - "\373\375\371\371\371\374\375\375\375\377\377\377\377\377\370\370\370\374" - "\365\365\365\372\365\365\365\372\365\365\365\372\364\364\364\372\362\362" - "\362\371\365\365\365\372\366\366\366\373\371\371\371\374\372\372\372\375" - "\370\370\370\374\372\372\372\375\366\366\366\373\370\370\370\374\364\364" - "\364\372\366\366\366\373\367\367\367\373\366\366\366\373\370\370\370\374" - "\372\372\372\375\357\357\357\370\364\364\364\372\366\366\366\373\363\363" - "\363\371\373\373\373\375\371\371\371\374\370\370\370\374\377\377\377\377" - "\372\372\372\375\375\375\375\376\364\364\364\372\370\370\370\374\375\375" - "\375\376\366\366\366\373\370\370\370\374\363\363\363\371\361\361\361\371" - "\363\363\363\372\365\365\365\372\375\375\375\377\373\373\373\376\366\366" - "\366\373\371\371\371\375\375\375\375\377\371\371\371\374\370\370\370\374" - "\371\371\371\374\374\374\374\376\365\365\365\372\363\363\363\371\370\370" - "\370\374\365\365\365\373\363\363\363\371\370\370\370\374\365\365\365\372" - "\366\366\366\373\371\371\371\374\375\375\375\376\366\366\366\373\373\373" - "\373\375\373\373\373\375\365\365\365\372\363\363\363\371\357\357\357\370" - "\364\364\364\372\363\363\363\371\364\364\364\372\372\372\372\375\372\372" - "\372\375\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" - "\373\373\373\375\374\374\374\376\371\371\371\374\363\363\363\371\362\362" - "\362\371\364\364\364\372\364\364\364\372\366\366\366\373\366\366\366\373" - "\377\377\377\377\373\373\373\376\373\373\373\375\375\375\375\376\371\371" - "\371\375\362\362\362\371\370\370\370\374\365\365\365\372\364\364\364\372" - "\366\366\366\373\363\363\363\372\365\365\365\373\367\367\367\373\375\375" - "\375\376\375\375\375\377\374\374\374\376\374\374\374\376\365\365\365\372" - "\371\371\371\374\371\371\371\375\372\372\372\375\374\374\374\376\371\371" - "\371\375\371\371\371\375\366\366\366\373\373\373\373\376\374\374\374\376" - "\374\374\374\376\374\374\374\376\372\372\372\375\375\375\375\377\373\373" - "\373\376\371\371\371\374\373\373\373\376\363\363\363\371\371\371\371\374" - "\366\366\366\373\371\371\371\375\366\366\366\373\373\373\373\375\373\373" - "\373\376\371\371\371\374\372\372\372\375\374\374\374\376\377\377\377\377" - "\375\375\375\376\374\374\374\376\370\370\370\374\365\365\365\373\370\370" - "\370\374\364\364\364\372\363\363\363\371\363\363\363\372\365\365\365\372" - "\372\372\372\375\371\371\371\375\367\367\367\373\371\371\371\374\372\372" - "\372\375\367\367\367\373\371\371\371\374\366\366\366\373\366\366\366\373" - "\366\366\366\373\372\372\372\375\373\373\373\376\363\363\363\371\366\366" - "\366\373\371\371\371\375\364\364\364\372\364\364\364\372\370\370\370\374" - "\377\377\377\377\371\371\371\374\374\374\374\376\375\375\375\376\370\370" - "\370\374\370\370\370\374\371\371\371\375\364\364\364\372\366\366\366\373" - "\366\366\366\373\366\366\366\373\363\363\363\372\371\371\371\375\377\377" - "\377\377\377\377\377\377\374\374\374\376\371\371\371\375\366\366\366\373" - "\372\372\372\375\364\364\364\372\373\373\373\375\376\376\376\377\366\366" - "\366\373\366\366\366\373\357\357\357\370\364\364\364\372\371\371\371\375" - "\371\371\371\374\366\366\366\373\366\366\366\373\367\367\367\373\377\377" - "\377\377\370\370\370\374\370\370\370\374\366\366\366\373\366\366\366\373" - "\372\372\372\375\357\357\357\370\367\367\367\373\363\363\363\371\364\364" - "\364\372\374\374\374\376\374\374\374\376\377\377\377\377\373\373\373\375" - "\371\371\371\375\372\372\372\375\371\371\371\375\365\365\365\372\362\362" - "\362\371\365\365\365\372\355\355\355\367\363\363\363\371\367\367\367\373" - "\370\370\370\374\366\366\366\373\373\373\373\376\373\373\373\376\372\372" - "\372\375\370\370\370\374\366\366\366\373\370\370\370\374\366\366\366\373" - "\373\373\373\376\363\363\363\371\361\361\361\371\364\364\364\372\363\363" - "\363\371\370\370\370\374\370\370\370\374\370\370\370\374\377\377\377\377" - "\371\371\371\374\373\373\373\375\365\365\365\372\365\365\365\373\371\371" - "\371\374\375\375\375\376\370\370\370\374\366\366\366\373\373\373\373\376" - "\370\370\370\374\373\373\373\375\377\377\377\377\377\377\377\377\375\375" - "\375\377\367\367\367\373\374\374\374\376\371\371\371\375\366\366\366\373" - "\365\365\365\373\365\365\365\372\374\374\374\376\356\356\356\367\370\370" - "\370\374\365\365\365\372\363\363\363\372\372\372\372\375\370\370\370\374" - "\377\377\377\377\372\372\372\375\373\373\373\375\370\370\370\374\372\372" - "\372\375\364\364\364\372\367\367\367\373\365\365\365\372\364\364\364\372" - "\364\364\364\372\367\367\367\373\372\372\372\375\374\374\374\376\367\367" - "\367\373\367\367\367\373\371\371\371\374\366\366\366\373\364\364\364\372" - "\370\370\370\374\371\371\371\375\371\371\371\374\365\365\365\373\370\370" - "\370\374\365\365\365\373\373\373\373\376\362\362\362\371\371\371\371\375" - "\366\366\366\373\372\372\372\375\373\373\373\375\371\371\371\375\372\372" - "\372\375\371\371\371\375\365\365\365\373\373\373\373\375\364\364\364\372" - "\366\366\366\373\364\364\364\372\366\366\366\373\366\366\366\373\367\367" - "\367\373\363\363\363\372\372\372\372\375\373\373\373\376\374\374\374\376" - "\365\365\365\372\366\366\366\373\365\365\365\372\370\370\370\374\374\374" - "\374\376\364\364\364\372\371\371\371\375\370\370\370\374\362\362\362\371" - "\375\375\375\377\370\370\370\374\363\363\363\372\365\365\365\372\370\370" - "\370\374\362\362\362\371\371\371\371\375\365\365\365\373\370\370\370\374" - "\364\364\364\372\365\365\365\372\365\365\365\372\365\365\365\372\363\363" - "\363\372\366\366\366\373\364\364\364\372\375\375\375\376\373\373\373\376" - "\377\377\377\377\377\377\377\377\371\371\371\374\375\375\375\376\363\363" - "\363\371\365\365\365\372\365\365\365\373\374\374\374\376\360\360\360\370" - "\356\356\356\367\370\370\370\374\370\370\370\374\373\373\373\375\367\367" - "\367\373\370\370\370\374\365\365\365\372\364\364\364\372\370\370\370\374" - "\373\373\373\375\371\371\371\375\365\365\365\372\366\366\366\373\363\363" - "\363\372\363\363\363\372\362\362\362\371\374\374\374\376\367\367\367\373" - "\371\371\371\374\371\371\371\374\371\371\371\375\370\370\370\374\370\370" - "\370\374\377\377\377\377\371\371\371\374\366\366\366\373\365\365\365\372" - "\362\362\362\371\364\364\364\372\373\373\373\376\377\377\377\377\372\372" - "\372\375\377\377\377\377\377\377\377\377\377\377\377\377\373\373\373\376" - "\377\377\377\377\374\374\374\376\370\370\370\374\374\374\374\376\365\365" - "\365\373\367\367\367\373\365\365\365\372\365\365\365\372\373\373\373\376" - "\366\366\366\373\374\374\374\376\375\375\375\376\366\366\366\373\366\366" - "\366\373\377\377\377\377\366\366\366\373\364\364\364\372\364\364\364\372" - "\367\367\367\373\365\365\365\373\366\366\366\373\361\361\361\371\367\367" - "\367\373\371\371\371\374\364\364\364\372\366\366\366\373\366\366\366\373" - "\365\365\365\372\371\371\371\375\371\371\371\375\366\366\366\373\373\373" - "\373\376\372\372\372\375\366\366\366\373\371\371\371\374\364\364\364\372" - "\362\362\362\371\366\366\366\373\373\373\373\375\371\371\371\374\374\374" - "\374\376\375\375\375\376\376\376\376\377\365\365\365\372\376\376\376\377" - "\373\373\373\376\376\376\376\377\372\372\372\375\366\366\366\373\371\371" - "\371\375\370\370\370\374\376\376\376\377\365\365\365\372\371\371\371\375" - "\373\373\373\375\370\370\370\374\367\367\367\373\365\365\365\372\376\376" - "\376\377\371\371\371\374\373\373\373\376\370\370\370\374\373\373\373\376" - "\371\371\371\374\353\353\353\366\364\364\364\372\373\373\373\375\370\370" - "\370\374\370\370\370\374\367\367\367\373\367\367\367\373\364\364\364\372" - "\366\366\366\373\365\365\365\373\372\372\372\375\370\370\370\374\365\365" - "\365\373\363\363\363\371\365\365\365\373\363\363\363\371\375\375\375\376" - "\375\375\375\376\367\367\367\373\373\373\373\375\374\374\374\376\366\366" - "\366\373\371\371\371\375\370\370\370\374\366\366\366\373\365\365\365\372" - "\364\364\364\372\366\366\366\373\364\364\364\372\366\366\366\373\367\367" - "\367\373\373\373\373\376\366\366\366\373\370\370\370\374\366\366\366\373" - "\372\372\372\375\364\364\364\372\371\371\371\374\371\371\371\374\362\362" - "\362\371\365\365\365\373\366\366\366\373\362\362\362\371\364\364\364\372" - "\373\373\373\376\375\375\375\377\372\372\372\375\372\372\372\375\377\377" - "\377\377\373\373\373\376\364\364\364\372\377\377\377\377\371\371\371\375" - "\367\367\367\373\370\370\370\374\366\366\366\373\366\366\366\373\370\370" - "\370\374\375\375\375\376\377\377\377\377\373\373\373\375\377\377\377\377" - "\377\377\377\377\374\374\374\376\376\376\376\377\375\375\375\376\370\370" - "\370\374\373\373\373\375\367\367\367\373\373\373\373\375\365\365\365\373" - "\371\371\371\374\377\377\377\377\377\377\377\377\373\373\373\375\373\373" - "\373\376\371\371\371\375\371\371\371\374\371\371\371\374\371\371\371\374" - "\365\365\365\372\365\365\365\372\366\366\366\373\365\365\365\373\367\367" - "\367\373\357\357\357\370\365\365\365\373\365\365\365\373\364\364\364\372" - "\367\367\367\373\365\365\365\372\373\373\373\376\371\371\371\374\367\367" - "\367\373\367\367\367\373\366\366\366\373\375\375\375\377\372\372\372\375" - "\367\367\367\373\370\370\370\374\357\357\357\370\364\364\364\372\364\364" - "\364\372\366\366\366\373\371\371\371\374\372\372\372\375\375\375\375\376" - "\364\364\364\372\371\371\371\375\371\371\371\375\370\370\370\374\367\367" - "\367\373\375\375\375\377\365\365\365\373\364\364\364\372\363\363\363\372" - "\366\366\366\373\366\366\366\373\370\370\370\374\373\373\373\375\365\365" - "\365\372\371\371\371\375\371\371\371\375\373\373\373\375\377\377\377\377" - "\370\370\370\374\365\365\365\373\354\354\354\366\361\361\361\371\363\363" - "\363\372\367\367\367\373\377\377\377\377\370\370\370\374\376\376\376\377" - "\371\371\371\375\366\366\366\373\365\365\365\372\370\370\370\374\365\365" - "\365\373\364\364\364\372\364\364\364\372\364\364\364\372\367\367\367\373" - "\364\364\364\372\371\371\371\375\377\377\377\377\373\373\373\376\374\374" - "\374\376\377\377\377\377\372\372\372\375\371\371\371\375\367\367\367\373" - "\371\371\371\374\367\367\367\373\362\362\362\371\363\363\363\372\370\370" - "\370\374\370\370\370\374\366\366\366\373\366\366\366\373\371\371\371\374" - "\366\366\366\373\365\365\365\372\370\370\370\374\370\370\370\374\370\370" - "\370\374\363\363\363\372\364\364\364\372\370\370\370\374\365\365\365\373" - "\363\363\363\372\365\365\365\373\363\363\363\371\373\373\373\375\372\372" - "\372\375\373\373\373\376\371\371\371\374\373\373\373\376\371\371\371\375" - "\372\372\372\375\371\371\371\374\364\364\364\372\373\373\373\375\364\364" - "\364\372\365\365\365\373\370\370\370\374\375\375\375\376\374\374\374\376" - "\377\377\377\377\376\376\376\377\377\377\377\377\371\371\371\375\370\370" - "\370\374\374\374\374\376\371\371\371\375\370\370\370\374\370\370\370\374" - "\363\363\363\372\373\373\373\376\365\365\365\373\367\367\367\373\375\375" - "\375\377\372\372\372\375\373\373\373\375\371\371\371\374\370\370\370\374" - "\367\367\367\373\371\371\371\374\364\364\364\372\372\372\372\375\366\366" - "\366\373\364\364\364\372\363\363\363\372\366\366\366\373\361\361\361\371" - "\362\362\362\371\366\366\366\373\362\362\362\371\363\363\363\372\373\373" - "\373\375\371\371\371\374\370\370\370\374\371\371\371\375\365\365\365\373" - "\370\370\370\374\370\370\370\374\366\366\366\373\370\370\370\374\364\364" - "\364\372\361\361\361\371\353\353\353\366\364\364\364\372\373\373\373\375" - "\377\377\377\377\373\373\373\376\370\370\370\374\366\366\366\373\367\367" - "\367\373\372\372\372\375\364\364\364\372\362\362\362\371\366\366\366\373" - "\364\364\364\372\371\371\371\374\364\364\364\372\363\363\363\372\364\364" - "\364\372\371\371\371\375\367\367\367\373\372\372\372\375\370\370\370\374" - "\373\373\373\375\373\373\373\376\370\370\370\374\366\366\366\373\356\356" - "\356\367\356\356\356\367\362\362\362\371\372\372\372\375\373\373\373\376" - "\365\365\365\373\371\371\371\375\372\372\372\375\370\370\370\374\371\371" - "\371\375\374\374\374\376\371\371\371\374\365\365\365\373\363\363\363\372" - "\366\366\366\373\370\370\370\374\363\363\363\371\373\373\373\375\371\371" - "\371\375\371\371\371\374\370\370\370\374\377\377\377\377\372\372\372\375" - "\371\371\371\375\372\372\372\375\363\363\363\372\364\364\364\372\373\373" - "\373\375\355\355\355\366\364\364\364\372\370\370\370\374\371\371\371\374" - "\366\366\366\373\372\372\372\375\364\364\364\372\371\371\371\375\372\372" - "\372\375\365\365\365\373\374\374\374\376\362\362\362\371\364\364\364\372" - "\367\367\367\373\363\363\363\372\364\364\364\372\364\364\364\372\371\371" - "\371\375\377\377\377\377\364\364\364\372\372\372\372\375\377\377\377\377" - "\375\375\375\377\371\371\371\375\373\373\373\376\361\361\361\371\377\377" - "\377\377\370\370\370\374\377\377\377\377\373\373\373\375\366\366\366\373" - "\377\377\377\377\377\377\377\377\377\377\377\377\371\371\371\374\374\374" - "\374\376\377\377\377\377\377\377\377\377\371\371\371\374\366\366\366\373" - "\371\371\371\374\362\362\362\371\370\370\370\374\375\375\375\377\371\371" - "\371\375\373\373\373\375\371\371\371\375\377\377\377\377\373\373\373\376" - "\371\371\371\375\366\366\366\373\373\373\373\375\372\372\372\375\367\367" - "\367\373\363\363\363\371\366\366\366\373\363\363\363\372\371\371\371\374" - "\363\363\363\371\355\355\355\367\364\364\364\372\364\364\364\372\364\364" - "\364\372\364\364\364\372\373\373\373\376\367\367\367\373\365\365\365\372" - "\367\367\367\373\367\367\367\373\365\365\365\372\365\365\365\372\370\370" - "\370\374\363\363\363\371\370\370\370\374\371\371\371\374\363\363\363\371" - "\373\373\373\375\374\374\374\376\371\371\371\374\374\374\374\376\373\373" - "\373\375\370\370\370\374\364\364\364\372\364\364\364\372\365\365\365\373" - "\370\370\370\374\366\366\366\373\366\366\366\373\366\366\366\373\366\366" - "\366\373\364\364\364\372\363\363\363\372\365\365\365\373\377\377\377\377" - "\370\370\370\374\375\375\375\377\373\373\373\375\365\365\365\373\365\365" - "\365\373\364\364\364\372\363\363\363\372\350\350\350\364\365\365\365\373" - "\371\371\371\374\373\373\373\376\371\371\371\374\366\366\366\373\371\371" - "\371\375\373\373\373\375\367\367\367\373\365\365\365\373\370\370\370\374" - "\365\365\365\372\365\365\365\373\363\363\363\371\364\364\364\372\365\365" - "\365\373\373\373\373\375\371\371\371\374\371\371\371\375\370\370\370\374" - "\373\373\373\375\371\371\371\374\373\373\373\375\373\373\373\375\366\366" - "\366\373\357\357\357\370\362\362\362\371\355\355\355\366\362\362\362\371" - "\370\370\370\374\366\366\366\373\365\365\365\373\373\373\373\376\373\373" - "\373\375\373\373\373\375\373\373\373\376\372\372\372\375\366\366\366\373" - "\366\366\366\373\361\361\361\371\365\365\365\372\357\357\357\370\371\371" - "\371\375\373\373\373\375\363\363\363\371\370\370\370\374\371\371\371\374" - "\377\377\377\377\370\370\370\374\371\371\371\374\372\372\372\375\371\371" - "\371\375\372\372\372\375\372\372\372\375\374\374\374\376\367\367\367\373" - "\366\366\366\373\371\371\371\374\372\372\372\375\377\377\377\377\375\375" - "\375\377\377\377\377\377\377\377\377\377\374\374\374\376\365\365\365\373" - "\364\364\364\372\367\367\367\373\365\365\365\372\364\364\364\372\371\371" - "\371\375\366\366\366\373\374\374\374\376\374\374\374\376\370\370\370\374" - "\374\374\374\376\372\372\372\375\364\364\364\372\371\371\371\374\371\371" - "\371\374\367\367\367\373\366\366\366\373\365\365\365\372\367\367\367\373" - "\364\364\364\372\357\357\357\370\357\357\357\370\363\363\363\371\355\355" - "\355\367\356\356\356\367\366\366\366\373\366\366\366\373\375\375\375\376" - "\370\370\370\374\365\365\365\373\366\366\366\373\365\365\365\372\362\362" - "\362\371\371\371\371\375\366\366\366\373\370\370\370\374\364\364\364\372" - "\370\370\370\374\366\366\366\373\371\371\371\374\373\373\373\376\374\374" - "\374\376\376\376\376\377\372\372\372\375\370\370\370\374\371\371\371\374" - "\362\362\362\371\363\363\363\372\365\365\365\372\367\367\367\373\366\366" - "\366\373\363\363\363\371\374\374\374\376\374\374\374\376\377\377\377\377" - "\374\374\374\376\377\377\377\377\370\370\370\374\377\377\377\377\365\365" - "\365\373\376\376\376\377\366\366\366\373\371\371\371\375\363\363\363\371" - "\373\373\373\375\362\362\362\371\371\371\371\374\370\370\370\374\367\367" - "\367\373\365\365\365\372\364\364\364\372\371\371\371\375\367\367\367\373" - "\374\374\374\376\367\367\367\373\377\377\377\377\362\362\362\371\374\374" - "\374\376\371\371\371\374\363\363\363\371\370\370\370\374\377\377\377\377" - "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\366\366" - "\366\373\377\377\377\377\370\370\370\374\364\364\364\372\356\356\356\367" - "\357\357\357\370\356\356\356\367\370\370\370\374\362\362\362\371\371\371" - "\371\374\370\370\370\374\373\373\373\375\373\373\373\375\372\372\372\375" - "\372\372\372\375\370\370\370\374\365\365\365\372\364\364\364\372\363\363" - "\363\371\364\364\364\372\367\367\367\373\370\370\370\374\365\365\365\372" - "\373\373\373\375\371\371\371\375\374\374\374\376\373\373\373\375\374\374" - "\374\376\376\376\376\377\366\366\366\373\370\370\370\374\372\372\372\375" - "\364\364\364\372\364\364\364\372\365\365\365\372\370\370\370\374\373\373" - "\373\376\372\372\372\375\377\377\377\377\374\374\374\376\374\374\374\376" - "\373\373\373\375\363\363\363\372\365\365\365\372\364\364\364\372\367\367" - "\367\373\366\366\366\373\365\365\365\373\371\371\371\374\372\372\372\375" - "\370\370\370\374\370\370\370\374\373\373\373\375\374\374\374\376\365\365" - "\365\373\370\370\370\374\372\372\372\375\371\371\371\375\363\363\363\372" - "\364\364\364\372\366\366\366\373\367\367\367\373\360\360\360\370\365\365" - "\365\372\365\365\365\372\363\363\363\372\364\364\364\372\365\365\365\372" - "\367\367\367\373\374\374\374\376\366\366\366\373\371\371\371\374\364\364" - "\364\372\370\370\370\374\370\370\370\374\370\370\370\374\366\366\366\373" - "\371\371\371\374\363\363\363\371\363\363\363\371\366\366\366\373\366\366" - "\366\373\374\374\374\376\374\374\374\376\373\373\373\376\374\374\374\376" - "\372\372\372\375\371\371\371\374\366\366\366\373\365\365\365\373\366\366" - "\366\373\365\365\365\373\365\365\365\372\371\371\371\375\370\370\370\374" - "\373\373\373\376\371\371\371\374\376\376\376\377\377\377\377\377\366\366" - "\366\373\371\371\371\374\373\373\373\375\364\364\364\372\370\370\370\374" - "\365\365\365\372\366\366\366\373\364\364\364\372\371\371\371\375\365\365" - "\365\372\370\370\370\374\366\366\366\373\363\363\363\371\366\366\366\373" - "\372\372\372\375\375\375\375\377\373\373\373\375\371\371\371\374\366\366" - "\366\373\370\370\370\374\371\371\371\375\365\365\365\372\370\370\370\374" - "\377\377\377\377\367\367\367\373\377\377\377\377\377\377\377\377\377\377" - "\377\377\377\377\377\377\371\371\371\375\377\377\377\377\363\363\363\372" - "\364\364\364\372\356\356\356\367\363\363\363\372\355\355\355\366\365\365" - "\365\373\356\356\356\367\370\370\370\374\375\375\375\377\377\377\377\377" - "\375\375\375\376\373\373\373\375\371\371\371\374\364\364\364\372\365\365" - "\365\373\365\365\365\372\360\360\360\370\362\362\362\371\365\365\365\372" - "\377\377\377\377\364\364\364\372\375\375\375\376\376\376\376\377\376\376" - "\376\377\373\373\373\375\371\371\371\374\375\375\375\377\365\365\365\373" - "\373\373\373\376\370\370\370\374\365\365\365\372\365\365\365\373\365\365" - "\365\373\371\371\371\375\373\373\373\376\374\374\374\376\377\377\377\377" - "\371\371\371\375\371\371\371\375\371\371\371\374\372\372\372\375\371\371" - "\371\374\374\374\374\376\370\370\370\374\371\371\371\375\365\365\365\373" - "\364\364\364\372\366\366\366\373\371\371\371\375\374\374\374\376\374\374" - "\374\376\371\371\371\374\371\371\371\375\371\371\371\374\371\371\371\374" - "\372\372\372\375\371\371\371\374\370\370\370\374\364\364\364\372\364\364" - "\364\372\366\366\366\373\364\364\364\372\363\363\363\372\362\362\362\371" - "\363\363\363\371\367\367\367\373\364\364\364\372\371\371\371\375\372\372" - "\372\375\371\371\371\374\371\371\371\375\372\372\372\375\372\372\372\375" - "\366\366\366\373\370\370\370\374\366\366\366\373\364\364\364\372\371\371" - "\371\375\366\366\366\373\370\370\370\374\375\375\375\376\375\375\375\377" - "\373\373\373\375\373\373\373\375\371\371\371\374\367\367\367\373\370\370" - "\370\374\365\365\365\372\371\371\371\374\365\365\365\372\366\366\366\373" - "\366\366\366\373\373\373\373\376\373\373\373\375\377\377\377\377\373\373" - "\373\376\377\377\377\377\375\375\375\376\374\374\374\376\365\365\365\373" - "\365\365\365\373\371\371\371\375\362\362\362\371\363\363\363\371\360\360" - "\360\370\363\363\363\371\365\365\365\373\365\365\365\372\371\371\371\374" - "\371\371\371\375\370\370\370\374\375\375\375\377\373\373\373\375\371\371" - "\371\375\372\372\372\375\371\371\371\374\364\364\364\372\365\365\365\372" - "\364\364\364\372\371\371\371\375\371\371\371\374\371\371\371\375\375\375" - "\375\377\377\377\377\377\377\377\377\377\377\377\377\377\367\367\367\373" - "\362\362\362\371\371\371\371\375\370\370\370\374\361\361\361\371\364\364" - "\364\372\362\362\362\371\364\364\364\372\356\356\356\367\365\365\365\373" - "\375\375\375\377\377\377\377\377\373\373\373\376\374\374\374\376\366\366" - "\366\373\367\367\367\373\367\367\367\373\364\364\364\372\363\363\363\372" - "\366\366\366\373\364\364\364\372\375\375\375\376\374\374\374\376\374\374" - "\374\376\377\377\377\377\377\377\377\377\371\371\371\374\373\373\373\375" - "\373\373\373\375\375\375\375\376\365\365\365\372\362\362\362\371\365\365" - "\365\373\364\364\364\372\367\367\367\373\371\371\371\374\373\373\373\375" - "\377\377\377\377\373\373\373\376\373\373\373\375\375\375\375\376\365\365" - "\365\373\374\374\374\376\365\365\365\373\370\370\370\374\366\366\366\373" - "\373\373\373\375\365\365\365\373\360\360\360\370\363\363\363\372\373\373" - "\373\376\372\372\372\375\371\371\371\375\374\374\374\376\364\364\364\372" - "\364\364\364\372\371\371\371\375\372\372\372\375\374\374\374\376\370\370" - "\370\374\366\366\366\373\365\365\365\372\365\365\365\372\370\370\370\374" - "\366\366\366\373\362\362\362\371\362\362\362\371\373\373\373\376\371\371" - "\371\374\372\372\372\375\371\371\371\375\371\371\371\374\375\375\375\377" - "\371\371\371\375\376\376\376\377\367\367\367\373\373\373\373\376\364\364" - "\364\372\366\366\366\373\363\363\363\371\372\372\372\375\366\366\366\373" - "\375\375\375\376\371\371\371\374\371\371\371\374\371\371\371\374\376\376" - "\376\377\374\374\374\376\373\373\373\376\370\370\370\374\364\364\364\372" - "\366\366\366\373\365\365\365\373\367\367\367\373\365\365\365\373\370\370" - "\370\374\373\373\373\376\377\377\377\377\377\377\377\377\377\377\377\377" - "\371\371\371\375\370\370\370\374\366\366\366\373\366\366\366\373\367\367" - "\367\373\361\361\361\371\362\362\362\371\362\362\362\371\362\362\362\371" - "\371\371\371\374\365\365\365\373\364\364\364\372\370\370\370\374\366\366" - "\366\373\373\373\373\376\373\373\373\375\373\373\373\375\370\370\370\374" - "\363\363\363\371\370\370\370\374\366\366\366\373\366\366\366\373\367\367" - "\367\373\374\374\374\376\374\374\374\376\377\377\377\377\377\377\377\377" - "\372\372\372\375\367\367\367\373\367\367\367\373\366\366\366\373\363\363" - "\363\372\362\362\362\371\364\364\364\372\354\354\354\366\361\361\361\371" - "\370\370\370\374\370\370\370\374\366\366\366\373\370\370\370\374\373\373" - "\373\375\376\376\376\377\373\373\373\376\364\364\364\372\364\364\364\372" - "\364\364\364\372\365\365\365\372\375\375\375\377\365\365\365\373\371\371" - "\371\375\367\367\367\373\377\377\377\377\377\377\377\377\374\374\374\376" - "\377\377\377\377\374\374\374\376\376\376\376\377\371\371\371\375\367\367" - "\367\373\365\365\365\373\371\371\371\375\372\372\372\375\366\366\366\373" - "\372\372\372\375\370\370\370\374\374\374\374\376\377\377\377\377\377\377" - "\377\377\363\363\363\372\372\372\372\375\374\374\374\376\371\371\371\374" - "\367\367\367\373\374\374\374\376\372\372\372\375\360\360\360\370\374\374" - "\374\376\355\355\355\367\367\367\367\373\365\365\365\373\364\364\364\372" - "\365\365\365\372\370\370\370\374\370\370\370\374\373\373\373\375\371\371" - "\371\374\374\374\374\376\364\364\364\372\366\366\366\373\364\364\364\372" - "\364\364\364\372\365\365\365\372\364\364\364\372\364\364\364\372\375\375" - "\375\377\365\365\365\373\371\371\371\375\370\370\370\374\366\366\366\373" - "\372\372\372\375\366\366\366\373\366\366\366\373\371\371\371\375\372\372" - "\372\375\370\370\370\374\367\367\367\373\373\373\373\375\365\365\365\373" - "\374\374\374\376\363\363\363\372\377\377\377\377\377\377\377\377\376\376" - "\376\377\373\373\373\376\370\370\370\374\374\374\374\376\373\373\373\375" - "\366\366\366\373\374\374\374\376\374\374\374\376\364\364\364\372\365\365" - "\365\373\366\366\366\373\367\367\367\373\375\375\375\377\377\377\377\377" - "\374\374\374\376\372\372\372\375\366\366\366\373\370\370\370\374\363\363" - "\363\371\362\362\362\371\362\362\362\371\363\363\363\372\365\365\365\373" - "\363\363\363\371\364\364\364\372\363\363\363\372\364\364\364\372\377\377" - "\377\377\364\364\364\372\373\373\373\376\375\375\375\376\371\371\371\374" - "\372\372\372\375\372\372\372\375\371\371\371\374\366\366\366\373\376\376" - "\376\377\370\370\370\374\374\374\374\376\366\366\366\373\371\371\371\375" - "\371\371\371\375\377\377\377\377\365\365\365\373\370\370\370\374\370\370" - "\370\374\365\365\365\372\366\366\366\373\364\364\364\372\356\356\356\367" - "\355\355\355\367\365\365\365\373\366\366\366\373\364\364\364\372\370\370" - "\370\374\371\371\371\375\375\375\375\376\375\375\375\377\373\373\373\375" - "\370\370\370\374\364\364\364\372\363\363\363\372\356\356\356\367\371\371" - "\371\374\371\371\371\374\370\370\370\374\374\374\374\376\376\376\376\377" - "\377\377\377\377\375\375\375\377\374\374\374\376\371\371\371\375\376\376" - "\376\377\364\364\364\372\370\370\370\374\364\364\364\372\370\370\370\374" - "\363\363\363\372\365\365\365\373\372\372\372\375\371\371\371\375\370\370" - "\370\374\377\377\377\377\374\374\374\376\371\371\371\374\371\371\371\374" - "\370\370\370\374\375\375\375\376\372\372\372\375\373\373\373\376\366\366" - "\366\373\361\361\361\371\363\363\363\371\366\366\366\373\366\366\366\373" - "\366\366\366\373\370\370\370\374\364\364\364\372\370\370\370\374\363\363" - "\363\372\364\364\364\372\366\366\366\373\370\370\370\374\366\366\366\373" - "\371\371\371\374\363\363\363\371\364\364\364\372\362\362\362\371\361\361" - "\361\371\366\366\366\373\365\365\365\372\366\366\366\373\365\365\365\373" - "\365\365\365\372\370\370\370\374\365\365\365\373\366\366\366\373\370\370" - "\370\374\365\365\365\372\370\370\370\374\370\370\370\374\366\366\366\373" - "\377\377\377\377\364\364\364\372\366\366\366\373\367\367\367\373\376\376" - "\376\377\375\375\375\377\371\371\371\375\373\373\373\375\371\371\371\375" - "\370\370\370\374\371\371\371\374\375\375\375\377\354\354\354\366\363\363" - "\363\371\357\357\357\370\366\366\366\373\372\372\372\375\371\371\371\374" - "\366\366\366\373\372\372\372\375\371\371\371\375\367\367\367\373\365\365" - "\365\372\370\370\370\374\364\364\364\372\356\356\356\367\354\354\354\366" - "\362\362\362\371\363\363\363\372\355\355\355\366\354\354\354\366\373\373" - "\373\376\355\355\355\367\377\377\377\377\370\370\370\374\377\377\377\377" - "\376\376\376\377\375\375\375\376\372\372\372\375\371\371\371\374\373\373" - "\373\376\365\365\365\372\373\373\373\376\373\373\373\375\366\366\366\373" - "\370\370\370\374\373\373\373\375\371\371\371\375\370\370\370\374\362\362" - "\362\371\366\366\366\373\374\374\374\376\367\367\367\373\370\370\370\374" - "\366\366\366\373\362\362\362\371\364\364\364\372\363\363\363\372\365\365" - "\365\373\367\367\367\373\371\371\371\375\377\377\377\377\371\371\371\374" - "\371\371\371\374\373\373\373\376\366\366\366\373\366\366\366\373\362\362" - "\362\371\371\371\371\375\372\372\372\375\371\371\371\374\375\375\375\377" - "\375\375\375\377\371\371\371\375\371\371\371\375\374\374\374\376\373\373" - "\373\375\374\374\374\376\364\364\364\372\371\371\371\374\371\371\371\375" - "\374\374\374\376\365\365\365\372\370\370\370\374\366\366\366\373\367\367" - "\367\373\375\375\375\376\373\373\373\375\367\367\367\373\367\367\367\373" - "\372\372\372\375\366\366\366\373\373\373\373\375\371\371\371\375\366\366" - "\366\373\370\370\370\374\364\364\364\372\357\357\357\370\370\370\370\374" - "\366\366\366\373\367\367\367\373\365\365\365\373\365\365\365\372\365\365" - "\365\373\373\373\373\376\371\371\371\374\365\365\365\373\370\370\370\374" - "\371\371\371\374\360\360\360\370\363\363\363\372\362\362\362\371\364\364" - "\364\372\361\361\361\371\365\365\365\372\361\361\361\371\362\362\362\371" - "\364\364\364\372\365\365\365\372\364\364\364\372\363\363\363\371\364\364" - "\364\372\370\370\370\374\364\364\364\372\370\370\370\374\367\367\367\373" - "\370\370\370\374\372\372\372\375\370\370\370\374\362\362\362\371\374\374" - "\374\376\367\367\367\373\374\374\374\376\375\375\375\377\375\375\375\377" - "\367\367\367\373\371\371\371\374\372\372\372\375\375\375\375\377\372\372" - "\372\375\373\373\373\375\371\371\371\375\362\362\362\371\354\354\354\366" - "\371\371\371\374\371\371\371\375\371\371\371\374\375\375\375\376\366\366" - "\366\373\366\366\366\373\364\364\364\372\362\362\362\371\355\355\355\367" - "\362\362\362\371\362\362\362\371\356\356\356\367\366\366\366\373\361\361" - "\361\371\355\355\355\367\366\366\366\373\370\370\370\374\377\377\377\377" - "\366\366\366\373\377\377\377\377\367\367\367\373\370\370\370\374\366\366" - "\366\373\366\366\366\373\371\371\371\375\365\365\365\373\366\366\366\373" - "\370\370\370\374\366\366\366\373\371\371\371\375\371\371\371\375\373\373" - "\373\375\372\372\372\375\367\367\367\373\371\371\371\374\377\377\377\377" - "\365\365\365\373\370\370\370\374\367\367\367\373\367\367\367\373\361\361" - "\361\371\363\363\363\372\365\365\365\373\370\370\370\374\372\372\372\375" - "\377\377\377\377\374\374\374\376\363\363\363\371\373\373\373\375\363\363" - "\363\371\366\366\366\373\365\365\365\373\366\366\366\373\372\372\372\375" - "\372\372\372\375\375\375\375\377\377\377\377\377\374\374\374\376\364\364" - "\364\372\374\374\374\376\374\374\374\376\373\373\373\376\371\371\371\374" - "\365\365\365\372\373\373\373\376\370\370\370\374\364\364\364\372\365\365" - "\365\373\364\364\364\372\364\364\364\372\375\375\375\377\364\364\364\372" - "\364\364\364\372\364\364\364\372\370\370\370\374\370\370\370\374\374\374" - "\374\376\375\375\375\377\356\356\356\367\366\366\366\373\365\365\365\372" - "\357\357\357\370\361\361\361\371\370\370\370\374\367\367\367\373\367\367" - "\367\373\365\365\365\373\364\364\364\372\363\363\363\371\372\372\372\375" - "\371\371\371\374\365\365\365\372\371\371\371\374\365\365\365\373\366\366" - "\366\373\360\360\360\370\354\354\354\366\363\363\363\372\362\362\362\371" - "\362\362\362\371\371\371\371\374\364\364\364\372\370\370\370\374\366\366" - "\366\373\356\356\356\367\364\364\364\372\370\370\370\374\364\364\364\372" - "\373\373\373\376\371\371\371\375\367\367\367\373\371\371\371\374\370\370" - "\370\374\367\367\367\373\364\364\364\372\377\377\377\377\375\375\375\376" - "\370\370\370\374\374\374\374\376\367\367\367\373\374\374\374\376\370\370" - "\370\374\374\374\374\376\366\366\366\373\370\370\370\374\367\367\367\373" - "\374\374\374\376\357\357\357\370\373\373\373\375\371\371\371\374\373\373" - "\373\376\373\373\373\376\373\373\373\375\370\370\370\374\371\371\371\374" - "\374\374\374\376\356\356\356\367\362\362\362\371\363\363\363\372\365\365" - "\365\373\364\364\364\372\357\357\357\370\364\364\364\372\370\370\370\374" - "\377\377\377\377\377\377\377\377\366\366\366\373\372\372\372\375\365\365" - "\365\372\371\371\371\374\371\371\371\374\370\370\370\374\372\372\372\375" - "\372\372\372\375\370\370\370\374\366\366\366\373\371\371\371\374\375\375" - "\375\377\372\372\372\375\370\370\370\374\370\370\370\374\366\366\366\373" - "\370\370\370\374\370\370\370\374\370\370\370\374\370\370\370\374\363\363" - "\363\372\366\366\366\373\363\363\363\371\363\363\363\371\370\370\370\374" - "\373\373\373\375\373\373\373\376\362\362\362\371\366\366\366\373\370\370" - "\370\374\367\367\367\373\366\366\366\373\364\364\364\372\367\367\367\373" - "\371\371\371\374\372\372\372\375\370\370\370\374\376\376\376\377\370\370" - "\370\374\371\371\371\375\376\376\376\377\365\365\365\373\366\366\366\373" - "\366\366\366\373\365\365\365\372\366\366\366\373\365\365\365\372\365\365" - "\365\373\363\363\363\372\364\364\364\372\365\365\365\372\363\363\363\372" - "\365\365\365\372\366\366\366\373\372\372\372\375\375\375\375\376\374\374" - "\374\376\377\377\377\377\366\366\366\373\370\370\370\374\370\370\370\374" - "\361\361\361\371\363\363\363\372\365\365\365\373\363\363\363\372\371\371" - "\371\375\373\373\373\375\365\365\365\373\373\373\373\376\363\363\363\372" - "\371\371\371\374\367\367\367\373\371\371\371\375\365\365\365\373\365\365" - "\365\372\365\365\365\373\362\362\362\371\370\370\370\374\357\357\357\370" - "\362\362\362\371\363\363\363\372\364\364\364\372\366\366\366\373\365\365" - "\365\372\366\366\366\373\366\366\366\373\364\364\364\372\362\362\362\371" - "\366\366\366\373\366\366\366\373\366\366\366\373\371\371\371\374\365\365" - "\365\373\366\366\366\373\371\371\371\375\364\364\364\372\367\367\367\373" - "\371\371\371\375\375\375\375\376\374\374\374\376\366\366\366\373\360\360" - "\360\370\364\364\364\372\371\371\371\375\374\374\374\376\373\373\373\375" - "\373\373\373\375\372\372\372\375\371\371\371\375\373\373\373\375\362\362" - "\362\371\371\371\371\374\372\372\372\375\365\365\365\372\366\366\366\373" - "\373\373\373\375\364\364\364\372\366\366\366\373\366\366\366\373\350\350" - "\350\364\362\362\362\371\370\370\370\374\364\364\364\372\354\354\354\366" - "\356\356\356\367\377\377\377\377\373\373\373\375\371\371\371\374\371\371" - "\371\375\373\373\373\375\370\370\370\374\370\370\370\374\363\363\363\371" - "\374\374\374\376\371\371\371\374\365\365\365\373\362\362\362\371\364\364" - "\364\372\366\366\366\373\366\366\366\373\373\373\373\375\365\365\365\373" - "\367\367\367\373\370\370\370\374\370\370\370\374\365\365\365\373\366\366" - "\366\373\362\362\362\371\363\363\363\372\355\355\355\367\362\362\362\371" - "\357\357\357\370\373\373\373\375\373\373\373\376\365\365\365\372\362\362" - "\362\371\371\371\371\375\373\373\373\375\367\367\367\373\370\370\370\374" - "\372\372\372\375\371\371\371\375\370\370\370\374\372\372\372\375\371\371" - "\371\375\373\373\373\376\363\363\363\372\376\376\376\377\364\364\364\372" - "\373\373\373\375\365\365\365\372\367\367\367\373\371\371\371\374\365\365" - "\365\372\366\366\366\373\364\364\364\372\356\356\356\367\363\363\363\371" - "\365\365\365\373\365\365\365\373\365\365\365\372\366\366\366\373\370\370" - "\370\374\374\374\374\376\375\375\375\377\375\375\375\377\374\374\374\376" - "\370\370\370\374\366\366\366\373\363\363\363\371\364\364\364\372\364\364" - "\364\372\360\360\360\370\363\363\363\372\375\375\375\377\373\373\373\375" - "\372\372\372\375\373\373\373\376\371\371\371\375\375\375\375\376\377\377" - "\377\377\367\367\367\373\366\366\366\373\365\365\365\372\365\365\365\373" - "\366\366\366\373\366\366\366\373\353\353\353\366\363\363\363\372\364\364" - "\364\372\362\362\362\371\364\364\364\372\366\366\366\373\365\365\365\372" - "\360\360\360\370\363\363\363\371\366\366\366\373\370\370\370\374\366\366" - "\366\373\364\364\364\372\371\371\371\375\360\360\360\370\365\365\365\372" - "\360\360\360\370\366\366\366\373\371\371\371\375\373\373\373\376\373\373" - "\373\376\370\370\370\374\373\373\373\376\366\366\366\373\371\371\371\374" - "\373\373\373\375\371\371\371\375\365\365\365\372\371\371\371\374\364\364" - "\364\372\365\365\365\373\364\364\364\372\371\371\371\374\371\371\371\374" - "\365\365\365\373\371\371\371\374\364\364\364\372\367\367\367\373\370\370" - "\370\374\364\364\364\372\361\361\361\371\360\360\360\370\377\377\377\377" - "\361\361\361\371\357\357\357\370\365\365\365\372\367\367\367\373\377\377" - "\377\377\374\374\374\376\377\377\377\377\366\366\366\373\373\373\373\376" - "\373\373\373\375\366\366\366\373\373\373\373\376\370\370\370\374\371\371" - "\371\375\360\360\360\370\367\367\367\373\364\364\364\372\366\366\366\373" - "\373\373\373\375\366\366\366\373\363\363\363\371\370\370\370\374\372\372" - "\372\375\366\366\366\373\366\366\366\373\363\363\363\371\362\362\362\371" - "\360\360\360\370\366\366\366\373\364\364\364\372\371\371\371\374\367\367" - "\367\373\364\364\364\372\364\364\364\372\364\364\364\372\370\370\370\374" - "\367\367\367\373\370\370\370\374\377\377\377\377\367\367\367\373\363\363" - "\363\371\364\364\364\372\367\367\367\373\365\365\365\372\374\374\374\376" - "\377\377\377\377\376\376\376\377\373\373\373\375\372\372\372\375\371\371" - "\371\375\373\373\373\375\373\373\373\375\364\364\364\372\370\370\370\374" - "\366\366\366\373\365\365\365\372\364\364\364\372\364\364\364\372\362\362" - "\362\371\365\365\365\373\371\371\371\375\373\373\373\376\377\377\377\377" - "\376\376\376\377\373\373\373\375\372\372\372\375\365\365\365\372\364\364" - "\364\372\363\363\363\371\365\365\365\373\360\360\360\370\363\363\363\372" - "\370\370\370\374\370\370\370\374\367\367\367\373\367\367\367\373\373\373" - "\373\375\375\375\375\377\377\377\377\377\370\370\370\374\365\365\365\372" - "\364\364\364\372\367\367\367\373\360\360\360\370\363\363\363\371\361\361" - "\361\371\363\363\363\372\364\364\364\372\360\360\360\370\363\363\363\372" - "\361\361\361\371\366\366\366\373\363\363\363\372\362\362\362\371\367\367" - "\367\373\366\366\366\373\366\366\366\373\365\365\365\373\365\365\365\373" - "\365\365\365\373\370\370\370\374\357\357\357\370\370\370\370\374\377\377" - "\377\377\371\371\371\374\366\366\366\373\370\370\370\374\365\365\365\373" - "\371\371\371\375\366\366\366\373\371\371\371\374\374\374\374\376\363\363" - "\363\372\366\366\366\373\363\363\363\372\365\365\365\372\370\370\370\374" - "\375\375\375\376\374\374\374\376\365\365\365\372\374\374\374\376\367\367" - "\367\373\371\371\371\375\365\365\365\372\367\367\367\373\363\363\363\372" - "\360\360\360\370\373\373\373\376\366\366\366\373\365\365\365\373\362\362" - "\362\371\375\375\375\376\372\372\372\375\371\371\371\374\377\377\377\377" - "\373\373\373\376\373\373\373\375\364\364\364\372\365\365\365\372\375\375" - "\375\376\364\364\364\372\363\363\363\372\365\365\365\373\363\363\363\372" - "\371\371\371\374\366\366\366\373\366\366\366\373\373\373\373\375\370\370" - "\370\374\366\366\366\373\367\367\367\373\365\365\365\373\364\364\364\372" - "\364\364\364\372\364\364\364\372\364\364\364\372\357\357\357\370\364\364" - "\364\372\367\367\367\373\373\373\373\375\364\364\364\372\365\365\365\372" - "\365\365\365\373\371\371\371\374\372\372\372\375\365\365\365\373\374\374" - "\374\376\374\374\374\376\365\365\365\373\373\373\373\375\365\365\365\372" - "\365\365\365\372\374\374\374\376\377\377\377\377\377\377\377\377\375\375" - "\375\376\372\372\372\375\371\371\371\375\372\372\372\375\374\374\374\376" - "\365\365\365\373\370\370\370\374\366\366\366\373\370\370\370\374\366\366" - "\366\373\371\371\371\375\362\362\362\371\371\371\371\374\371\371\371\375" - "\370\370\370\374\374\374\374\376\377\377\377\377\372\372\372\375\374\374" - "\374\376\370\370\370\374\366\366\366\373\362\362\362\371\365\365\365\373" - "\364\364\364\372\373\373\373\375\362\362\362\371\370\370\370\374\374\374" - "\374\376\366\366\366\373\377\377\377\377\377\377\377\377\377\377\377\377" - "\374\374\374\376\371\371\371\375\366\366\366\373\365\365\365\372\363\363" - "\363\372\363\363\363\372\363\363\363\372\364\364\364\372\365\365\365\373" - "\355\355\355\367\365\365\365\373\365\365\365\372\365\365\365\372\365\365" - "\365\372\365\365\365\372\364\364\364\372\362\362\362\371\364\364\364\372" - "\373\373\373\375\357\357\357\370\365\365\365\372\362\362\362\371\357\357" - "\357\370\370\370\370\374\376\376\376\377\356\356\356\367\370\370\370\374" - "\373\373\373\375\370\370\370\374\370\370\370\374\367\367\367\373\374\374" - "\374\376\370\370\370\374\365\365\365\373\370\370\370\374\367\367\367\373" - "\366\366\366\373\370\370\370\374\373\373\373\375\372\372\372\375\363\363" - "\363\372\375\375\375\377\367\367\367\373\365\365\365\373\371\371\371\375" - "\363\363\363\372\355\355\355\367\366\366\366\373\365\365\365\372\370\370" - "\370\374\366\366\366\373\365\365\365\373\371\371\371\375\375\375\375\376" - "\373\373\373\376\373\373\373\375\373\373\373\375\373\373\373\376\366\366" - "\366\373\370\370\370\374\365\365\365\373\365\365\365\373\366\366\366\373" - "\360\360\360\370\363\363\363\371\367\367\367\373\370\370\370\374\371\371" - "\371\374\370\370\370\374\373\373\373\376\363\363\363\371\371\371\371\374" - "\370\370\370\374\363\363\363\371\367\367\367\373\363\363\363\371\371\371" - "\371\374\364\364\364\372\372\372\372\375\366\366\366\373\366\366\366\373" - "\373\373\373\375\372\372\372\375\370\370\370\374\372\372\372\375\372\372" - "\372\375\372\372\372\375\376\376\376\377\371\371\371\375\366\366\366\373" - "\374\374\374\376\367\367\367\373\366\366\366\373\365\365\365\373\375\375" - "\375\377\375\375\375\377\372\372\372\375\373\373\373\376\364\364\364\372" - "\364\364\364\372\370\370\370\374\373\373\373\375\364\364\364\372\363\363" - "\363\372\357\357\357\370\361\361\361\371\364\364\364\372\371\371\371\375" - "\373\373\373\375\371\371\371\374\367\367\367\373\377\377\377\377\366\366" - "\366\373\371\371\371\375\371\371\371\374\366\366\366\373\363\363\363\372" - "\364\364\364\372\366\366\366\373\357\357\357\370\367\367\367\373\362\362" - "\362\371\367\367\367\373\373\373\373\375\374\374\374\376\373\373\373\376" - "\372\372\372\375\372\372\372\375\376\376\376\377\366\366\366\373\370\370" - "\370\374\365\365\365\372\367\367\367\373\365\365\365\372\362\362\362\371" - "\363\363\363\372\364\364\364\372\355\355\355\367\371\371\371\375\364\364" - "\364\372\363\363\363\372\367\367\367\373\370\370\370\374\364\364\364\372" - "\361\361\361\371\366\366\366\373\367\367\367\373\364\364\364\372\367\367" - "\367\373\365\365\365\372\371\371\371\374\366\366\366\373\366\366\366\373" - "\371\371\371\374\370\370\370\374\366\366\366\373\375\375\375\377\373\373" - "\373\376\374\374\374\376\370\370\370\374\367\367\367\373\372\372\372\375" - "\366\366\366\373\366\366\366\373\366\366\366\373\365\365\365\373\367\367" - "\367\373\364\364\364\372\366\366\366\373\370\370\370\374\375\375\375\377" - "\364\364\364\372\375\375\375\377\366\366\366\373\362\362\362\371\371\371" - "\371\375\372\372\372\375\375\375\375\377\373\373\373\375\367\367\367\373" - "\371\371\371\375\366\366\366\373\365\365\365\373\373\373\373\376\373\373" - "\373\376\365\365\365\372\375\375\375\376\363\363\363\371\370\370\370\374" - "\371\371\371\374\365\365\365\373\365\365\365\373\365\365\365\372\371\371" - "\371\375\367\367\367\373\377\377\377\377\377\377\377\377\371\371\371\375" - "\363\363\363\372\371\371\371\374\370\370\370\374\363\363\363\372\366\366" - "\366\373\366\366\366\373\371\371\371\374\366\366\366\373\367\367\367\373" - "\366\366\366\373\366\366\366\373\373\373\373\375\371\371\371\374\371\371" - "\371\374\374\374\374\376\371\371\371\374\371\371\371\375\371\371\371\374" - "\374\374\374\376\373\373\373\375\370\370\370\374\367\367\367\373\367\367" - "\367\373\373\373\373\375\370\370\370\374\366\366\366\373\374\374\374\376" - "\372\372\372\375\361\361\361\371\366\366\366\373\375\375\375\376\366\366" - "\366\373\365\365\365\372\364\364\364\372\363\363\363\371\360\360\360\370" - "\365\365\365\372\371\371\371\374\377\377\377\377\370\370\370\374\371\371" - "\371\374\372\372\372\375\373\373\373\375\375\375\375\376\367\367\367\373" - "\371\371\371\374\366\366\366\373\365\365\365\373\364\364\364\372\364\364" - "\364\372\366\366\366\373\367\367\367\373\371\371\371\374\373\373\373\376" - "\375\375\375\376\373\373\373\375\373\373\373\376\375\375\375\377\377\377" - "\377\377\363\363\363\372\366\366\366\373\364\364\364\372\367\367\367\373" - "\364\364\364\372\362\362\362\371\360\360\360\370\356\356\356\367\363\363" - "\363\372\367\367\367\373\372\372\372\375\362\362\362\371\370\370\370\374" - "\367\367\367\373\365\365\365\373\366\366\366\373\366\366\366\373\366\366" - "\366\373\364\364\364\372\370\370\370\374\364\364\364\372\366\366\366\373" - "\366\366\366\373\366\366\366\373\370\370\370\374\371\371\371\374\372\372" - "\372\375\370\370\370\374\373\373\373\376\370\370\370\374\371\371\371\374" - "\362\362\362\371\366\366\366\373\365\365\365\372\350\350\350\364\363\363" - "\363\371\366\366\366\373\367\367\367\373\370\370\370\374\367\367\367\373" - "\367\367\367\373\377\377\377\377\371\371\371\375\374\374\374\376\371\371" - "\371\375\366\366\366\373\366\366\366\373\367\367\367\373\374\374\374\376" - "\374\374\374\376\366\366\366\373\374\374\374\376\377\377\377\377\377\377" - "\377\377\374\374\374\376\370\370\370\374\371\371\371\375\370\370\370\374" - "\365\365\365\373\373\373\373\376\364\364\364\372\370\370\370\374\363\363" - "\363\372\364\364\364\372\373\373\373\376\365\365\365\373\373\373\373\376" - "\372\372\372\375\364\364\364\372\363\363\363\372\366\366\366\373\370\370" - "\370\374\370\370\370\374\371\371\371\375\371\371\371\374\367\367\367\373" - "\363\363\363\372\366\366\366\373\367\367\367\373\373\373\373\375\373\373" - "\373\376\373\373\373\375\372\372\372\375\372\372\372\375\374\374\374\376" - "\375\375\375\377\373\373\373\376\375\375\375\376\374\374\374\376\367\367" - "\367\373\367\367\367\373\365\365\365\372\375\375\375\376\377\377\377\377" - "\366\366\366\373\374\374\374\376\371\371\371\375\365\365\365\372\373\373" - "\373\375\370\370\370\374\363\363\363\371\365\365\365\372\365\365\365\372" - "\364\364\364\372\366\366\366\373\372\372\372\375\373\373\373\376\374\374" - "\374\376\374\374\374\376\373\373\373\375\372\372\372\375\366\366\366\373" - "\374\374\374\376\372\372\372\375\366\366\366\373\371\371\371\375\363\363" - "\363\371\363\363\363\371\364\364\364\372\365\365\365\372\372\372\372\375" - "\377\377\377\377\377\377\377\377\377\377\377\377\375\375\375\377\375\375" - "\375\376\373\373\373\376\375\375\375\376\366\366\366\373\366\366\366\373" - "\363\363\363\372\355\355\355\366\361\361\361\371\364\364\364\372\363\363" - "\363\372\362\362\362\371\363\363\363\371\366\366\366\373\373\373\373\375" - "\370\370\370\374\370\370\370\374\370\370\370\374\366\366\366\373\372\372" - "\372\375\367\367\367\373\370\370\370\374\364\364\364\372\363\363\363\372" - "\366\366\366\373\364\364\364\372\364\364\364\372\366\366\366\373\371\371" - "\371\374\371\371\371\375\373\373\373\376\375\375\375\377\371\371\371\374" - "\373\373\373\376\371\371\371\375\375\375\375\377\367\367\367\373\363\363" - "\363\372\351\351\351\365\363\363\363\372\366\366\366\373\370\370\370\374" - "\364\364\364\372\367\367\367\373\364\364\364\372\371\371\371\374\370\370" - "\370\374\366\366\366\373\370\370\370\374\372\372\372\375\364\364\364\372" - "\365\365\365\372\373\373\373\376\372\372\372\375\373\373\373\376\373\373" - "\373\375\374\374\374\376\364\364\364\372\372\372\372\375\366\366\366\373" - "\366\366\366\373\370\370\370\374\373\373\373\375\371\371\371\375\374\374" - "\374\376\371\371\371\375\364\364\364\372\372\372\372\375\373\373\373\375" - "\370\370\370\374\373\373\373\376\367\367\367\373\356\356\356\367\365\365" - "\365\372\366\366\366\373\367\367\367\373\370\370\370\374\366\366\366\373" - "\371\371\371\374\370\370\370\374\357\357\357\370\362\362\362\371\374\374" - "\374\376\375\375\375\377\374\374\374\376\374\374\374\376\373\373\373\376" - "\373\373\373\376\375\375\375\377\376\376\376\377\377\377\377\377\373\373" - "\373\375\377\377\377\377\371\371\371\374\366\366\366\373\363\363\363\372" - "\367\367\367\373\370\370\370\374\371\371\371\375\373\373\373\375\364\364" - "\364\372\377\377\377\377\377\377\377\377\361\361\361\371\364\364\364\372" - "\364\364\364\372\365\365\365\372\371\371\371\374\372\372\372\375\371\371" - "\371\374\371\371\371\374\371\371\371\374\374\374\374\376\366\366\366\373" - "\371\371\371\374\373\373\373\376\372\372\372\375\372\372\372\375\366\366" - "\366\373\367\367\367\373\370\370\370\374\367\367\367\373\363\363\363\371" - "\370\370\370\374\370\370\370\374\377\377\377\377\373\373\373\376\377\377" - "\377\377\374\374\374\376\375\375\375\376\376\376\376\377\377\377\377\377" - "\373\373\373\375\367\367\367\373\362\362\362\371\354\354\354\366\362\362" - "\362\371\362\362\362\371\365\365\365\373\361\361\361\371\362\362\362\371" - "\365\365\365\372\365\365\365\373\371\371\371\374\366\366\366\373\366\366" - "\366\373\367\367\367\373\374\374\374\376\375\375\375\377\372\372\372\375" - "\373\373\373\376\367\367\367\373\366\366\366\373\363\363\363\371\366\366" - "\366\373\366\366\366\373\374\374\374\376\377\377\377\377\374\374\374\376" - "\374\374\374\376\374\374\374\376\373\373\373\375\374\374\374\376\371\371" - "\371\374\377\377\377\377\364\364\364\372\362\362\362\371\363\363\363\372" - "\371\371\371\375\367\367\367\373\366\366\366\373\370\370\370\374\366\366" - "\366\373\366\366\366\373\371\371\371\374\366\366\366\373\364\364\364\372" - "\371\371\371\375\366\366\366\373\370\370\370\374\374\374\374\376\376\376" - "\376\377\364\364\364\372\377\377\377\377\375\375\375\376\377\377\377\377" - "\365\365\365\373\366\366\366\373\356\356\356\367\367\367\367\373\371\371" - "\371\375\373\373\373\376\371\371\371\375\374\374\374\376\371\371\371\375" - "\370\370\370\374\371\371\371\375\370\370\370\374\366\366\366\373\364\364" - "\364\372\365\365\365\373\371\371\371\374\364\364\364\372\364\364\364\372" - "\366\366\366\373\366\366\366\373\366\366\366\373\364\364\364\372\365\365" - "\365\372\365\365\365\373\371\371\371\374\371\371\371\375\374\374\374\376" - "\377\377\377\377\374\374\374\376\371\371\371\375\377\377\377\377\377\377" - "\377\377\377\377\377\377\375\375\375\376\364\364\364\372\373\373\373\376" - "\370\370\370\374\374\374\374\376\363\363\363\372\371\371\371\374\375\375" - "\375\376\376\376\376\377\373\373\373\375\371\371\371\374\371\371\371\375" - "\372\372\372\375\364\364\364\372\370\370\370\374\371\371\371\375\371\371" - "\371\375\371\371\371\374\377\377\377\377\372\372\372\375\373\373\373\376" - "\372\372\372\375\373\373\373\375\373\373\373\376\371\371\371\374\370\370" - "\370\374\372\372\372\375\375\375\375\377\371\371\371\375\366\366\366\373" - "\364\364\364\372\371\371\371\375\367\367\367\373\372\372\372\375\374\374" - "\374\376\374\374\374\376\374\374\374\376\374\374\374\376\373\373\373\375" - "\373\373\373\376\372\372\372\375\373\373\373\376\373\373\373\375\363\363" - "\363\372\363\363\363\371\364\364\364\372\364\364\364\372\365\365\365\372" - "\361\361\361\371\364\364\364\372\366\366\366\373\370\370\370\374\372\372" - "\372\375\371\371\371\374\373\373\373\375\375\375\375\377\371\371\371\374" - "\372\372\372\375\375\375\375\377\370\370\370\374\366\366\366\373\362\362" - "\362\371\362\362\362\371\362\362\362\371\370\370\370\374\374\374\374\376" - "\375\375\375\377\377\377\377\377\377\377\377\377\374\374\374\376\375\375" - "\375\377\376\376\376\377\371\371\371\374\370\370\370\374\363\363\363\371" - "\367\367\367\373\357\357\357\370\373\373\373\375\356\356\356\367\367\367" - "\367\373\374\374\374\376\366\366\366\373\363\363\363\371\370\370\370\374" - "\371\371\371\375\365\365\365\373\367\367\367\373\371\371\371\375\364\364" - "\364\372\371\371\371\374\373\373\373\376\371\371\371\375\371\371\371\375" - "\377\377\377\377\376\376\376\377\363\363\363\371\374\374\374\376\371\371" - "\371\374\364\364\364\372\365\365\365\373\366\366\366\373\370\370\370\374" - "\371\371\371\374\366\366\366\373\366\366\366\373\363\363\363\371\366\366" - "\366\373\372\372\372\375\370\370\370\374\370\370\370\374\370\370\370\374" - "\371\371\371\374\371\371\371\374\364\364\364\372\367\367\367\373\362\362" - "\362\371\363\363\363\371\364\364\364\372\363\363\363\371\371\371\371\374" - "\373\373\373\375\366\366\366\373\372\372\372\375\376\376\376\377\377\377" - "\377\377\374\374\374\376\374\374\374\376\372\372\372\375\372\372\372\375" - "\371\371\371\375\371\371\371\375\371\371\371\375\371\371\371\374\371\371" - "\371\374\371\371\371\374\377\377\377\377\377\377\377\377\370\370\370\374" - "\376\376\376\377\366\366\366\373\373\373\373\376\365\365\365\373\366\366" - "\366\373\370\370\370\374\366\366\366\373\373\373\373\375\371\371\371\375" - "\371\371\371\375\371\371\371\375\373\373\373\376\371\371\371\375\371\371" - "\371\375\373\373\373\376\373\373\373\375\371\371\371\375\371\371\371\375" - "\372\372\372\375\365\365\365\372\370\370\370\374\370\370\370\374\363\363" - "\363\372\371\371\371\375\373\373\373\376\372\372\372\375\377\377\377\377" - "\373\373\373\375\373\373\373\376\377\377\377\377\373\373\373\375\367\367" - "\367\373\366\366\366\373\363\363\363\372\363\363\363\371\363\363\363\371" - "\364\364\364\372\357\357\357\370\364\364\364\372\370\370\370\374\366\366" - "\366\373\371\371\371\374\366\366\366\373\371\371\371\374\372\372\372\375" - "\366\366\366\373\366\366\366\373\373\373\373\375\371\371\371\375\366\366" - "\366\373\364\364\364\372\365\365\365\373\365\365\365\372\365\365\365\373" - "\370\370\370\374\377\377\377\377\376\376\376\377\377\377\377\377\375\375" - "\375\376\377\377\377\377\372\372\372\375\374\374\374\376\370\370\370\374" - "\364\364\364\372\371\371\371\375\362\362\362\371\363\363\363\371\366\366" - "\366\373\363\363\363\371\364\364\364\372\371\371\371\374\365\365\365\373" - "\365\365\365\373\366\366\366\373\367\367\367\373\367\367\367\373\367\367" - "\367\373\371\371\371\374\367\367\367\373\370\370\370\374\371\371\371\374" - "\366\366\366\373\370\370\370\374\370\370\370\374\370\370\370\374\370\370" - "\370\374\367\367\367\373\373\373\373\375\374\374\374\376\370\370\370\374" - "\370\370\370\374\367\367\367\373\364\364\364\372\366\366\366\373\364\364" - "\364\372\360\360\360\370\371\371\371\374\365\365\365\372\364\364\364\372" - "\365\365\365\373\363\363\363\372\366\366\366\373\365\365\365\373\355\355" - "\355\367\363\363\363\371\356\356\356\367\362\362\362\371\362\362\362\371" - "\362\362\362\371\365\365\365\373\367\367\367\373\366\366\366\373\371\371" - "\371\374\374\374\374\376\377\377\377\377\363\363\363\371\373\373\373\376" - "\371\371\371\374\370\370\370\374\371\371\371\374\371\371\371\375\374\374" - "\374\376\371\371\371\375\377\377\377\377\377\377\377\377\377\377\377\377" - "\377\377\377\377\371\371\371\375\374\374\374\376\373\373\373\376\374\374" - "\374\376\371\371\371\374\363\363\363\372\366\366\366\373\365\365\365\373" - "\365\365\365\373\366\366\366\373\371\371\371\374\374\374\374\376\373\373" - "\373\375\372\372\372\375\377\377\377\377\374\374\374\376\370\370\370\374" - "\371\371\371\374\371\371\371\374\377\377\377\377\370\370\370\374\372\372" - "\372\375\371\371\371\375\374\374\374\376\374\374\374\376\377\377\377\377" - "\377\377\377\377\377\377\377\377\374\374\374\376\375\375\375\376\376\376" - "\376\377\375\375\375\377\370\370\370\374\366\366\366\373\365\365\365\372" - "\360\360\360\370\362\362\362\371\357\357\357\370\363\363\363\372\363\363" - "\363\372\366\366\366\373\364\364\364\372\366\366\366\373\366\366\366\373" - "\367\367\367\373\371\371\371\374\365\365\365\373\366\366\366\373\366\366" - "\366\373\371\371\371\374\366\366\366\373\363\363\363\372\364\364\364\372" - "\371\371\371\374\366\366\366\373\374\374\374\376\371\371\371\375\374\374" - "\374\376\376\376\376\377\376\376\376\377\371\371\371\374\371\371\371\375" - "\370\370\370\374\366\366\366\373\363\363\363\372\364\364\364\372\361\361" - "\361\371\356\356\356\367\361\361\361\371\370\370\370\374\373\373\373\375" - "\372\372\372\375\366\366\366\373\366\366\366\373\373\373\373\375\371\371" - "\371\374\367\367\367\373\371\371\371\374\367\367\367\373\363\363\363\372" - "\371\371\371\374\372\372\372\375\372\372\372\375\374\374\374\376\364\364" - "\364\372\357\357\357\370\363\363\363\372\366\366\366\373\365\365\365\373" - "\363\363\363\372\362\362\362\371\361\361\361\371\366\366\366\373\364\364" - "\364\372\370\370\370\374\366\366\366\373\370\370\370\374\360\360\360\370" - "\366\366\366\373\371\371\371\375\371\371\371\374\366\366\366\373\371\371" - "\371\375\366\366\366\373\363\363\363\372\362\362\362\371\364\364\364\372" - "\357\357\357\370\362\362\362\371\361\361\361\371\365\365\365\372\366\366" - "\366\373\375\375\375\377\375\375\375\377\371\371\371\375\371\371\371\374" - "\377\377\377\377\377\377\377\377\374\374\374\376\372\372\372\375\375\375" - "\375\376\374\374\374\376\370\370\370\374\372\372\372\375\374\374\374\376" - "\375\375\375\377\377\377\377\377\377\377\377\377\373\373\373\375\373\373" - "\373\375\370\370\370\374\371\371\371\374\371\371\371\375\365\365\365\373" - "\363\363\363\372\357\357\357\370\364\364\364\372\372\372\372\375\367\367" - "\367\373\371\371\371\374\375\375\375\376\374\374\374\376\375\375\375\376" - "\372\372\372\375\370\370\370\374\366\366\366\373\364\364\364\372\371\371" - "\371\374\373\373\373\375\373\373\373\376\370\370\370\374\374\374\374\376" - "\370\370\370\374\377\377\377\377\376\376\376\377\377\377\377\377\371\371" - "\371\375\367\367\367\373\371\371\371\375\375\375\375\376\363\363\363\372" - "\364\364\364\372\361\361\361\371\355\355\355\367\356\356\356\367\357\357" - "\357\370\364\364\364\372\366\366\366\373\367\367\367\373\365\365\365\372" - "\364\364\364\372\371\371\371\374\370\370\370\374\365\365\365\373\366\366" - "\366\373\366\366\366\373\365\365\365\373\371\371\371\375\366\366\366\373" - "\364\364\364\372\366\366\366\373\373\373\373\375\367\367\367\373\377\377" - "\377\377\371\371\371\375\371\371\371\375\374\374\374\376\376\376\376\377" - "\371\371\371\375\373\373\373\376\370\370\370\374\364\364\364\372\373\373" - "\373\375\363\363\363\371\363\363\363\371\364\364\364\372\350\350\350\364" - "\375\375\375\376\377\377\377\377\372\372\372\375\371\371\371\375\371\371" - "\371\375\376\376\376\377\371\371\371\374\367\367\367\373\375\375\375\377" - "\374\374\374\376\364\364\364\372\365\365\365\372\371\371\371\375\370\370" - "\370\374\373\373\373\375\373\373\373\375\373\373\373\375\375\375\375\377" - "\375\375\375\377\373\373\373\376\365\365\365\373\371\371\371\375\364\364" - "\364\372\370\370\370\374\370\370\370\374\370\370\370\374\367\367\367\373" - "\370\370\370\374\370\370\370\374\371\371\371\375\375\375\375\376\365\365" - "\365\373\365\365\365\372\354\354\354\366\367\367\367\373\363\363\363\371" - "\362\362\362\371\361\361\361\371\360\360\360\370\362\362\362\371\362\362" - "\362\371\370\370\370\374\364\364\364\372\371\371\371\374\374\374\374\376" - "\370\370\370\374\373\373\373\376\377\377\377\377\373\373\373\375\375\375" - "\375\377\365\365\365\373\370\370\370\374\366\366\366\373\366\366\366\373" - "\372\372\372\375\370\370\370\374\371\371\371\374\373\373\373\376\377\377" - "\377\377\374\374\374\376\372\372\372\375\370\370\370\374\371\371\371\374" - "\371\371\371\374\363\363\363\371\363\363\363\371\364\364\364\372\365\365" - "\365\373\366\366\366\373\366\366\366\373\373\373\373\375\364\364\364\372" - "\366\366\366\373\370\370\370\374\365\365\365\372\362\362\362\371\365\365" - "\365\373\367\367\367\373\362\362\362\371\355\355\355\366\362\362\362\371" - "\355\355\355\367\371\371\371\375\370\370\370\374\375\375\375\377\371\371" - "\371\375\373\373\373\376\374\374\374\376\366\366\366\373\371\371\371\375" - "\363\363\363\371\366\366\366\373\366\366\366\373\355\355\355\367\357\357" - "\357\370\366\366\366\373\352\352\352\365\365\365\365\372\370\370\370\374" - "\362\362\362\371\366\366\366\373\370\370\370\374\373\373\373\376\370\370" - "\370\374\367\367\367\373\374\374\374\376\372\372\372\375\371\371\371\374" - "\373\373\373\376\371\371\371\375\373\373\373\376\360\360\360\370\372\372" - "\372\375\373\373\373\376\371\371\371\374\370\370\370\374\374\374\374\376" - "\371\371\371\375\373\373\373\375\365\365\365\373\370\370\370\374\365\365" - "\365\373\372\372\372\375\365\365\365\373\362\362\362\371\363\363\363\372" - "\366\366\366\373\365\365\365\373\374\374\374\376\370\370\370\374\372\372" - "\372\375\372\372\372\375\370\370\370\374\375\375\375\376\365\365\365\373" - "\366\366\366\373\373\373\373\376\365\365\365\373\371\371\371\375\366\366" - "\366\373\367\367\367\373\357\357\357\370\356\356\356\367\363\363\363\372" - "\373\373\373\375\373\373\373\375\366\366\366\373\362\362\362\371\366\366" - "\366\373\366\366\366\373\363\363\363\372\370\370\370\374\367\367\367\373" - "\363\363\363\371\363\363\363\372\365\365\365\372\366\366\366\373\371\371" - "\371\375\373\373\373\375\371\371\371\375\365\365\365\373\364\364\364\372" - "\366\366\366\373\362\362\362\371\357\357\357\370\371\371\371\374\354\354" - "\354\366\356\356\356\367\364\364\364\372\360\360\360\370\370\370\370\374" - "\372\372\372\375\375\375\375\376\374\374\374\376\377\377\377\377\375\375" - "\375\377\372\372\372\375\372\372\372\375\367\367\367\373\374\374\374\376" - "\366\366\366\373\366\366\366\373\366\366\366\373\364\364\364\372\366\366" - "\366\373\372\372\372\375\374\374\374\376\373\373\373\376\371\371\371\375" - "\370\370\370\374\375\375\375\376\370\370\370\374\367\367\367\373\361\361" - "\361\371\364\364\364\372\366\366\366\373\365\365\365\372\372\372\372\375" - "\373\373\373\375\362\362\362\371\364\364\364\372\364\364\364\372\356\356" - "\356\367\363\363\363\372\365\365\365\372\357\357\357\370\364\364\364\372" - "\361\361\361\371\365\365\365\372\364\364\364\372\366\366\366\373\377\377" - "\377\377\377\377\377\377\373\373\373\376\377\377\377\377\373\373\373\375" - "\372\372\372\375\371\371\371\375\365\365\365\373\372\372\372\375\360\360" - "\360\370\352\352\352\365\360\360\360\370\366\366\366\373\361\361\361\371" - "\365\365\365\373\364\364\364\372\364\364\364\372\364\364\364\372\370\370" - "\370\374\371\371\371\374\367\367\367\373\366\366\366\373\370\370\370\374" - "\374\374\374\376\364\364\364\372\365\365\365\373\364\364\364\372\364\364" - "\364\372\350\350\350\364\362\362\362\371\364\364\364\372\364\364\364\372" - "\365\365\365\372\371\371\371\374\371\371\371\374\372\372\372\375\366\366" - "\366\373\364\364\364\372\374\374\374\376\364\364\364\372\364\364\364\372" - "\365\365\365\372\361\361\361\371\357\357\357\370\365\365\365\373\365\365" - "\365\373\370\370\370\374\371\371\371\375\375\375\375\376\377\377\377\377" - "\371\371\371\375\371\371\371\375\375\375\375\376\373\373\373\375\365\365" - "\365\372\374\374\374\376\357\357\357\370\362\362\362\371\362\362\362\371" - "\371\371\371\374\371\371\371\375\372\372\372\375\373\373\373\375\364\364" - "\364\372\365\365\365\372\363\363\363\372\364\364\364\372\375\375\375\377" - "\363\363\363\372\363\363\363\372\363\363\363\371\357\357\357\370\366\366" - "\366\373\366\366\366\373\372\372\372\375\373\373\373\375\374\374\374\376" - "\362\362\362\371\362\362\362\371\371\371\371\374\370\370\370\374\365\365" - "\365\373\363\363\363\371\355\355\355\367\360\360\360\370\357\357\357\370" - "\365\365\365\373\370\370\370\374\373\373\373\375\376\376\376\377\374\374" - "\374\376\377\377\377\377\374\374\374\376\371\371\371\375\377\377\377\377" - "\370\370\370\374\374\374\374\376\370\370\370\374\366\366\366\373\363\363" - "\363\372\361\361\361\371\371\371\371\374\367\367\367\373\372\372\372\375" - "\372\372\372\375\376\376\376\377\370\370\370\374\371\371\371\375\366\366" - "\366\373\370\370\370\374\363\363\363\372\364\364\364\372\357\357\357\370" - "\365\365\365\373\373\373\373\375\372\372\372\375\371\371\371\375\365\365" - "\365\372\367\367\367\373\365\365\365\372\373\373\373\375\371\371\371\374" - "\357\357\357\370\364\364\364\372\364\364\364\372\366\366\366\373\372\372" - "\372\375\371\371\371\375\377\377\377\377\377\377\377\377\371\371\371\375" - "\377\377\377\377\372\372\372\375\372\372\372\375\373\373\373\375\366\366" - "\366\373\371\371\371\375\362\362\362\371\357\357\357\370\357\357\357\370" - "\362\362\362\371\357\357\357\370\357\357\357\370\363\363\363\372\365\365" - "\365\373\371\371\371\374\374\374\374\376\370\370\370\374\365\365\365\372" - "\371\371\371\375\370\370\370\374\365\365\365\372\371\371\371\374\370\370" - "\370\374\365\365\365\372\364\364\364\372\352\352\352\365\363\363\363\371" - "\365\365\365\372\363\363\363\372\365\365\365\372\371\371\371\374\367\367" - "\367\373\374\374\374\376\371\371\371\374\370\370\370\374\365\365\365\373" - "\363\363\363\372\361\361\361\371\363\363\363\372\356\356\356\367\355\355" - "\355\366\364\364\364\372\370\370\370\374\366\366\366\373\373\373\373\376" - "\377\377\377\377\374\374\374\376\375\375\375\377\377\377\377\377\370\370" - "\370\374\372\372\372\375\373\373\373\376\371\371\371\375\365\365\365\373" - "\365\365\365\372\362\362\362\371\364\364\364\372\373\373\373\375\375\375" - "\375\377\373\373\373\375\370\370\370\374\364\364\364\372\365\365\365\373" - "\371\371\371\375\364\364\364\372\365\365\365\372\362\362\362\371\357\357" - "\357\370\363\363\363\371\365\365\365\372\365\365\365\372\366\366\366\373" - "\371\371\371\374\377\377\377\377\364\364\364\372\366\366\366\373\366\366" - "\366\373\370\370\370\374\367\367\367\373\362\362\362\371\363\363\363\371" - "\357\357\357\370\357\357\357\370\365\365\365\373\370\370\370\374\372\372" - "\372\375\376\376\376\377\371\371\371\374\377\377\377\377\370\370\370\374" - "\377\377\377\377\373\373\373\376\375\375\375\376\374\374\374\376\375\375" - "\375\376\361\361\361\371\363\363\363\371\364\364\364\372\364\364\364\372" - "\375\375\375\377\370\370\370\374\371\371\371\375\377\377\377\377\370\370" - "\370\374\373\373\373\376\373\373\373\375\370\370\370\374\373\373\373\376" - "\364\364\364\372\357\357\357\370\365\365\365\372\375\375\375\377\375\375" - "\375\376\370\370\370\374\375\375\375\376\375\375\375\377\373\373\373\375" - "\370\370\370\374\371\371\371\374\370\370\370\374\374\374\374\376\363\363" - "\363\372\373\373\373\375\371\371\371\375\377\377\377\377\377\377\377\377" - "\375\375\375\377\377\377\377\377\377\377\377\377\377\377\377\377\373\373" - "\373\375\367\367\367\373\364\364\364\372\365\365\365\372\357\357\357\370" - "\356\356\356\367\350\350\350\364\356\356\356\367\357\357\357\370\362\362" - "\362\371\363\363\363\372\366\366\366\373\371\371\371\374\365\365\365\373" - "\371\371\371\375\373\373\373\376\366\366\366\373\372\372\372\375\373\373" - "\373\375\377\377\377\377\371\371\371\375\371\371\371\375\366\366\366\373" - "\362\362\362\371\371\371\371\375\370\370\370\374\371\371\371\374\366\366" - "\366\373\371\371\371\375\370\370\370\374\374\374\374\376\362\362\362\371" - "\374\374\374\376\366\366\366\373\367\367\367\373\371\371\371\374\365\365" - "\365\373\370\370\370\374\366\366\366\373\366\366\366\373\366\366\366\373" - "\374\374\374\376\375\375\375\376\375\375\375\377\373\373\373\376\377\377" - "\377\377\377\377\377\377\373\373\373\375\367\367\367\373\374\374\374\376" - "\370\370\370\374\365\365\365\373\360\360\360\370\374\374\374\376\372\372" - "\372\375\377\377\377\377\376\376\376\377\377\377\377\377\364\364\364\372" - "\372\372\372\375\365\365\365\372\371\371\371\374\373\373\373\376\372\372" - "\372\375\370\370\370\374\362\362\362\371\365\365\365\373\366\366\366\373" - "\371\371\371\375\367\367\367\373\375\375\375\377\371\371\371\375\371\371" - "\371\375\370\370\370\374\370\370\370\374\370\370\370\374\370\370\370\374" - "\370\370\370\374\365\365\365\373\365\365\365\372\363\363\363\371\370\370" - "\370\374\377\377\377\377\372\372\372\375\377\377\377\377\377\377\377\377" - "\377\377\377\377\377\377\377\377\377\377\377\377\374\374\374\376\374\374" - "\374\376\371\371\371\374\365\365\365\373\370\370\370\374\365\365\365\372" - "\364\364\364\372\373\373\373\376\371\371\371\374\375\375\375\376\370\370" - "\370\374\374\374\374\376\372\372\372\375\372\372\372\375\366\366\366\373" - "\366\366\366\373\371\371\371\374\363\363\363\372\366\366\366\373\371\371" - "\371\374\371\371\371\375\366\366\366\373\377\377\377\377\372\372\372\375" - "\377\377\377\377\377\377\377\377\375\375\375\377\373\373\373\375\370\370" - "\370\374\371\371\371\374\367\367\367\373\370\370\370\374\370\370\370\374" - "\374\374\374\376\371\371\371\374\377\377\377\377\370\370\370\374\372\372" - "\372\375\375\375\375\376\370\370\370\374\370\370\370\374\365\365\365\373" - "\362\362\362\371\366\366\366\373\363\363\363\371\365\365\365\372\363\363" - "\363\371\360\360\360\370\364\364\364\372\365\365\365\373\367\367\367\373" - "\370\370\370\374\370\370\370\374\377\377\377\377\373\373\373\376\367\367" - "\367\373\370\370\370\374\366\366\366\373\370\370\370\374\367\367\367\373" - "\364\364\364\372\366\366\366\373\370\370\370\374\357\357\357\370\367\367" - "\367\373\362\362\362\371\367\367\367\373\366\366\366\373\371\371\371\375" - "\377\377\377\377\375\375\375\376\373\373\373\376\373\373\373\375\365\365" - "\365\372\371\371\371\375\366\366\366\373\364\364\364\372\366\366\366\373" - "\371\371\371\374\364\364\364\372\372\372\372\375\372\372\372\375\377\377" - "\377\377\377\377\377\377\377\377\377\377\373\373\373\376\377\377\377\377" - "\374\374\374\376\373\373\373\375\363\363\363\372\374\374\374\376\366\366" - "\366\373\375\375\375\376\374\374\374\376\373\373\373\375\374\374\374\376" - "\371\371\371\375\375\375\375\377\364\364\364\372\362\362\362\371\362\362" - "\362\371\371\371\371\374\366\366\366\373\362\362\362\371\370\370\370\374" - "\365\365\365\372\373\373\373\375\371\371\371\374\370\370\370\374\365\365" - "\365\373\371\371\371\374\367\367\367\373\367\367\367\373\364\364\364\372" - "\371\371\371\375\366\366\366\373\365\365\365\372\363\363\363\372\370\370" - "\370\374\363\363\363\371\366\366\366\373\375\375\375\376\373\373\373\376" - "\374\374\374\376\376\376\376\377\375\375\375\377\377\377\377\377\372\372" - "\372\375\376\376\376\377\373\373\373\375\371\371\371\375\362\362\362\371" - "\371\371\371\374\364\364\364\372\371\371\371\374\373\373\373\375\371\371" - "\371\375\371\371\371\375\376\376\376\377\373\373\373\375\367\367\367\373" - "\373\373\373\376\366\366\366\373\364\364\364\372\364\364\364\372\364\364" - "\364\372\363\363\363\371\366\366\366\373\375\375\375\376\372\372\372\375" - "\371\371\371\375\370\370\370\374\373\373\373\376\373\373\373\376\373\373" - "\373\376\376\376\376\377\364\364\364\372\373\373\373\376\364\364\364\372" - "\374\374\374\376\361\361\361\371\375\375\375\376\374\374\374\376\371\371" - "\371\374\375\375\375\377\365\365\365\373\374\374\374\376\370\370\370\374" - "\371\371\371\375\365\365\365\372\362\362\362\371\365\365\365\372\362\362" - "\362\371\363\363\363\371\360\360\360\370\364\364\364\372\363\363\363\372" - "\364\364\364\372\365\365\365\373\373\373\373\375\376\376\376\377\374\374" - "\374\376\374\374\374\376\374\374\374\376\365\365\365\373\373\373\373\376" - "\371\371\371\374\364\364\364\372\371\371\371\375\365\365\365\372\364\364" - "\364\372\360\360\360\370\366\366\366\373\360\360\360\370\365\365\365\372" - "\366\366\366\373\372\372\372\375\373\373\373\375\373\373\373\375\376\376" - "\376\377\370\370\370\374\370\370\370\374\372\372\372\375\371\371\371\375" - "\365\365\365\372\366\366\366\373\364\364\364\372\370\370\370\374\370\370" - "\370\374\370\370\370\374\377\377\377\377\377\377\377\377\377\377\377\377" - "\375\375\375\376\377\377\377\377\374\374\374\376\375\375\375\376\364\364" - "\364\372\371\371\371\374\371\371\371\374\374\374\374\376\373\373\373\375" - "\372\372\372\375\371\371\371\375\371\371\371\375\370\370\370\374\364\364" - "\364\372\364\364\364\372\363\363\363\372\364\364\364\372\364\364\364\372" - "\365\365\365\372\371\371\371\375\365\365\365\372\371\371\371\374\371\371" - "\371\375\365\365\365\373\367\367\367\373\373\373\373\376\371\371\371\375" - "\373\373\373\375\373\373\373\375\365\365\365\372\367\367\367\373\370\370" - "\370\374\363\363\363\371\371\371\371\374\360\360\360\370\372\372\372\375" - "\374\374\374\376\367\367\367\373\377\377\377\377\377\377\377\377\377\377" - "\377\377\371\371\371\375\375\375\375\377\374\374\374\376\367\367\367\373" - "\363\363\363\371\372\372\372\375\364\364\364\372\365\365\365\373\373\373" - "\373\375\370\370\370\374\371\371\371\375\366\366\366\373\376\376\376\377" - "\371\371\371\375\373\373\373\375\373\373\373\375\362\362\362\371\363\363" - "\363\371\370\370\370\374\367\367\367\373\366\366\366\373\372\372\372\375" - "\373\373\373\375\366\366\366\373\375\375\375\377\365\365\365\373\371\371" - "\371\375\377\377\377\377\372\372\372\375\373\373\373\375\377\377\377\377" - "\370\370\370\374\366\366\366\373\377\377\377\377\365\365\365\372\376\376" - "\376\377\373\373\373\375\371\371\371\375\371\371\371\375\370\370\370\374" - "\374\374\374\376\370\370\370\374\371\371\371\374\364\364\364\372\364\364" - "\364\372\366\366\366\373\360\360\360\370\362\362\362\371\356\356\356\367" - "\362\362\362\371\365\365\365\372\365\365\365\373\364\364\364\372\372\372" - "\372\375\372\372\372\375\376\376\376\377\374\374\374\376\374\374\374\376" - "\367\367\367\373\376\376\376\377\371\371\371\374\375\375\375\377\363\363" - "\363\371\372\372\372\375\363\363\363\372\364\364\364\372\365\365\365\372" - "\370\370\370\374\364\364\364\372\363\363\363\371\365\365\365\373\366\366" - "\366\373\366\366\366\373\375\375\375\377\370\370\370\374\366\366\366\373" - "\367\367\367\373\371\371\371\374\366\366\366\373\364\364\364\372\365\365" - "\365\372\366\366\366\373\374\374\374\376\371\371\371\375\377\377\377\377" - "\377\377\377\377\375\375\375\377\371\371\371\374\372\372\372\375\373\373" - "\373\376\364\364\364\372\366\366\366\373\366\366\366\373\364\364\364\372" - "\372\372\372\375\374\374\374\376\373\373\373\375\375\375\375\376\371\371" - "\371\374\367\367\367\373\365\365\365\373\360\360\360\370\371\371\371\374" - "\370\370\370\374\365\365\365\373\363\363\363\372\370\370\370\374\362\362" - "\362\371\370\370\370\374\375\375\375\377\373\373\373\376\366\366\366\373" - "\371\371\371\375\371\371\371\374\377\377\377\377\373\373\373\375\370\370" - "\370\374\371\371\371\374\371\371\371\374\361\361\361\371\365\365\365\372" - "\355\355\355\367\373\373\373\375\370\370\370\374\374\374\374\376\377\377" - "\377\377\377\377\377\377\377\377\377\377\372\372\372\375\371\371\371\375" - "\370\370\370\374\370\370\370\374\364\364\364\372\370\370\370\374\371\371" - "\371\374\364\364\364\372\375\375\375\377\375\375\375\377\367\367\367\373" - "\367\367\367\373\365\365\365\373\371\371\371\375\374\374\374\376\367\367" - "\367\373\370\370\370\374\361\361\361\371\360\360\360\370\362\362\362\371" - "\364\364\364\372\374\374\374\376\373\373\373\375\367\367\367\373\375\375" - "\375\376\364\364\364\372\366\366\366\373\377\377\377\377\370\370\370\374" - "\366\366\366\373\370\370\370\374\371\371\371\374\370\370\370\374\371\371" - "\371\374\362\362\362\371\374\374\374\376\370\370\370\374\365\365\365\372" - "\370\370\370\374\371\371\371\374\363\363\363\372\361\361\361\371\367\367" - "\367\373\365\365\365\373\362\362\362\371\362\362\362\371\362\362\362\371" - "\360\360\360\370\362\362\362\371\360\360\360\370\364\364\364\372\370\370" - "\370\374\365\365\365\372\370\370\370\374\372\372\372\375\374\374\374\376" - "\373\373\373\375\375\375\375\377\377\377\377\377\377\377\377\377\373\373" - "\373\376\374\374\374\376\370\370\370\374\371\371\371\374\356\356\356\367" - "\373\373\373\375\375\375\375\377\370\370\370\374\371\371\371\374\364\364" - "\364\372\371\371\371\375\363\363\363\372\365\365\365\373\371\371\371\374" - "\364\364\364\372\377\377\377\377\364\364\364\372\370\370\370\374\366\366" - "\366\373\362\362\362\371\364\364\364\372\374\374\374\376\374\374\374\376" - "\373\373\373\376\374\374\374\376\377\377\377\377\377\377\377\377\370\370" - "\370\374\373\373\373\375\367\367\367\373\375\375\375\376\367\367\367\373" - "\362\362\362\371\362\362\362\371\373\373\373\376\373\373\373\376\374\374" - "\374\376\377\377\377\377\372\372\372\375\371\371\371\374\366\366\366\373" - "\374\374\374\376\373\373\373\376\370\370\370\374\364\364\364\372\363\363" - "\363\372\365\365\365\373\370\370\370\374\372\372\372\375\371\371\371\374" - "\370\370\370\374\367\367\367\373\366\366\366\373\370\370\370\374\371\371" - "\371\375\370\370\370\374\366\366\366\373\364\364\364\372\370\370\370\374" - "\363\363\363\371\365\365\365\372\365\365\365\372\367\367\367\373\374\374" - "\374\376\375\375\375\377\377\377\377\377\374\374\374\376\377\377\377\377" - "\371\371\371\374\371\371\371\375\373\373\373\375\367\367\367\373\362\362" - "\362\371\374\374\374\376\370\370\370\374\371\371\371\375\367\367\367\373" - "\374\374\374\376\370\370\370\374\373\373\373\375\370\370\370\374\372\372" - "\372\375\366\366\366\373\365\365\365\373\363\363\363\371\366\366\366\373" - "\364\364\364\372\365\365\365\373\362\362\362\371\363\363\363\372\362\362" - "\362\371\371\371\371\374\372\372\372\375\371\371\371\374\366\366\366\373" - "\373\373\373\375\375\375\375\376\366\366\366\373\375\375\375\377\370\370" - "\370\374\374\374\374\376\356\356\356\367\365\365\365\372\377\377\377\377" - "\377\377\377\377\370\370\370\374\365\365\365\372\365\365\365\373\363\363" - "\363\372\361\361\361\371\363\363\363\371\361\361\361\371\370\370\370\374" - "\363\363\363\371\362\362\362\371\357\357\357\370\361\361\361\371\362\362" - "\362\371\366\366\366\373\366\366\366\373\370\370\370\374\365\365\365\372" - "\376\376\376\377\374\374\374\376\372\372\372\375\372\372\372\375\377\377" - "\377\377\375\375\375\377\371\371\371\374\376\376\376\377\370\370\370\374" - "\363\363\363\371\363\363\363\371\370\370\370\374\372\372\372\375\371\371" - "\371\374\366\366\366\373\370\370\370\374\370\370\370\374\371\371\371\374" - "\370\370\370\374\365\365\365\372\374\374\374\376\367\367\367\373\364\364" - "\364\372\367\367\367\373\364\364\364\372\357\357\357\370\366\366\366\373" - "\370\370\370\374\371\371\371\374\372\372\372\375\377\377\377\377\374\374" - "\374\376\375\375\375\376\371\371\371\374\373\373\373\375\370\370\370\374" - "\373\373\373\376\363\363\363\371\364\364\364\372\364\364\364\372\374\374" - "\374\376\366\366\366\373\372\372\372\375\372\372\372\375\370\370\370\374" - "\372\372\372\375\373\373\373\376\374\374\374\376\366\366\366\373\364\364" - "\364\372\363\363\363\372\364\364\364\372\364\364\364\372\366\366\366\373" - "\371\371\371\375\365\365\365\373\370\370\370\374\367\367\367\373\374\374" - "\374\376\371\371\371\375\373\373\373\375\370\370\370\374\367\367\367\373" - "\364\364\364\372\367\367\367\373\364\364\364\372\363\363\363\371\366\366" - "\366\373\373\373\373\375\374\374\374\376\371\371\371\375\377\377\377\377" - "\376\376\376\377\371\371\371\375\372\372\372\375\374\374\374\376\373\373" - "\373\375\375\375\375\377\364\364\364\372\372\372\372\375\367\367\367\373" - "\373\373\373\375\370\370\370\374\377\377\377\377\371\371\371\374\375\375" - "\375\376\371\371\371\375\372\372\372\375\367\367\367\373\365\365\365\373" - "\363\363\363\372\364\364\364\372\365\365\365\373\366\366\366\373\360\360" - "\360\370\362\362\362\371\367\367\367\373\371\371\371\375\374\374\374\376" - "\370\370\370\374\370\370\370\374\377\377\377\377\371\371\371\375\366\366" - "\366\373\366\366\366\373\370\370\370\374\372\372\372\375\370\370\370\374" - "\365\365\365\372\367\367\367\373\374\374\374\376\371\371\371\375\370\370" - "\370\374\371\371\371\374\365\365\365\372\370\370\370\374\370\370\370\374" - "\370\370\370\374\356\356\356\367\360\360\360\370\357\357\357\370\354\354" - "\354\366\357\357\357\370\363\363\363\371\365\365\365\373\370\370\370\374" - "\366\366\366\373\371\371\371\374\372\372\372\375\370\370\370\374\373\373" - "\373\376\373\373\373\375\371\371\371\375\374\374\374\376\375\375\375\377" - "\367\367\367\373\364\364\364\372\365\365\365\373\364\364\364\372\365\365" - "\365\372\371\371\371\375\370\370\370\374\370\370\370\374\367\367\367\373" - "\371\371\371\375\374\374\374\376\373\373\373\376\364\364\364\372\364\364" - "\364\372\370\370\370\374\365\365\365\372\366\366\366\373\370\370\370\374" - "\364\364\364\372\366\366\366\373\365\365\365\372\373\373\373\376\373\373" - "\373\376\375\375\375\376\372\372\372\375\375\375\375\377\373\373\373\376" - "\374\374\374\376\376\376\376\377\373\373\373\376\365\365\365\373\374\374" - "\374\376\373\373\373\376\371\371\371\375\373\373\373\375\370\370\370\374" - "\370\370\370\374\377\377\377\377\370\370\370\374\374\374\374\376\367\367" - "\367\373\365\365\365\372\370\370\370\374\365\365\365\372\362\362\362\371" - "\367\367\367\373\365\365\365\372\365\365\365\373\365\365\365\373\372\372" - "\372\375\374\374\374\376\371\371\371\375\366\366\366\373\367\367\367\373" - "\367\367\367\373\364\364\364\372\366\366\366\373\370\370\370\374\371\371" - "\371\375\365\365\365\372\366\366\366\373\373\373\373\375\373\373\373\375" - "\377\377\377\377\370\370\370\374\372\372\372\375\373\373\373\375\374\374" - "\374\376\377\377\377\377\371\371\371\374\371\371\371\374\366\366\366\373" - "\372\372\372\375\372\372\372\375\372\372\372\375\367\367\367\373\371\371" - "\371\374\372\372\372\375\377\377\377\377\373\373\373\375\372\372\372\375" - "\366\366\366\373\365\365\365\373\364\364\364\372\362\362\362\371\365\365" - "\365\373\363\363\363\372\372\372\372\375\364\364\364\372\366\366\366\373" - "\370\370\370\374\371\371\371\375\372\372\372\375\362\362\362\371\373\373" - "\373\376\375\375\375\377\375\375\375\377\372\372\372\375\373\373\373\375" - "\374\374\374\376\366\366\366\373\363\363\363\371\371\371\371\375\366\366" - "\366\373\367\367\367\373\370\370\370\374\370\370\370\374\370\370\370\374" - "\366\366\366\373\364\364\364\372\366\366\366\373\365\365\365\373\363\363" - "\363\371\364\364\364\372\365\365\365\372\363\363\363\371\365\365\365\373" - "\365\365\365\373\370\370\370\374\373\373\373\375\374\374\374\376\371\371" - "\371\374\371\371\371\375\375\375\375\377\376\376\376\377\374\374\374\376" - "\371\371\371\374\365\365\365\373\370\370\370\374\374\374\374\376\376\376" - "\376\377\364\364\364\372\370\370\370\374\373\373\373\375\373\373\373\375" - "\363\363\363\372\370\370\370\374\372\372\372\375\370\370\370\374\374\374" - "\374\376\363\363\363\372\364\364\364\372\371\371\371\375\365\365\365\372" - "\370\370\370\374\364\364\364\372\355\355\355\367\364\364\364\372\372\372" - "\372\375\373\373\373\376\370\370\370\374\367\367\367\373\375\375\375\377" - "\377\377\377\377\375\375\375\376\377\377\377\377\377\377\377\377\377\377" - "\377\377\375\375\375\377\377\377\377\377\374\374\374\376\372\372\372\375" - "\374\374\374\376\376\376\376\377\364\364\364\372\377\377\377\377\372\372" - "\372\375\366\366\366\373\364\364\364\372\366\366\366\373\370\370\370\374" - "\365\365\365\372\365\365\365\372\364\364\364\372\366\366\366\373\372\372" - "\372\375\366\366\366\373\373\373\373\376\375\375\375\376\371\371\371\374" - "\366\366\366\373\366\366\366\373\373\373\373\376\366\366\366\373\365\365" - "\365\373\363\363\363\371\363\363\363\371\364\364\364\372\364\364\364\372" - "\372\372\372\375\372\372\372\375\373\373\373\375\373\373\373\375\367\367" - "\367\373\373\373\373\375\377\377\377\377\377\377\377\377\371\371\371\375" - "\375\375\375\377\364\364\364\372\371\371\371\375\373\373\373\376\370\370" - "\370\374\372\372\372\375\364\364\364\372\376\376\376\377\372\372\372\375" - "\375\375\375\376\373\373\373\376\364\364\364\372\365\365\365\373\364\364" - "\364\372\362\362\362\371\355\355\355\366\357\357\357\370\361\361\361\371" - "\372\372\372\375\370\370\370\374\374\374\374\376\373\373\373\375\365\365" - "\365\373\374\374\374\376\375\375\375\376\370\370\370\374\370\370\370\374" - "\373\373\373\376\371\371\371\375\373\373\373\376\365\365\365\372\367\367" - "\367\373\374\374\374\376\366\366\366\373\365\365\365\372\366\366\366\373" - "\364\364\364\372\363\363\363\371\371\371\371\375\365\365\365\373\366\366" - "\366\373\364\364\364\372\357\357\357\370\364\364\364\372\363\363\363\371" - "\360\360\360\370\365\365\365\372\371\371\371\374\371\371\371\375\365\365" - "\365\372\370\370\370\374\370\370\370\374\375\375\375\377\377\377\377\377" - "\377\377\377\377\373\373\373\376\374\374\374\376\371\371\371\374\366\366" - "\366\373\367\367\367\373\371\371\371\375\365\365\365\373\363\363\363\372" - "\371\371\371\375\373\373\373\376\365\365\365\373\373\373\373\376\371\371" - "\371\375\373\373\373\375\365\365\365\372\365\365\365\373\373\373\373\376" - "\366\366\366\373\364\364\364\372\374\374\374\376\367\367\367\373\360\360" - "\360\370\365\365\365\372\373\373\373\376\366\366\366\373\372\372\372\375" - "\372\372\372\375\377\377\377\377\374\374\374\376\377\377\377\377\376\376" - "\376\377\374\374\374\376\367\367\367\373\373\373\373\375\365\365\365\372" - "\375\375\375\376\375\375\375\376\366\366\366\373\375\375\375\376\373\373" - "\373\376\377\377\377\377\375\375\375\377\370\370\370\374\365\365\365\373" - "\366\366\366\373\363\363\363\371\366\366\366\373\364\364\364\372\367\367" - "\367\373\364\364\364\372\367\367\367\373\370\370\370\374\377\377\377\377" - "\374\374\374\376\373\373\373\375\371\371\371\375\373\373\373\375\366\366" - "\366\373\364\364\364\372\366\366\366\373\370\370\370\374\357\357\357\370" - "\370\370\370\374\362\362\362\371\372\372\372\375\375\375\375\376\375\375" - "\375\377\372\372\372\375\373\373\373\376\377\377\377\377\374\374\374\376" - "\373\373\373\375\377\377\377\377\373\373\373\375\372\372\372\375\371\371" - "\371\375\374\374\374\376\366\366\366\373\370\370\370\374\370\370\370\374" - "\371\371\371\374\377\377\377\377\373\373\373\375\373\373\373\375\364\364" - "\364\372\370\370\370\374\371\371\371\375\362\362\362\371\361\361\361\371" - "\356\356\356\367\367\367\367\373\371\371\371\374\373\373\373\376\373\373" - "\373\375\373\373\373\376\366\366\366\373\375\375\375\377\375\375\375\377" - "\377\377\377\377\375\375\375\376\372\372\372\375\370\370\370\374\364\364" - "\364\372\367\367\367\373\366\366\366\373\363\363\363\372\364\364\364\372" - "\373\373\373\376\373\373\373\376\370\370\370\374\373\373\373\375\365\365" - "\365\372\367\367\367\373\371\371\371\374\362\362\362\371\362\362\362\371" - "\365\365\365\373\357\357\357\370\360\360\360\370\365\365\365\373\371\371" - "\371\374\366\366\366\373\372\372\372\375\370\370\370\374\374\374\374\376" - "\370\370\370\374\374\374\374\376\377\377\377\377\371\371\371\374\371\371" - "\371\374\371\371\371\375\373\373\373\376\371\371\371\374\367\367\367\373" - "\367\367\367\373\366\366\366\373\363\363\363\371\364\364\364\372\375\375" - "\375\376\372\372\372\375\373\373\373\376\373\373\373\376\371\371\371\374" - "\371\371\371\374\366\366\366\373\365\365\365\373\364\364\364\372\365\365" - "\365\373\365\365\365\373\365\365\365\372\367\367\367\373\372\372\372\375" - "\366\366\366\373\375\375\375\377\373\373\373\375\373\373\373\376\377\377" - "\377\377\373\373\373\375\372\372\372\375\377\377\377\377\376\376\376\377" - "\372\372\372\375\372\372\372\375\374\374\374\376\366\366\366\373\376\376" - "\376\377\373\373\373\375\377\377\377\377\366\366\366\373\370\370\370\374" - "\371\371\371\374\371\371\371\374\365\365\365\372\363\363\363\372\362\362" - "\362\371\366\366\366\373\365\365\365\372\370\370\370\374\373\373\373\376" - "\371\371\371\374\373\373\373\376\364\364\364\372\374\374\374\376\372\372" - "\372\375\372\372\372\375\374\374\374\376\362\362\362\371\374\374\374\376" - "\362\362\362\371\367\367\367\373\367\367\367\373\363\363\363\371\373\373" - "\373\375\375\375\375\376\377\377\377\377\370\370\370\374\373\373\373\375" - "\377\377\377\377\377\377\377\377\370\370\370\374\375\375\375\377\375\375" - "\375\376\371\371\371\375\375\375\375\377\366\366\366\373\375\375\375\376" - "\371\371\371\374\373\373\373\375\373\373\373\375\370\370\370\374\377\377" - "\377\377\375\375\375\376\373\373\373\376\373\373\373\376\364\364\364\372" - "\372\372\372\375\370\370\370\374\364\364\364\372\363\363\363\372\370\370" - "\370\374\372\372\372\375\372\372\372\375\371\371\371\374\377\377\377\377" - "\372\372\372\375\372\372\372\375\377\377\377\377\372\372\372\375\366\366" - "\366\373\372\372\372\375\365\365\365\373\366\366\366\373\370\370\370\374" - "\364\364\364\372\366\366\366\373\371\371\371\374\373\373\373\376\366\366" - "\366\373\373\373\373\376\367\367\367\373\373\373\373\375\371\371\371\374" - "\364\364\364\372\361\361\361\371\357\357\357\370\364\364\364\372\361\361" - "\361\371\365\365\365\372\370\370\370\374\366\366\366\373\373\373\373\376" - "\370\370\370\374\372\372\372\375\375\375\375\376\375\375\375\377\372\372" - "\372\375\374\374\374\376\367\367\367\373\373\373\373\376\366\366\366\373" - "\370\370\370\374\360\360\360\370\373\373\373\375\367\367\367\373\362\362" - "\362\371\367\367\367\373\375\375\375\377\377\377\377\377\371\371\371\374" - "\371\371\371\375\377\377\377\377\372\372\372\375\370\370\370\374\370\370" - "\370\374\364\364\364\372\364\364\364\372\363\363\363\371\363\363\363\372" - "\363\363\363\372\353\353\353\366\371\371\371\374\371\371\371\375\375\375" - "\375\376\377\377\377\377\377\377\377\377\373\373\373\376\377\377\377\377" - "\376\376\376\377\376\376\376\377\374\374\374\376\373\373\373\376\370\370" - "\370\374\374\374\374\376\371\371\371\374\377\377\377\377\375\375\375\376" - "\374\374\374\376\370\370\370\374\376\376\376\377\370\370\370\374\366\366" - "\366\373\365\365\365\372\364\364\364\372\366\366\366\373\365\365\365\372" - "\365\365\365\372\371\371\371\375\374\374\374\376\373\373\373\375\374\374" - "\374\376\364\364\364\372\365\365\365\373\364\364\364\372\366\366\366\373" - "\363\363\363\371\367\367\367\373\363\363\363\372\365\365\365\372\363\363" - "\363\371\375\375\375\376\372\372\372\375\366\366\366\373\377\377\377\377" - "\373\373\373\376\370\370\370\374\377\377\377\377\370\370\370\374\371\371" - "\371\374\372\372\372\375\371\371\371\375\375\375\375\376\367\367\367\373" - "\365\365\365\372\366\366\366\373\371\371\371\375\373\373\373\375\371\371" - "\371\375\366\366\366\373\377\377\377\377\373\373\373\375\363\363\363\371" - "\371\371\371\375\367\367\367\373\371\371\371\375\367\367\367\373\367\367" - "\367\373\364\364\364\372\367\367\367\373\370\370\370\374\366\366\366\373" - "\373\373\373\375\373\373\373\376\375\375\375\377\376\376\376\377\374\374" - "\374\376\376\376\376\377\365\365\365\373\365\365\365\372\370\370\370\374" - "\364\364\364\372\364\364\364\372\361\361\361\371\362\362\362\371\373\373" - "\373\376\371\371\371\374\373\373\373\375\367\367\367\373\371\371\371\374" - "\372\372\372\375\365\365\365\373\371\371\371\375\355\355\355\366\363\363" - "\363\371\355\355\355\367\364\364\364\372\363\363\363\371\356\356\356\367" - "\366\366\366\373\367\367\367\373\370\370\370\374\375\375\375\376\374\374" - "\374\376\377\377\377\377\375\375\375\376\373\373\373\375\365\365\365\373" - "\370\370\370\374\365\365\365\373\371\371\371\374\363\363\363\372\367\367" - "\367\373\367\367\367\373\365\365\365\372\370\370\370\374\374\374\374\376" - "\374\374\374\376\371\371\371\375\371\371\371\375\377\377\377\377\374\374" - "\374\376\374\374\374\376\370\370\370\374\365\365\365\373\365\365\365\372" - "\364\364\364\372\361\361\361\371\364\364\364\372\357\357\357\370\367\367" - "\367\373\372\372\372\375\377\377\377\377\375\375\375\376\373\373\373\375" - "\363\363\363\371\377\377\377\377\371\371\371\375\371\371\371\375\371\371" - "\371\375\371\371\371\374\371\371\371\375\367\367\367\373\377\377\377\377" - "\377\377\377\377\375\375\375\377\372\372\372\375\372\372\372\375\377\377" - "\377\377\375\375\375\376\374\374\374\376\364\364\364\372\371\371\371\375" - "\361\361\361\371\363\363\363\372\364\364\364\372\373\373\373\376\377\377" - "\377\377\366\366\366\373\374\374\374\376\367\367\367\373\366\366\366\373" - "\371\371\371\375\364\364\364\372\366\366\366\373\367\367\367\373\365\365" - "\365\372\367\367\367\373\363\363\363\372\375\375\375\376\370\370\370\374" - "\357\357\357\370\374\374\374\376\371\371\371\375\374\374\374\376\374\374" - "\374\376\371\371\371\375\374\374\374\376\373\373\373\375\370\370\370\374" - "\371\371\371\374\366\366\366\373\364\364\364\372\363\363\363\372\371\371" - "\371\375\371\371\371\375\370\370\370\374\370\370\370\374\370\370\370\374" - "\370\370\370\374\366\366\366\373\366\366\366\373\366\366\366\373\365\365" - "\365\372\367\367\367\373\373\373\373\376\364\364\364\372\364\364\364\372" - "\373\373\373\376\366\366\366\373\374\374\374\376\377\377\377\377\376\376" - "\376\377\377\377\377\377\376\376\376\377\373\373\373\375\370\370\370\374" - "\356\356\356\367\362\362\362\371\355\355\355\366\364\364\364\372\367\367" - "\367\373\365\365\365\372\373\373\373\375\374\374\374\376\357\357\357\370" - "\371\371\371\374\371\371\371\374\370\370\370\374\371\371\371\375\364\364" - "\364\372\364\364\364\372\357\357\357\370\361\361\361\371\361\361\361\371" - "\362\362\362\371\361\361\361\371\364\364\364\372\366\366\366\373\373\373" - "\373\375\372\372\372\375\372\372\372\375\371\371\371\374\372\372\372\375" - "\373\373\373\376\373\373\373\376\365\365\365\373\365\365\365\372\365\365" - "\365\372\362\362\362\371\360\360\360\370\362\362\362\371\372\372\372\375" - "\367\367\367\373\373\373\373\375\376\376\376\377\374\374\374\376\366\366" - "\366\373\371\371\371\374\371\371\371\375\372\372\372\375\364\364\364\372" - "\365\365\365\373\363\363\363\371\360\360\360\370\363\363\363\371\362\362" - "\362\371\371\371\371\374\375\375\375\376\375\375\375\377\377\377\377\377" - "\367\367\367\373\377\377\377\377\365\365\365\372\377\377\377\377\372\372" - "\372\375\373\373\373\375\374\374\374\376\353\353\353\366\377\377\377\377" - "\373\373\373\375\377\377\377\377\377\377\377\377\374\374\374\376\371\371" - "\371\374\377\377\377\377\374\374\374\376\377\377\377\377\371\371\371\375" - "\365\365\365\372\364\364\364\372\360\360\360\370\361\361\361\371\367\367" - "\367\373\372\372\372\375\373\373\373\375\371\371\371\375\377\377\377\377" - "\365\365\365\373\370\370\370\374\364\364\364\372\372\372\372\375\372\372" - "\372\375\371\371\371\375\365\365\365\373\367\367\367\373\370\370\370\374" - "\365\365\365\373\372\372\372\375\373\373\373\375\367\367\367\373\366\366" - "\366\373\375\375\375\376\374\374\374\376\371\371\371\374\371\371\371\374" - "\371\371\371\375\371\371\371\375\364\364\364\372\365\365\365\372\363\363" - "\363\372\357\357\357\370\370\370\370\374\373\373\373\376\370\370\370\374" - "\373\373\373\375\370\370\370\374\367\367\367\373\364\364\364\372\370\370" - "\370\374\371\371\371\375\357\357\357\370\364\364\364\372\364\364\364\372" - "\363\363\363\372\365\365\365\372\363\363\363\371\373\373\373\375\374\374" - "\374\376\374\374\374\376\376\376\376\377\377\377\377\377\367\367\367\373" - "\374\374\374\376\364\364\364\372\370\370\370\374\363\363\363\371\365\365" - "\365\373\352\352\352\365\366\366\366\373\365\365\365\372\364\364\364\372" - "\364\364\364\372\366\366\366\373\373\373\373\375\364\364\364\372\365\365" - "\365\373\356\356\356\367\362\362\362\371\362\362\362\371\363\363\363\371" - "\360\360\360\370\362\362\362\371\362\362\362\371\364\364\364\372\367\367" - "\367\373\366\366\366\373\376\376\376\377\371\371\371\374\373\373\373\375" - "\366\366\366\373\370\370\370\374\372\372\372\375\376\376\376\377\365\365" - "\365\373\366\366\366\373\367\367\367\373\363\363\363\371\363\363\363\372" - "\366\366\366\373\366\366\366\373\373\373\373\375\373\373\373\375\374\374" - "\374\376\372\372\372\375\373\373\373\375\377\377\377\377\371\371\371\375" - "\374\374\374\376\370\370\370\374\371\371\371\374\365\365\365\373\363\363" - "\363\372\365\365\365\373\366\366\366\373\366\366\366\373\373\373\373\375" - "\375\375\375\376\372\372\372\375\377\377\377\377\374\374\374\376\371\371" - "\371\375\373\373\373\376\367\367\367\373\357\357\357\370\366\366\366\373" - "\371\371\371\374\365\365\365\372\374\374\374\376\370\370\370\374\375\375" - "\375\377\373\373\373\376\377\377\377\377\373\373\373\376\371\371\371\375" - "\370\370\370\374\370\370\370\374\365\365\365\372\364\364\364\372\370\370" - "\370\374\356\356\356\367\365\365\365\373\371\371\371\374\363\363\363\372" - "\374\374\374\376\375\375\375\377\366\366\366\373\372\372\372\375\373\373" - "\373\375\371\371\371\375\377\377\377\377\370\370\370\374\367\367\367\373" - "\367\367\367\373\357\357\357\370\365\365\365\373\374\374\374\376\373\373" - "\373\376\373\373\373\376\371\371\371\375\371\371\371\374\363\363\363\372" - "\371\371\371\374\375\375\375\376\372\372\372\375\374\374\374\376\367\367" - "\367\373\364\364\364\372\357\357\357\370\370\370\370\374\366\366\366\373" - "\367\367\367\373\366\366\366\373\373\373\373\376\367\367\367\373\366\366" - "\366\373\370\370\370\374\365\365\365\372\370\370\370\374\363\363\363\371" - "\363\363\363\371\363\363\363\371\364\364\364\372\371\371\371\374\365\365" - "\365\373\376\376\376\377\365\365\365\372\365\365\365\373\373\373\373\376" - "\370\370\370\374\377\377\377\377\366\366\366\373\367\367\367\373\362\362" - "\362\371\365\365\365\372\356\356\356\367\365\365\365\372\356\356\356\367" - "\373\373\373\375\370\370\370\374\366\366\366\373\366\366\366\373\371\371" - "\371\374\366\366\366\373\366\366\366\373\366\366\366\373\365\365\365\372" - "\364\364\364\372\363\363\363\372\363\363\363\371\363\363\363\371\364\364" - "\364\372\363\363\363\372\365\365\365\373\366\366\366\373\366\366\366\373" - "\376\376\376\377\373\373\373\375\373\373\373\375\367\367\367\373\370\370" - "\370\374\370\370\370\374\370\370\370\374\366\366\366\373\365\365\365\372" - "\366\366\366\373\364\364\364\372\367\367\367\373\361\361\361\371\370\370" - "\370\374\374\374\374\376\373\373\373\375\370\370\370\374\372\372\372\375" - "\374\374\374\376\370\370\370\374\370\370\370\374\373\373\373\375\363\363" - "\363\372\362\362\362\371\366\366\366\373\365\365\365\372\364\364\364\372" - "\377\377\377\377\370\370\370\374\377\377\377\377\377\377\377\377\377\377" - "\377\377\374\374\374\376\370\370\370\374\374\374\374\376\365\365\365\372" - "\366\366\366\373\371\371\371\374\370\370\370\374\371\371\371\374\373\373" - "\373\375\371\371\371\375\377\377\377\377\373\373\373\375\370\370\370\374" - "\371\371\371\375\377\377\377\377\357\357\357\370\364\364\364\372\365\365" - "\365\372\367\367\367\373\361\361\361\371\366\366\366\373\366\366\366\373" - "\354\354\354\366\365\365\365\373\372\372\372\375\370\370\370\374\370\370" - "\370\374\370\370\370\374\372\372\372\375\373\373\373\376\366\366\366\373" - "\364\364\364\372\366\366\366\373\363\363\363\372\363\363\363\372\367\367" - "\367\373\371\371\371\375\366\366\366\373\374\374\374\376\371\371\371\375" - "\367\367\367\373\366\366\366\373\371\371\371\375\375\375\375\376\370\370" - "\370\374\370\370\370\374\374\374\374\376\370\370\370\374\365\365\365\373" - "\372\372\372\375\363\363\363\371\367\367\367\373\367\367\367\373\367\367" - "\367\373\367\367\367\373\366\366\366\373\366\366\366\373\371\371\371\374" - "\364\364\364\372\357\357\357\370\346\346\346\363\362\362\362\371\364\364" - "\364\372\370\370\370\374\367\367\367\373\373\373\373\375\370\370\370\374" - "\371\371\371\374\376\376\376\377\373\373\373\375\375\375\375\377\371\371" - "\371\375\365\365\365\372\367\367\367\373\364\364\364\372\363\363\363\371" - "\363\363\363\372\357\357\357\370\370\370\370\374\366\366\366\373\370\370" - "\370\374\371\371\371\375\371\371\371\375\367\367\367\373\370\370\370\374" - "\371\371\371\374\370\370\370\374\364\364\364\372\366\366\366\373\370\370" - "\370\374\360\360\360\370\363\363\363\371\370\370\370\374\366\366\366\373" - "\372\372\372\375\371\371\371\375\374\374\374\376\374\374\374\376\375\375" - "\375\376\374\374\374\376\377\377\377\377\374\374\374\376\361\361\361\371" - "\367\367\367\373\365\365\365\373\366\366\366\373\357\357\357\370\364\364" - "\364\372\360\360\360\370\367\367\367\373\371\371\371\375\373\373\373\376" - "\377\377\377\377\373\373\373\376\373\373\373\376\374\374\374\376\365\365" - "\365\373\366\366\366\373\362\362\362\371\363\363\363\371\365\365\365\373" - "\366\366\366\373\370\370\370\374\366\366\366\373\370\370\370\374\377\377" - "\377\377\375\375\375\376\374\374\374\376\377\377\377\377\377\377\377\377" - "\372\372\372\375\371\371\371\374\362\362\362\371\365\365\365\372\371\371" - "\371\375\372\372\372\375\372\372\372\375\370\370\370\374\376\376\376\377" - "\376\376\376\377\371\371\371\374\372\372\372\375\371\371\371\375\370\370" - "\370\374\364\364\364\372\363\363\363\371\370\370\370\374\352\352\352\365" - "\364\364\364\372\366\366\366\373\357\357\357\370\364\364\364\372\364\364" - "\364\372\364\364\364\372\370\370\370\374\371\371\371\374\377\377\377\377" - "\371\371\371\375\366\366\366\373\366\366\366\373\363\363\363\371\361\361" - "\361\371\363\363\363\372\371\371\371\375\370\370\370\374\372\372\372\375" - "\361\361\361\371\366\366\366\373\371\371\371\375\376\376\376\377\373\373" - "\373\376\372\372\372\375\372\372\372\375\371\371\371\375\372\372\372\375" - "\367\367\367\373\370\370\370\374\360\360\360\370\366\366\366\373\364\364" - "\364\372\365\365\365\372\370\370\370\374\370\370\370\374\367\367\367\373" - "\370\370\370\374\361\361\361\371\357\357\357\370\363\363\363\371\346\346" - "\346\363\356\356\356\367\365\365\365\373\370\370\370\374\372\372\372\375" - "\364\364\364\372\377\377\377\377\377\377\377\377\377\377\377\377\370\370" - "\370\374\365\365\365\372\374\374\374\376\366\366\366\373\377\377\377\377" - "\364\364\364\372\364\364\364\372\363\363\363\371\364\364\364\372\370\370" - "\370\374\366\366\366\373\371\371\371\375\373\373\373\375\370\370\370\374" - "\364\364\364\372\371\371\371\375\363\363\363\372\363\363\363\371\364\364" - "\364\372\366\366\366\373\370\370\370\374\363\363\363\372\363\363\363\372" - "\362\362\362\371\366\366\366\373\367\367\367\373\371\371\371\375\372\372" - "\372\375\375\375\375\377\377\377\377\377\373\373\373\375\370\370\370\374" - "\365\365\365\373\371\371\371\374\363\363\363\371\364\364\364\372\366\366" - "\366\373\357\357\357\370\357\357\357\370\362\362\362\371\364\364\364\372" - "\371\371\371\374\371\371\371\375\375\375\375\376\371\371\371\375\374\374" - "\374\376\371\371\371\374\363\363\363\372\365\365\365\372\365\365\365\373" - "\366\366\366\373\364\364\364\372\365\365\365\373\361\361\361\371\373\373" - "\373\375\372\372\372\375\377\377\377\377\377\377\377\377\376\376\376\377" - "\377\377\377\377\371\371\371\374\371\371\371\374\354\354\354\366\363\363" - "\363\371\365\365\365\373\364\364\364\372\370\370\370\374\364\364\364\372" - "\365\365\365\373\377\377\377\377\373\373\373\376\373\373\373\375\373\373" - "\373\375\370\370\370\374\363\363\363\371\370\370\370\374\366\366\366\373" - "\365\365\365\373\356\356\356\367\356\356\356\367\366\366\366\373\357\357" - "\357\370\363\363\363\371\366\366\366\373\357\357\357\370\372\372\372\375" - "\373\373\373\376\375\375\375\377\376\376\376\377\371\371\371\375\363\363" - "\363\372\365\365\365\372\363\363\363\372\363\363\363\372\366\366\366\373" - "\377\377\377\377\372\372\372\375\366\366\366\373\363\363\363\371\374\374" - "\374\376\377\377\377\377\374\374\374\376\371\371\371\374\373\373\373\375" - "\367\367\367\373\363\363\363\372\366\366\366\373\366\366\366\373\356\356" - "\356\367\370\370\370\374\362\362\362\371\371\371\371\375\370\370\370\374" - "\372\372\372\375\372\372\372\375\373\373\373\376\364\364\364\372\371\371" - "\371\375\363\363\363\372\366\366\366\373\367\367\367\373\365\365\365\372" - "\370\370\370\374\373\373\373\375\371\371\371\374\377\377\377\377\377\377" - "\377\377\374\374\374\376\372\372\372\375\372\372\372\375\364\364\364\372" - "\371\371\371\375\364\364\364\372\371\371\371\374\357\357\357\370\356\356" - "\356\367\366\366\366\373\366\366\366\373\367\367\367\373\363\363\363\371" - "\374\374\374\376\363\363\363\371\365\365\365\373\361\361\361\371\363\363" - "\363\372\363\363\363\372\365\365\365\372\366\366\366\373\362\362\362\371" - "\365\365\365\372\355\355\355\367\362\362\362\371\364\364\364\372\373\373" - "\373\375\373\373\373\376\371\371\371\375\374\374\374\376\374\374\374\376" - "\374\374\374\376\373\373\373\375\370\370\370\374\372\372\372\375\371\371" - "\371\374\363\363\363\371\370\370\370\374\360\360\360\370\363\363\363\372" - "\362\362\362\371\361\361\361\371\373\373\373\376\374\374\374\376\370\370" - "\370\374\371\371\371\374\363\363\363\372\371\371\371\375\365\365\365\373" - "\373\373\373\375\365\365\365\373\365\365\365\372\363\363\363\372\365\365" - "\365\372\361\361\361\371\373\373\373\376\374\374\374\376\377\377\377\377" - "\377\377\377\377\375\375\375\377\372\372\372\375\364\364\364\372\375\375" - "\375\377\374\374\374\376\372\372\372\375\354\354\354\366\365\365\365\372" - "\365\365\365\372\364\364\364\372\373\373\373\375\367\367\367\373\377\377" - "\377\377\372\372\372\375\377\377\377\377\375\375\375\376\370\370\370\374" - "\365\365\365\372\366\366\366\373\354\354\354\366\364\364\364\372\364\364" - "\364\372\351\351\351\365\370\370\370\374\364\364\364\372\365\365\365\373" - "\370\370\370\374\373\373\373\376\372\372\372\375\373\373\373\375\375\375" - "\375\377\366\366\366\373\364\364\364\372\367\367\367\373\363\363\363\372" - "\360\360\360\370\365\365\365\373\371\371\371\375\364\364\364\372\371\371" - "\371\375\373\373\373\376\367\367\367\373\374\374\374\376\372\372\372\375" - "\373\373\373\376\365\365\365\372\365\365\365\373\363\363\363\371\362\362" - "\362\371\364\364\364\372\364\364\364\372\367\367\367\373\370\370\370\374" - "\370\370\370\374\370\370\370\374\372\372\372\375\365\365\365\373\373\373" - "\373\375\362\362\362\371\365\365\365\373\367\367\367\373\366\366\366\373" - "\365\365\365\372\366\366\366\373\366\366\366\373\371\371\371\374\374\374" - "\374\376\377\377\377\377\371\371\371\375\370\370\370\374\366\366\366\373" - "\364\364\364\372\364\364\364\372\367\367\367\373\362\362\362\371\366\366" - "\366\373\363\363\363\372\361\361\361\371\365\365\365\372\372\372\372\375" - "\374\374\374\376\363\363\363\372\375\375\375\377\365\365\365\372\364\364" - "\364\372\366\366\366\373\365\365\365\372\360\360\360\370\366\366\366\373" - "\365\365\365\373\362\362\362\371\362\362\362\371\356\356\356\367\364\364" - "\364\372\364\364\364\372\375\375\375\376\374\374\374\376\371\371\371\374" - "\370\370\370\374\371\371\371\374\370\370\370\374\367\367\367\373\372\372" - "\372\375\366\366\366\373\372\372\372\375\373\373\373\376\363\363\363\371" - "\366\366\366\373\363\363\363\372\364\364\364\372\365\365\365\373\373\373" - "\373\376\373\373\373\376\375\375\375\377\374\374\374\376\366\366\366\373" - "\372\372\372\375\373\373\373\376\377\377\377\377\374\374\374\376\372\372" - "\372\375\371\371\371\374\373\373\373\375\366\366\366\373\366\366\366\373" - "\377\377\377\377\371\371\371\375\365\365\365\373\377\377\377\377\377\377" - "\377\377\374\374\374\376\375\375\375\376\374\374\374\376\370\370\370\374" - "\352\352\352\365\364\364\364\372\362\362\362\371\373\373\373\376\363\363" - "\363\371\371\371\371\375\375\375\375\376\372\372\372\375\371\371\371\375" - "\370\370\370\374\373\373\373\375\370\370\370\374\362\362\362\371\350\350" - "\350\364\363\363\363\371\372\372\372\375\364\364\364\372\363\363\363\372" - "\370\370\370\374\363\363\363\372\372\372\372\375\374\374\374\376\366\366" - "\366\373\370\370\370\374\373\373\373\376\370\370\370\374\365\365\365\373" - "\367\367\367\373\360\360\360\370\352\352\352\365\362\362\362\371\367\367" - "\367\373\370\370\370\374\371\371\371\374\373\373\373\375\371\371\371\374" - "\373\373\373\376\372\372\372\375\374\374\374\376\370\370\370\374\371\371" - "\371\374\365\365\365\372\362\362\362\371\363\363\363\372\360\360\360\370" - "\367\367\367\373\373\373\373\375\370\370\370\374\371\371\371\374\367\367" - "\367\373\365\365\365\372\367\367\367\373\362\362\362\371\365\365\365\372" - "\370\370\370\374\370\370\370\374\371\371\371\374\364\364\364\372\362\362" - "\362\371\371\371\371\375\365\365\365\373\371\371\371\375\367\367\367\373" - "\367\367\367\373\370\370\370\374\367\367\367\373\365\365\365\372\365\365" - "\365\372\366\366\366\373\366\366\366\373\366\366\366\373\365\365\365\372" - "\365\365\365\373\360\360\360\370\370\370\370\374\367\367\367\373\373\373" - "\373\375\365\365\365\372\367\367\367\373\371\371\371\374\364\364\364\372" - "\370\370\370\374\365\365\365\372\366\366\366\373\361\361\361\371\370\370" - "\370\374\365\365\365\372\363\363\363\371\370\370\370\374\366\366\366\373" - "\366\366\366\373\377\377\377\377\377\377\377\377\371\371\371\374\375\375" - "\375\376\374\374\374\376\370\370\370\374\371\371\371\375\366\366\366\373" - "\370\370\370\374\365\365\365\372\370\370\370\374\371\371\371\374\362\362" - "\362\371\374\374\374\376\373\373\373\375\374\374\374\376\373\373\373\376" - "\374\374\374\376\372\372\372\375\371\371\371\374\373\373\373\376\366\366" - "\366\373\372\372\372\375\366\366\366\373\371\371\371\375\371\371\371\374" - "\374\374\374\376\371\371\371\375\377\377\377\377\377\377\377\377\365\365" - "\365\373\377\377\377\377\377\377\377\377\373\373\373\376\374\374\374\376" - "\363\363\363\372\361\361\361\371\370\370\370\374\363\363\363\372\356\356" - "\356\367\370\370\370\374\363\363\363\372\365\365\365\373\367\367\367\373" - "\373\373\373\376\375\375\375\376\364\364\364\372\364\364\364\372\366\366" - "\366\373\370\370\370\374\366\366\366\373\362\362\362\371\355\355\355\367" - "\363\363\363\372\360\360\360\370\366\366\366\373\366\366\366\373\377\377" - "\377\377\377\377\377\377\370\370\370\374\372\372\372\375\375\375\375\376" - "\371\371\371\375\366\366\366\373\363\363\363\371\366\366\366\373\356\356" - "\356\367\370\370\370\374\365\365\365\373\373\373\373\375\376\376\376\377" - "\373\373\373\375\374\374\374\376\371\371\371\374\375\375\375\376\367\367" - "\367\373\373\373\373\375\364\364\364\372\361\361\361\371\364\364\364\372" - "\366\366\366\373\356\356\356\367\370\370\370\374\367\367\367\373\365\365" - "\365\373\364\364\364\372\372\372\372\375\357\357\357\370\364\364\364\372" - "\362\362\362\371\373\373\373\375\374\374\374\376\365\365\365\373\364\364" - "\364\372\364\364\364\372\371\371\371\375\367\367\367\373\366\366\366\373" - "\373\373\373\376\372\372\372\375\374\374\374\376\376\376\376\377\362\362" - "\362\371\360\360\360\370\366\366\366\373\366\366\366\373\350\350\350\364" - "\363\363\363\372\373\373\373\376\364\364\364\372\366\366\366\373\372\372" - "\372\375\373\373\373\376\373\373\373\376\365\365\365\373\371\371\371\375" - "\367\367\367\373\371\371\371\375\357\357\357\370\364\364\364\372\364\364" - "\364\372\366\366\366\373\365\365\365\373\370\370\370\374\365\365\365\373" - "\370\370\370\374\374\374\374\376\366\366\366\373\374\374\374\376\375\375" - "\375\377\374\374\374\376\372\372\372\375\377\377\377\377\363\363\363\371" - "\374\374\374\376\366\366\366\373\365\365\365\373\365\365\365\372\366\366" - "\366\373\356\356\356\367\363\363\363\371\370\370\370\374\367\367\367\373" - "\374\374\374\376\372\372\372\375\372\372\372\375\375\375\375\376\371\371" - "\371\375\375\375\375\376\374\374\374\376\366\366\366\373\370\370\370\374" - "\366\366\366\373\367\367\367\373\370\370\370\374\375\375\375\377\374\374" - "\374\376\373\373\373\376\377\377\377\377\377\377\377\377\373\373\373\376" - "\364\364\364\372\371\371\371\375\354\354\354\366\355\355\355\366\371\371" - "\371\375\362\362\362\371\370\370\370\374\365\365\365\372\371\371\371\375" - "\364\364\364\372\365\365\365\373\371\371\371\374\370\370\370\374\370\370" - "\370\374\364\364\364\372\365\365\365\372\364\364\364\372\370\370\370\374" - "\357\357\357\370\351\351\351\365\365\365\365\372\364\364\364\372\365\365" - "\365\372\373\373\373\376\377\377\377\377\374\374\374\376\374\374\374\376" - "\373\373\373\375\371\371\371\374\375\375\375\377\372\372\372\375\365\365" - "\365\372\371\371\371\374\365\365\365\372\370\370\370\374\370\370\370\374" - "\377\377\377\377\373\373\373\375\377\377\377\377\374\374\374\376\366\366" - "\366\373\372\372\372\375\373\373\373\375\364\364\364\372\367\367\367\373" - "\362\362\362\371\363\363\363\371\364\364\364\372\366\366\366\373\370\370" - "\370\374\367\367\367\373\370\370\370\374\364\364\364\372\373\373\373\375" - "\362\362\362\371\373\373\373\375\370\370\370\374\373\373\373\375\374\374" - "\374\376\373\373\373\375\371\371\371\375\373\373\373\376\370\370\370\374" - "\370\370\370\374\371\371\371\375\372\372\372\375\376\376\376\377\373\373" - "\373\375\364\364\364\372\372\372\372\375\367\367\367\373\373\373\373\375" - "\364\364\364\372\364\364\364\372\364\364\364\372\371\371\371\375\370\370" - "\370\374\366\366\366\373\367\367\367\373\362\362\362\371\376\376\376\377" - "\362\362\362\371\366\366\366\373\366\366\366\373\371\371\371\374\367\367" - "\367\373\364\364\364\372\362\362\362\371\357\357\357\370\364\364\364\372" - "\365\365\365\372\373\373\373\376\366\366\366\373\371\371\371\375\376\376" - "\376\377\373\373\373\376\371\371\371\374\370\370\370\374\370\370\370\374" - "\374\374\374\376\371\371\371\374\363\363\363\371\365\365\365\373\367\367" - "\367\373\373\373\373\376\372\372\372\375\374\374\374\376\363\363\363\372" - "\375\375\375\376\367\367\367\373\370\370\370\374\373\373\373\376\377\377" - "\377\377\377\377\377\377\371\371\371\375\373\373\373\375\364\364\364\372" - "\370\370\370\374\371\371\371\374\375\375\375\377\374\374\374\376\370\370" - "\370\374\372\372\372\375\377\377\377\377\377\377\377\377\377\377\377\377" - "\375\375\375\376\375\375\375\377\377\377\377\377\362\362\362\371\367\367" - "\367\373\365\365\365\373\364\364\364\372\370\370\370\374\357\357\357\370" - "\363\363\363\371\372\372\372\375\366\366\366\373\373\373\373\375\364\364" - "\364\372\363\363\363\372\372\372\372\375\370\370\370\374\370\370\370\374" - "\366\366\366\373\365\365\365\372\362\362\362\371\370\370\370\374\362\362" - "\362\371\371\371\371\375\370\370\370\374\376\376\376\377\375\375\375\377" - "\371\371\371\375\373\373\373\375\370\370\370\374\374\374\374\376\373\373" - "\373\375\373\373\373\375\373\373\373\376\374\374\374\376\365\365\365\373" - "\373\373\373\375\371\371\371\374\375\375\375\377\377\377\377\377\377\377" - "\377\377\374\374\374\376\377\377\377\377\373\373\373\376\371\371\371\374" - "\366\366\366\373\366\366\366\373\356\356\356\367\367\367\367\373\362\362" - "\362\371\370\370\370\374\366\366\366\373\371\371\371\375\367\367\367\373" - "\367\367\367\373\365\365\365\372\366\366\366\373\366\366\366\373\377\377" - "\377\377\367\367\367\373\371\371\371\375\373\373\373\376\376\376\376\377" - "\364\364\364\372\366\366\366\373\366\366\366\373\371\371\371\375\371\371" - "\371\375\375\375\375\377\372\372\372\375\367\367\367\373\365\365\365\373" - "\371\371\371\375\363\363\363\372\366\366\366\373\356\356\356\367\363\363" - "\363\372\367\367\367\373\374\374\374\376\373\373\373\376\370\370\370\374" - "\364\364\364\372\366\366\366\373\365\365\365\373\370\370\370\374\371\371" - "\371\375\357\357\357\370\363\363\363\372\364\364\364\372\360\360\360\370" - "\362\362\362\371\370\370\370\374\367\367\367\373\373\373\373\375\373\373" - "\373\375\373\373\373\375\374\374\374\376\371\371\371\375\374\374\374\376" - "\375\375\375\376\371\371\371\375\371\371\371\374\376\376\376\377\362\362" - "\362\371\366\366\366\373\364\364\364\372\371\371\371\375\365\365\365\372" - "\365\365\365\372\366\366\366\373\366\366\366\373\375\375\375\376\375\375" - "\375\376\374\374\374\376\374\374\374\376\374\374\374\376\373\373\373\376" - "\375\375\375\376\370\370\370\374\367\367\367\373\370\370\370\374\372\372" - "\372\375\371\371\371\375\371\371\371\375\366\366\366\373\374\374\374\376" - "\377\377\377\377\377\377\377\377\374\374\374\376\375\375\375\377\370\370" - "\370\374\377\377\377\377\367\367\367\373\366\366\366\373\373\373\373\375" - "\370\370\370\374\363\363\363\371\366\366\366\373\366\366\366\373\371\371" - "\371\375\372\372\372\375\370\370\370\374\370\370\370\374\372\372\372\375" - "\371\371\371\374\371\371\371\374\365\365\365\372\362\362\362\371\362\362" - "\362\371\366\366\366\373\370\370\370\374\366\366\366\373\374\374\374\376" - "\375\375\375\376\377\377\377\377\373\373\373\376\372\372\372\375\370\370" - "\370\374\374\374\374\376\373\373\373\375\371\371\371\375\371\371\371\374" - "\374\374\374\376\372\372\372\375\371\371\371\374\371\371\371\375\373\373" - "\373\376\375\375\375\376\374\374\374\376\377\377\377\377\372\372\372\375" - "\371\371\371\375\366\366\366\373\362\362\362\371\367\367\367\373\355\355" - "\355\366\357\357\357\370\362\362\362\371\364\364\364\372\370\370\370\374" - "\374\374\374\376\366\366\366\373\366\366\366\373\371\371\371\375\371\371" - "\371\374\373\373\373\375\371\371\371\375\371\371\371\375\371\371\371\375" - "\371\371\371\375\372\372\372\375\363\363\363\372\366\366\366\373\373\373" - "\373\375\374\374\374\376\371\371\371\375\374\374\374\376\372\372\372\375" - "\366\366\366\373\371\371\371\375\365\365\365\372\371\371\371\375\365\365" - "\365\372\360\360\360\370\357\357\357\370\363\363\363\371\366\366\366\373" - "\371\371\371\375\363\363\363\372\370\370\370\374\371\371\371\374\377\377" - "\377\377\370\370\370\374\370\370\370\374\366\366\366\373\365\365\365\373" - "\364\364\364\372\366\366\366\373\364\364\364\372\366\366\366\373\370\370" - "\370\374\370\370\370\374\366\366\366\373\374\374\374\376\372\372\372\375" - "\366\366\366\373\371\371\371\375\371\371\371\375\373\373\373\376\374\374" - "\374\376\366\366\366\373\365\365\365\372\365\365\365\372\363\363\363\372" - "\364\364\364\372\365\365\365\372\362\362\362\371\370\370\370\374\366\366" - "\366\373\371\371\371\375\373\373\373\376\377\377\377\377\377\377\377\377" - "\371\371\371\375\370\370\370\374\364\364\364\372\366\366\366\373\365\365" - "\365\372\362\362\362\371\366\366\366\373\365\365\365\372\370\370\370\374" - "\373\373\373\376\371\371\371\375\377\377\377\377\374\374\374\376\371\371" - "\371\375\375\375\375\376\372\372\372\375\377\377\377\377\365\365\365\373" - "\366\366\366\373\374\374\374\376\365\365\365\373\363\363\363\372\366\366" - "\366\373\375\375\375\376\373\373\373\375\372\372\372\375\373\373\373\375" - "\367\367\367\373\371\371\371\375\372\372\372\375\367\367\367\373\356\356" - "\356\367\362\362\362\371\364\364\364\372\362\362\362\371\365\365\365\372" - "\374\374\374\376\372\372\372\375\374\374\374\376\371\371\371\374\374\374" - "\374\376\372\372\372\375\373\373\373\376\370\370\370\374\373\373\373\376" - "\376\376\376\377\371\371\371\375\367\367\367\373\367\367\367\373\371\371" - "\371\375\371\371\371\375\375\375\375\376\370\370\370\374\377\377\377\377" - "\373\373\373\376\377\377\377\377\371\371\371\375\375\375\375\377\370\370" - "\370\374\365\365\365\373\362\362\362\371\370\370\370\374\362\362\362\371" - "\366\366\366\373\372\372\372\375\373\373\373\375\370\370\370\374\367\367" - "\367\373\371\371\371\374\373\373\373\376\376\376\376\377\375\375\375\376" - "\374\374\374\376\372\372\372\375\375\375\375\376\366\366\366\373\365\365" - "\365\372\361\361\361\371\375\375\375\377\371\371\371\375\373\373\373\375" - "\371\371\371\375\373\373\373\375\366\366\366\373\371\371\371\374\373\373" - "\373\376\365\365\365\373\364\364\364\372\365\365\365\372\360\360\360\370" - "\364\364\364\372\360\360\360\370\374\374\374\376\370\370\370\374\367\367" - "\367\373\367\367\367\373\371\371\371\374\375\375\375\376\370\370\370\374" - "\362\362\362\371\370\370\370\374\355\355\355\366\360\360\360\370\363\363" - "\363\371\365\365\365\372\371\371\371\375\371\371\371\375\370\370\370\374" - "\373\373\373\376\371\371\371\375\370\370\370\374\373\373\373\375\371\371" - "\371\374\371\371\371\375\373\373\373\375\370\370\370\374\371\371\371\375" - "\370\370\370\374\364\364\364\372\365\365\365\373\366\366\366\373\360\360" - "\360\370\365\365\365\373\366\366\366\373\371\371\371\375\371\371\371\374" - "\375\375\375\376\374\374\374\376\373\373\373\376\367\367\367\373\370\370" - "\370\374\363\363\363\371\365\365\365\373\364\364\364\372\370\370\370\374" - "\367\367\367\373\371\371\371\374\376\376\376\377\374\374\374\376\377\377" - "\377\377\374\374\374\376\373\373\373\375\370\370\370\374\364\364\364\372" - "\365\365\365\373\366\366\366\373\361\361\361\371\364\364\364\372\365\365" - "\365\373\367\367\367\373\366\366\366\373\377\377\377\377\371\371\371\374" - "\371\371\371\375\373\373\373\375\363\363\363\372\367\367\367\373\374\374" - "\374\376\370\370\370\374\372\372\372\375\366\366\366\373\371\371\371\375" - "\362\362\362\371\366\366\366\373\371\371\371\374\372\372\372\375\371\371" - "\371\375\377\377\377\377\374\374\374\376\370\370\370\374\371\371\371\374" - "\372\372\372\375\375\375\375\377\371\371\371\374\371\371\371\375\373\373" - "\373\375\364\364\364\372\377\377\377\377\371\371\371\374\377\377\377\377" - "\376\376\376\377\374\374\374\376\372\372\372\375\377\377\377\377\367\367" - "\367\373\375\375\375\376\362\362\362\371\366\366\366\373\364\364\364\372" - "\370\370\370\374\362\362\362\371\365\365\365\372\370\370\370\374\374\374" - "\374\376\372\372\372\375\374\374\374\376\371\371\371\375\373\373\373\375" - "\371\371\371\374\373\373\373\375\371\371\371\375\363\363\363\371\364\364" - "\364\372\361\361\361\371\363\363\363\372\365\365\365\373\367\367\367\373" - "\371\371\371\374\374\374\374\376\371\371\371\375\371\371\371\375\373\373" - "\373\375\365\365\365\372\370\370\370\374\370\370\370\374\350\350\350\364" - "\363\363\363\372\361\361\361\371\367\367\367\373\365\365\365\372\377\377" - "\377\377\370\370\370\374\365\365\365\372\370\370\370\374\364\364\364\372" - "\375\375\375\376\370\370\370\374\364\364\364\372\362\362\362\371\363\363" - "\363\371\364\364\364\372\365\365\365\372\367\367\367\373\366\366\366\373" - "\370\370\370\374\370\370\370\374\373\373\373\375\372\372\372\375\367\367" - "\367\373\370\370\370\374\370\370\370\374\371\371\371\374\370\370\370\374" - "\372\372\372\375\370\370\370\374\366\366\366\373\366\366\366\373\371\371" - "\371\374\365\365\365\373\365\365\365\372\367\367\367\373\370\370\370\374" - "\377\377\377\377\374\374\374\376\376\376\376\377\372\372\372\375\371\371" - "\371\375\370\370\370\374\366\366\366\373\362\362\362\371\362\362\362\371" - "\365\365\365\373\362\362\362\371\365\365\365\372\371\371\371\374\377\377" - "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\377" - "\370\370\370\374\372\372\372\375\366\366\366\373\366\366\366\373\367\367" - "\367\373\370\370\370\374\367\367\367\373\372\372\372\375\364\364\364\372" - "\367\367\367\373\374\374\374\376\373\373\373\375\373\373\373\375\374\374" - "\374\376\366\366\366\373\371\371\371\375\363\363\363\372\367\367\367\373" - "\373\373\373\375\371\371\371\374\371\371\371\374\366\366\366\373\367\367" - "\367\373\367\367\367\373\370\370\370\374\373\373\373\375\374\374\374\376" - "\374\374\374\376\370\370\370\374\370\370\370\374\371\371\371\375\367\367" - "\367\373\365\365\365\372\364\364\364\372\370\370\370\374\365\365\365\373" - "\376\376\376\377\377\377\377\377\372\372\372\375\375\375\375\377\371\371" - "\371\375\374\374\374\376\370\370\370\374\370\370\370\374\356\356\356\367" - "\364\364\364\372\357\357\357\370\363\363\363\372\364\364\364\372\363\363" - "\363\372\366\366\366\373\371\371\371\375\370\370\370\374\373\373\373\376" - "\374\374\374\376\372\372\372\375\373\373\373\376\365\365\365\372\374\374" - "\374\376\366\366\366\373\362\362\362\371\365\365\365\373\364\364\364\372" - "\364\364\364\372\364\364\364\372\370\370\370\374\372\372\372\375\371\371" - "\371\374\371\371\371\374\371\371\371\375\367\367\367\373\364\364\364\372" - "\371\371\371\375\356\356\356\367\362\362\362\371\357\357\357\370\373\373" - "\373\376\365\365\365\372\374\374\374\376\364\364\364\372\366\366\366\373" - "\367\367\367\373\366\366\366\373\371\371\371\374\366\366\366\373\362\362" - "\362\371\360\360\360\370\363\363\363\371\364\364\364\372\361\361\361\371" - "\365\365\365\372\367\367\367\373\371\371\371\375\373\373\373\376\373\373" - "\373\376\374\374\374\376\370\370\370\374\370\370\370\374\370\370\370\374" - "\371\371\371\374\370\370\370\374\371\371\371\374\364\364\364\372\367\367" - "\367\373\365\365\365\373\371\371\371\374\370\370\370\374\366\366\366\373" - "\370\370\370\374\371\371\371\374\374\374\374\376\371\371\371\374\370\370" - "\370\374\367\367\367\373\372\372\372\375\372\372\372\375\375\375\375\377" - "\371\371\371\374\364\364\364\372\363\363\363\372\364\364\364\372\366\366" - "\366\373\374\374\374\376\377\377\377\377\373\373\373\375\370\370\370\374" - "\377\377\377\377\377\377\377\377\373\373\373\376\367\367\367\373\373\373" - "\373\375\364\364\364\372\370\370\370\374\366\366\366\373\370\370\370\374" - "\367\367\367\373\363\363\363\372\363\363\363\372\367\367\367\373\373\373" - "\373\375\373\373\373\375\373\373\373\375\370\370\370\374\370\370\370\374" - "\365\365\365\373\364\364\364\372\372\372\372\375\366\366\366\373\364\364" - "\364\372\363\363\363\372\367\367\367\373\371\371\371\375\370\370\370\374" - "\371\371\371\374\370\370\370\374\375\375\375\376\370\370\370\374\371\371" - "\371\375\364\364\364\372\365\365\365\372\366\366\366\373\367\367\367\373" - "\366\366\366\373\365\365\365\373\376\376\376\377\374\374\374\376\370\370" - "\370\374\371\371\371\375\372\372\372\375\371\371\371\374\366\366\366\373" - "\371\371\371\374\364\364\364\372\364\364\364\372\355\355\355\367\363\363" - "\363\372\365\365\365\372\364\364\364\372\364\364\364\372\371\371\371\374" - "\370\370\370\374\373\373\373\376\366\366\366\373\371\371\371\374\371\371" - "\371\374\365\365\365\373\373\373\373\376\370\370\370\374\362\362\362\371" - "\370\370\370\374\364\364\364\372\365\365\365\373\365\365\365\372\370\370" - "\370\374\371\371\371\374\372\372\372\375\373\373\373\375\370\370\370\374" - "\365\365\365\373\366\366\366\373\366\366\366\373\366\366\366\373\362\362" - "\362\371\366\366\366\373\363\363\363\372\373\373\373\375\364\364\364\372" - "\372\372\372\375\366\366\366\373\371\371\371\374\376\376\376\377\365\365" - "\365\373\364\364\364\372\366\366\366\373\364\364\364\372\360\360\360\370" - "\364\364\364\372\362\362\362\371\366\366\366\373\367\367\367\373\366\366" - "\366\373\367\367\367\373\366\366\366\373\373\373\373\375\371\371\371\374" - "\367\367\367\373\366\366\366\373\376\376\376\377\371\371\371\375\370\370" - "\370\374\367\367\367\373\371\371\371\374\362\362\362\371\365\365\365\373" - "\365\365\365\372\371\371\371\374\371\371\371\374\372\372\372\375\372\372" - "\372\375\372\372\372\375\372\372\372\375\371\371\371\374\367\367\367\373" - "\370\370\370\374\367\367\367\373\364\364\364\372\362\362\362\371\366\366" - "\366\373\367\367\367\373\365\365\365\373\371\371\371\374\374\374\374\376" - "\377\377\377\377\373\373\373\375\375\375\375\376\374\374\374\376\366\366" - "\366\373\377\377\377\377\374\374\374\376\367\367\367\373\365\365\365\372" - "\365\365\365\373\365\365\365\373\363\363\363\372\365\365\365\373\367\367" - "\367\373\367\367\367\373\374\374\374\376\374\374\374\376\372\372\372\375" - "\370\370\370\374\370\370\370\374\365\365\365\372\370\370\370\374\371\371" - "\371\374\366\366\366\373\371\371\371\374\364\364\364\372\365\365\365\372" - "\366\366\366\373\373\373\373\375\371\371\371\375\371\371\371\374\371\371" - "\371\375\371\371\371\375\371\371\371\375\366\366\366\373\370\370\370\374" - "\371\371\371\375\371\371\371\374\370\370\370\374\372\372\372\375\370\370" - "\370\374\374\374\374\376\373\373\373\376\371\371\371\374\373\373\373\376" - "\371\371\371\375\372\372\372\375\371\371\371\375\363\363\363\372\365\365" - "\365\373\363\363\363\372\362\362\362\371\365\365\365\372\364\364\364\372" - "\366\366\366\373\370\370\370\374\370\370\370\374\375\375\375\377\374\374" - "\374\376\373\373\373\375\370\370\370\374\370\370\370\374\371\371\371\374" - "\362\362\362\371\357\357\357\370\353\353\353\366\365\365\365\372\371\371" - "\371\374\367\367\367\373\370\370\370\374\370\370\370\374\365\365\365\372" - "\367\367\367\373\370\370\370\374\363\363\363\372\366\366\366\373\365\365" - "\365\372\365\365\365\373\360\360\360\370\366\366\366\373\364\364\364\372" - "\367\367\367\373\366\366\366\373\366\366\366\373\374\374\374\376\373\373" - "\373\375\373\373\373\375\365\365\365\372\366\366\366\373\363\363\363\371" - "\363\363\363\371\360\360\360\370\364\364\364\372\365\365\365\372\366\366" - "\366\373\365\365\365\372\370\370\370\374\373\373\373\375\370\370\370\374" - "\370\370\370\374\370\370\370\374\365\365\365\372\375\375\375\376\373\373" - "\373\375\370\370\370\374\371\371\371\375\366\366\366\373\371\371\371\374" - "\362\362\362\371\370\370\370\374\370\370\370\374\363\363\363\372\367\367" - "\367\373\371\371\371\375\373\373\373\375\376\376\376\377\370\370\370\374" - "\373\373\373\376\373\373\373\375\374\374\374\376\377\377\377\377\370\370" - "\370\374\365\365\365\373\371\371\371\375\371\371\371\374\367\367\367\373" - "\370\370\370\374\373\373\373\376\375\375\375\376\367\367\367\373\374\374" - "\374\376\373\373\373\375\377\377\377\377\375\375\375\376\370\370\370\374" - "\377\377\377\377\364\364\364\372\365\365\365\372\370\370\370\374\364\364" - "\364\372\370\370\370\374\371\371\371\375\373\373\373\375\373\373\373\375" - "\371\371\371\374\371\371\371\374\363\363\363\372\371\371\371\374\363\363" - "\363\371\366\366\366\373\370\370\370\374\367\367\367\373\362\362\362\371" - "\365\365\365\373\363\363\363\372\371\371\371\375\371\371\371\374\374\374" - "\374\376\377\377\377\377\373\373\373\376\373\373\373\375\367\367\367\373" - "\371\371\371\375\371\371\371\374\365\365\365\373\366\366\366\373\366\366" - "\366\373\373\373\373\376\373\373\373\375\375\375\375\376\376\376\376\377" - "\374\374\374\376\375\375\375\377\374\374\374\376\370\370\370\374\367\367" - "\367\373\364\364\364\372\366\366\366\373\363\363\363\372\364\364\364\372" - "\364\364\364\372\363\363\363\372\367\367\367\373\366\366\366\373\374\374" - "\374\376\375\375\375\376\374\374\374\376\372\372\372\375\375\375\375\377" - "\373\373\373\376\371\371\371\374\363\363\363\371\362\362\362\371\364\364" - "\364\372\365\365\365\373\366\366\366\373\366\366\366\373\375\375\375\377" - "\372\372\372\375\370\370\370\374\366\366\366\373\370\370\370\374\367\367" - "\367\373\366\366\366\373\367\367\367\373\365\365\365\372\365\365\365\372" - "\365\365\365\373\371\371\371\374\365\365\365\372\367\367\367\373\367\367" - "\367\373\366\366\366\373\370\370\370\374\370\370\370\374\365\365\365\373" - "\370\370\370\374\364\364\364\372\362\362\362\371\363\363\363\372\366\366" - "\366\373\365\365\365\372\370\370\370\374\370\370\370\374\370\370\370\374" - "\370\370\370\374\367\367\367\373\371\371\371\374\370\370\370\374\371\371" - "\371\374\371\371\371\375\373\373\373\375\367\367\367\373\370\370\370\374" - "\373\373\373\375\364\364\364\372\363\363\363\372\364\364\364\372\371\371" - "\371\374\365\365\365\372\364\364\364\372\371\371\371\374\371\371\371\374" - "\373\373\373\376\366\366\366\373\371\371\371\375\372\372\372\375\371\371" - "\371\374\366\366\366\373\370\370\370\374\367\367\367\373\371\371\371\375" - "\363\363\363\372\367\367\367\373\366\366\366\373\367\367\367\373\373\373" - "\373\376\377\377\377\377\373\373\373\375\375\375\375\377\374\374\374\376" - "\371\371\371\374\374\374\374\376\367\367\367\373\365\365\365\372\365\365" - "\365\373\355\355\355\366\366\366\366\373\364\364\364\372\371\371\371\374" - "\370\370\370\374\367\367\367\373\366\366\366\373\366\366\366\373\364\364" - "\364\372\365\365\365\373\357\357\357\370\362\362\362\371\366\366\366\373" - "\364\364\364\372\363\363\363\371\356\356\356\367\365\365\365\372\376\376" - "\376\377\371\371\371\374\367\367\367\373\377\377\377\377\373\373\373\376" - "\373\373\373\375\373\373\373\375\375\375\375\376\371\371\371\374\371\371" - "\371\374\366\366\366\373\366\366\366\373\366\366\366\373\373\373\373\375" - "\377\377\377\377\376\376\376\377\377\377\377\377\376\376\376\377\377\377" - "\377\377\374\374\374\376\371\371\371\375\362\362\362\371\370\370\370\374" - "\365\365\365\373\363\363\363\372\370\370\370\374\366\366\366\373\371\371" - "\371\374\373\373\373\376\374\374\374\376\373\373\373\375\377\377\377\377" - "\373\373\373\375\373\373\373\375\372\372\372\375\372\372\372\375\362\362" - "\362\371\362\362\362\371\364\364\364\372\366\366\366\373\363\363\363\371" - "\366\366\366\373\370\370\370\374\370\370\370\374\373\373\373\376\370\370" - "\370\374\370\370\370\374\371\371\371\375\370\370\370\374\371\371\371\375" - "\367\367\367\373\371\371\371\374\364\364\364\372\372\372\372\375\366\366" - "\366\373\366\366\366\373\365\365\365\372\374\374\374\376\364\364\364\372" - "\371\371\371\374\371\371\371\375\370\370\370\374\361\361\361\371\364\364" - "\364\372\365\365\365\373\365\365\365\372\362\362\362\371\367\367\367\373" - "\365\365\365\373\365\365\365\373\366\366\366\373\371\371\371\375\370\370" - "\370\374\372\372\372\375\370\370\370\374\373\373\373\375\371\371\371\374" - "\372\372\372\375\365\365\365\373\373\373\373\375\365\365\365\372\365\365" - "\365\372\371\371\371\374\366\366\366\373\365\365\365\372\363\363\363\371" - "\366\366\366\373\373\373\373\376\372\372\372\375\374\374\374\376\377\377" - "\377\377\371\371\371\375\366\366\366\373\373\373\373\375\371\371\371\375" - "\370\370\370\374\370\370\370\374\370\370\370\374\371\371\371\374\366\366" - "\366\373\370\370\370\374\375\375\375\377\377\377\377\377\375\375\375\377" - "\366\366\366\373\374\374\374\376\372\372\372\375\373\373\373\376\372\372" - "\372\375\370\370\370\374\370\370\370\374\364\364\364\372\372\372\372\375" - "\370\370\370\374\372\372\372\375\364\364\364\372\374\374\374\376\362\362" - "\362\371\366\366\366\373\366\366\366\373\371\371\371\374\363\363\363\371" - "\365\365\365\373\367\367\367\373\370\370\370\374\363\363\363\372\366\366" - "\366\373\364\364\364\372\371\371\371\375\374\374\374\376\373\373\373\375" - "\374\374\374\376\372\372\372\375\372\372\372\375\374\374\374\376\372\372" - "\372\375\371\371\371\374\375\375\375\376\370\370\370\374\363\363\363\372" - "\366\366\366\373\365\365\365\372\377\377\377\377\372\372\372\375\373\373" - "\373\376\374\374\374\376\373\373\373\375\370\370\370\374\373\373\373\375" - "\365\365\365\372\370\370\370\374\365\365\365\372\364\364\364\372\362\362" - "\362\371\366\366\366\373\364\364\364\372\373\373\373\375\373\373\373\375" - "\371\371\371\374\373\373\373\375\367\367\367\373\371\371\371\374\370\370" - "\370\374\366\366\366\373\362\362\362\371\363\363\363\372\356\356\356\367" - "\357\357\357\370\362\362\362\371\370\370\370\374\367\367\367\373\372\372" - "\372\375\371\371\371\375\370\370\370\374\367\367\367\373\365\365\365\373" - "\367\367\367\373\370\370\370\374\366\366\366\373\370\370\370\374\365\365" - "\365\372\370\370\370\374\364\364\364\372\371\371\371\374\365\365\365\372" - "\373\373\373\375\364\364\364\372\367\367\367\373\365\365\365\372\365\365" - "\365\373\364\364\364\372\363\363\363\371\364\364\364\372\363\363\363\372" - "\364\364\364\372\371\371\371\375\366\366\366\373\367\367\367\373\371\371" - "\371\374\374\374\374\376\371\371\371\375\366\366\366\373\373\373\373\375" - "\376\376\376\377\371\371\371\375\373\373\373\375\371\371\371\374\371\371" - "\371\375\372\372\372\375\367\367\367\373\364\364\364\372\365\365\365\372" - "\365\365\365\372\363\363\363\372\365\365\365\373\375\375\375\377\373\373" - "\373\376\373\373\373\375\377\377\377\377\366\366\366\373\365\365\365\373" - "\375\375\375\377\373\373\373\375\372\372\372\375\370\370\370\374\371\371" - "\371\374\370\370\370\374\373\373\373\375\365\365\365\372\377\377\377\377" - "\377\377\377\377\376\376\376\377\367\367\367\373\377\377\377\377\372\372" - "\372\375\372\372\372\375\365\365\365\373\364\364\364\372\365\365\365\373" - "\364\364\364\372\367\367\367\373\366\366\366\373\370\370\370\374\366\366" - "\366\373\374\374\374\376\367\367\367\373\375\375\375\376\370\370\370\374" - "\371\371\371\375\370\370\370\374\372\372\372\375\365\365\365\373\365\365" - "\365\372\371\371\371\375\365\365\365\373\371\371\371\374\364\364\364\372" - "\371\371\371\375\374\374\374\376\374\374\374\376\371\371\371\375\370\370" - "\370\374\370\370\370\374\366\366\366\373\364\364\364\372\357\357\357\370" - "\366\366\366\373\364\364\364\372\366\366\366\373\370\370\370\374\366\366" - "\366\373\371\371\371\375\374\374\374\376\373\373\373\376\373\373\373\376" - "\366\366\366\373\366\366\366\373\365\365\365\373\367\367\367\373\364\364" - "\364\372\362\362\362\371\363\363\363\371\370\370\370\374\367\367\367\373" - "\366\366\366\373\366\366\366\373\370\370\370\374\370\370\370\374\365\365" - "\365\372\365\365\365\373\364\364\364\372\364\364\364\372\363\363\363\371" - "\354\354\354\366\354\354\354\366\365\365\365\372\365\365\365\372\370\370" - "\370\374\366\366\366\373\365\365\365\373\364\364\364\372\371\371\371\374" - "\366\366\366\373\362\362\362\371\367\367\367\373\357\357\357\370\363\363" - "\363\372\366\366\366\373\365\365\365\373\362\362\362\371\365\365\365\373" - "\374\374\374\376\373\373\373\375\370\370\370\374\373\373\373\376\371\371" - "\371\374\367\367\367\373\363\363\363\371\364\364\364\372\364\364\364\372" - "\367\367\367\373\363\363\363\372\364\364\364\372\364\364\364\372\370\370" - "\370\374\365\365\365\372\371\371\371\374\375\375\375\377\373\373\373\375" - "\366\366\366\373\376\376\376\377\366\366\366\373\372\372\372\375\370\370" - "\370\374\366\366\366\373\370\370\370\374\366\366\366\373\362\362\362\371" - "\367\367\367\373\367\367\367\373\371\371\371\374\365\365\365\373\373\373" - "\373\375\365\365\365\373\374\374\374\376\370\370\370\374\371\371\371\375" - "\373\373\373\375\370\370\370\374\365\365\365\372\366\366\366\373\364\364" - "\364\372\370\370\370\374\374\374\374\376\366\366\366\373\364\364\364\372" - "\370\370\370\374\376\376\376\377\377\377\377\377\370\370\370\374\374\374" - "\374\376\370\370\370\374\367\367\367\373\367\367\367\373\366\366\366\373" - "\362\362\362\371\365\365\365\373\363\363\363\371\361\361\361\371\366\366" - "\366\373\373\373\373\376\370\370\370\374\365\365\365\372\366\366\366\373" - "\365\365\365\373\370\370\370\374\370\370\370\374\364\364\364\372\362\362" - "\362\371\363\363\363\371\363\363\363\372\362\362\362\371\362\362\362\371" - "\365\365\365\373\366\366\366\373\371\371\371\375\370\370\370\374\377\377" - "\377\377\374\374\374\376\370\370\370\374\371\371\371\374\365\365\365\372" - "\365\365\365\372\362\362\362\371\364\364\364\372\364\364\364\372\366\366" - "\366\373\370\370\370\374\373\373\373\376\373\373\373\376\374\374\374\376" - "\373\373\373\375\371\371\371\374\365\365\365\372\370\370\370\374\362\362" - "\362\371\363\363\363\371\363\363\363\372\364\364\364\372\362\362\362\371" - "\366\366\366\373\363\363\363\372\364\364\364\372\370\370\370\374\366\366" - "\366\373\371\371\371\375\365\365\365\373\370\370\370\374\364\364\364\372" - "\364\364\364\372\361\361\361\371\364\364\364\372\357\357\357\370\363\363" - "\363\371\363\363\363\371\366\366\366\373\370\370\370\374\366\366\366\373" - "\370\370\370\374\370\370\370\374\367\367\367\373\366\366\366\373\364\364" - "\364\372\364\364\364\372\364\364\364\372\370\370\370\374\365\365\365\373" - "\364\364\364\372\366\366\366\373\366\366\366\373\372\372\372\375\371\371" - "\371\375\371\371\371\375\371\371\371\374\370\370\370\374\364\364\364\372" - "\370\370\370\374\365\365\365\372\364\364\364\372\363\363\363\372\366\366" - "\366\373\366\366\366\373\365\365\365\372\367\367\367\373\370\370\370\374" - "\375\375\375\376\366\366\366\373\371\371\371\374\366\366\366\373\366\366" - "\366\373\370\370\370\374\365\365\365\373\367\367\367\373\364\364\364\372" - "\371\371\371\375\365\365\365\372\364\364\364\372\365\365\365\372\370\370" - "\370\374\370\370\370\374\366\366\366\373\366\366\366\373\364\364\364\372" - "\364\364\364\372\366\366\366\373\370\370\370\374\370\370\370\374\365\365" - "\365\373\367\367\367\373\365\365\365\373\365\365\365\373\372\372\372\375" - "\364\364\364\372\366\366\366\373\364\364\364\372\372\372\372\375\377\377" - "\377\377\375\375\375\377\371\371\371\374\372\372\372\375\376\376\376\377" - "\374\374\374\376\371\371\371\374\371\371\371\374\364\364\364\372\367\367" - "\367\373\370\370\370\374\366\366\366\373\371\371\371\374\365\365\365\373" - "\366\366\366\373\364\364\364\372\367\367\367\373\370\370\370\374\366\366" - "\366\373\364\364\364\372\363\363\363\372\363\363\363\371\362\362\362\371" - "\364\364\364\372\363\363\363\372\366\366\366\373\365\365\365\373\366\366" - "\366\373\371\371\371\375\371\371\371\374\371\371\371\375\367\367\367\373" - "\371\371\371\375\363\363\363\372\363\363\363\371\360\360\360\370\363\363" - "\363\372\370\370\370\374\366\366\366\373\366\366\366\373\372\372\372\375" - "\365\365\365\373\371\371\371\374\366\366\366\373\366\366\366\373\366\366" - "\366\373\371\371\371\374\362\362\362\371\364\364\364\372\366\366\366\373" - "\363\363\363\371\362\362\362\371\364\364\364\372\365\365\365\373\363\363" - "\363\371\375\375\375\376\372\372\372\375\367\367\367\373\366\366\366\373" - "\367\367\367\373\364\364\364\372\362\362\362\371\356\356\356\367\365\365" - "\365\372\356\356\356\367\363\363\363\371\364\364\364\372\367\367\367\373" - "\370\370\370\374\366\366\366\373\370\370\370\374\363\363\363\371\366\366" - "\366\373\366\366\366\373\365\365\365\372\367\367\367\373\363\363\363\372" - "\366\366\366\373\366\366\366\373\370\370\370\374\366\366\366\373\371\371" - "\371\374\370\370\370\374\371\371\371\375\370\370\370\374\374\374\374\376" - "\366\366\366\373\365\365\365\373\367\367\367\373\364\364\364\372\364\364" - "\364\372\361\361\361\371\366\366\366\373\365\365\365\372\364\364\364\372" - "\365\365\365\372\365\365\365\372\373\373\373\375\370\370\370\374\367\367" - "\367\373\371\371\371\374\371\371\371\374\370\370\370\374\367\367\367\373" - "\370\370\370\374\365\365\365\373\370\370\370\374\366\366\366\373\365\365" - "\365\373\366\366\366\373\366\366\366\373\367\367\367\373\371\371\371\375" - "\372\372\372\375\367\367\367\373\366\366\366\373\365\365\365\372\365\365" - "\365\373\370\370\370\374\365\365\365\372\370\370\370\374\371\371\371\374" - "\363\363\363\371\364\364\364\372\363\363\363\371\362\362\362\371\371\371" - "\371\374\371\371\371\375\375\375\375\377\373\373\373\375\366\366\366\373" - "\373\373\373\375\375\375\375\377\371\371\371\375\372\372\372\375\371\371" - "\371\375\366\366\366\373\356\356\356\367\367\367\367\373\367\367\367\373" - "\363\363\363\372\364\364\364\372\366\366\366\373\366\366\366\373\371\371" - "\371\375\364\364\364\372\367\367\367\373\365\365\365\372\362\362\362\371" - "\364\364\364\372\362\362\362\371\364\364\364\372\365\365\365\372\366\366" - "\366\373\366\366\366\373\370\370\370\374\367\367\367\373\370\370\370\374" - "\371\371\371\374\366\366\366\373\366\366\366\373\365\365\365\373\361\361" - "\361\371\355\355\355\367\363\363\363\371\362\362\362\371\367\367\367\373" - "\366\366\366\373\371\371\371\374\371\371\371\374\367\367\367\373\370\370" - "\370\374\370\370\370\374\370\370\370\374\371\371\371\375\366\366\366\373" - "\364\364\364\372\363\363\363\372\363\363\363\372\364\364\364\372\361\361" - "\361\371\370\370\370\374\366\366\366\373\370\370\370\374\366\366\366\373" - "\367\367\367\373\365\365\365\372\366\366\366\373\363\363\363\372\363\363" - "\363\371\361\361\361\371\364\364\364\372\352\352\352\365\356\356\356\367" - "\361\361\361\371\366\366\366\373\374\374\374\376\366\366\366\373\365\365" - "\365\373\365\365\365\373\370\370\370\374\365\365\365\372\367\367\367\373" - "\361\361\361\371\362\362\362\371\365\365\365\372\363\363\363\372\364\364" - "\364\372\371\371\371\374\370\370\370\374\371\371\371\375\364\364\364\372" - "\366\366\366\373\371\371\371\374\366\366\366\373\371\371\371\375\365\365" - "\365\373\370\370\370\374\366\366\366\373\360\360\360\370\362\362\362\371" - "\363\363\363\371\363\363\363\371\367\367\367\373\367\367\367\373\367\367" - "\367\373\367\367\367\373\365\365\365\373\367\367\367\373\371\371\371\375" - "\367\367\367\373\366\366\366\373\370\370\370\374\371\371\371\374\361\361" - "\361\371\365\365\365\373\364\364\364\372\365\365\365\372\365\365\365\373" - "\366\366\366\373\371\371\371\375\367\367\367\373\370\370\370\374\371\371" - "\371\375\367\367\367\373\370\370\370\374\371\371\371\375\366\366\366\373" - "\366\366\366\373\367\367\367\373\363\363\363\372\365\365\365\373\371\371" - "\371\375\354\354\354\366\366\366\366\373\373\373\373\376\373\373\373\375" - "\375\375\375\376\366\366\366\373\371\371\371\375\375\375\375\377\371\371" - "\371\375\371\371\371\374\364\364\364\372\365\365\365\372\363\363\363\372" - "\366\366\366\373\367\367\367\373\370\370\370\374\367\367\367\373\370\370" - "\370\374\366\366\366\373\374\374\374\376\365\365\365\373\366\366\366\373" - "\365\365\365\373\362\362\362\371\364\364\364\372\364\364\364\372\365\365" - "\365\373\365\365\365\372\366\366\366\373\367\367\367\373\371\371\371\375" - "\370\370\370\374\370\370\370\374\372\372\372\375\366\366\366\373\365\365" - "\365\373\355\355\355\366\364\364\364\372\362\362\362\371\363\363\363\372" - "\360\360\360\370\364\364\364\372\363\363\363\371\367\367\367\373\367\367" - "\367\373\373\373\373\375\370\370\370\374\371\371\371\374\365\365\365\373" - "\366\366\366\373\366\366\366\373\366\366\366\373\357\357\357\370\364\364" - "\364\372\366\366\366\373\364\364\364\372\363\363\363\372\366\366\366\373" - "\366\366\366\373\364\364\364\372\374\374\374\376\366\366\366\373\365\365" - "\365\373\363\363\363\371\365\365\365\372\363\363\363\371\363\363\363\372" - "\355\355\355\366\363\363\363\371\362\362\362\371\366\366\366\373\366\366" - "\366\373\365\365\365\372\366\366\366\373\363\363\363\372\364\364\364\372" - "\371\371\371\374\371\371\371\375\362\362\362\371\363\363\363\372\363\363" - "\363\372\362\362\362\371\362\362\362\371\366\366\366\373\365\365\365\373" - "\366\366\366\373\370\370\370\374\365\365\365\373\372\372\372\375\365\365" - "\365\372\370\370\370\374\366\366\366\373\365\365\365\372\364\364\364\372" - "\362\362\362\371\363\363\363\372\366\366\366\373\361\361\361\371\364\364" - "\364\372\367\367\367\373\371\371\371\374\372\372\372\375\365\365\365\372" - "\371\371\371\375\373\373\373\375\371\371\371\374\365\365\365\373\367\367" - "\367\373\371\371\371\374\366\366\366\373\363\363\363\372\366\366\366\373" - "\367\367\367\373\370\370\370\374\370\370\370\374\366\366\366\373\374\374" - "\374\376\372\372\372\375\373\373\373\376\373\373\373\376\370\370\370\374" - "\370\370\370\374\370\370\370\374\370\370\370\374\365\365\365\373\370\370" - "\370\374\365\365\365\373\366\366\366\373\357\357\357\370\357\357\357\370" - "\374\374\374\376\366\366\366\373\377\377\377\377\371\371\371\375\371\371" - "\371\375\375\375\375\377\370\370\370\374\370\370\370\374\365\365\365\373" - "\357\357\357\370\362\362\362\371\370\370\370\374\370\370\370\374\365\365" - "\365\373\365\365\365\373\371\371\371\375\370\370\370\374\373\373\373\376" - "\370\370\370\374\365\365\365\372\363\363\363\371\362\362\362\371\360\360" - "\360\370\361\361\361\371\366\366\366\373\366\366\366\373\367\367\367\373" - "\370\370\370\374\373\373\373\375\373\373\373\375\370\370\370\374\365\365" - "\365\373\363\363\363\372\365\365\365\372\370\370\370\374\363\363\363\372" - "\362\362\362\371\357\357\357\370\364\364\364\372\362\362\362\371\363\363" - "\363\371\366\366\366\373\370\370\370\374\371\371\371\375\370\370\370\374" - "\366\366\366\373\363\363\363\372\366\366\366\373\362\362\362\371\363\363" - "\363\372\357\357\357\370\363\363\363\371\366\366\366\373\363\363\363\371" - "\365\365\365\373\371\371\371\374\370\370\370\374\367\367\367\373\372\372" - "\372\375\367\367\367\373\364\364\364\372\351\351\351\365\355\355\355\367" - "\362\362\362\371\363\363\363\372\365\365\365\372\363\363\363\371\364\364" - "\364\372\363\363\363\372\371\371\371\375\372\372\372\375\366\366\366\373" - "\367\367\367\373\364\364\364\372\370\370\370\374\371\371\371\374\363\363" - "\363\372\367\367\367\373\365\365\365\373\361\361\361\371\365\365\365\372" - "\364\364\364\372\357\357\357\370\371\371\371\375\374\374\374\376\373\373" - "\373\375\364\364\364\372\367\367\367\373\365\365\365\372\367\367\367\373" - "\360\360\360\370\360\360\360\370\363\363\363\371\363\363\363\371\366\366" - "\366\373\362\362\362\371\360\360\360\370\370\370\370\374\366\366\366\373" - "\373\373\373\376\370\370\370\374\373\373\373\375\370\370\370\374\364\364" - "\364\372\366\366\366\373\366\366\366\373\370\370\370\374\371\371\371\374" - "\363\363\363\372\365\365\365\372\362\362\362\371\366\366\366\373\367\367" - "\367\373\363\363\363\371\370\370\370\374\371\371\371\374\371\371\371\375" - "\372\372\372\375\367\367\367\373\374\374\374\376\370\370\370\374\365\365" - "\365\373\362\362\362\371\365\365\365\372\362\362\362\371\366\366\366\373" - "\362\362\362\371\370\370\370\374\364\364\364\372\371\371\371\374\375\375" - "\375\376\370\370\370\374\370\370\370\374\370\370\370\374\364\364\364\372" - "\365\365\365\372\367\367\367\373\363\363\363\372\365\365\365\373\371\371" - "\371\374\371\371\371\374\374\374\374\376\373\373\373\375\375\375\375\376" - "\377\377\377\377\373\373\373\376\370\370\370\374\365\365\365\373\364\364" - "\364\372\356\356\356\367\364\364\364\372\362\362\362\371\363\363\363\371" - "\361\361\361\371\370\370\370\374\367\367\367\373\370\370\370\374\367\367" - "\367\373\360\360\360\370\364\364\364\372\364\364\364\372\364\364\364\372" - "\366\366\366\373\363\363\363\372\366\366\366\373\361\361\361\371\356\356" - "\356\367\361\361\361\371\364\364\364\372\365\365\365\373\371\371\371\375" - "\370\370\370\374\373\373\373\375\365\365\365\373\366\366\366\373\366\366" - "\366\373\366\366\366\373\367\367\367\373\362\362\362\371\362\362\362\371" - "\364\364\364\372\365\365\365\372\366\366\366\373\371\371\371\375\367\367" - "\367\373\367\367\367\373\365\365\365\373\366\366\366\373\364\364\364\372" - "\352\352\352\365\363\363\363\372\363\363\363\371\366\366\366\373\362\362" - "\362\371\364\364\364\372\366\366\366\373\370\370\370\374\370\370\370\374" - "\373\373\373\375\367\367\367\373\370\370\370\374\366\366\366\373\364\364" - "\364\372\365\365\365\372\365\365\365\373\362\362\362\371\357\357\357\370" - "\365\365\365\373\371\371\371\375\370\370\370\374\365\365\365\373\370\370" - "\370\374\370\370\370\374\367\367\367\373\370\370\370\374\370\370\370\374" - "\367\367\367\373\366\366\366\373\355\355\355\366\360\360\360\370\362\362" - "\362\371\357\357\357\370\365\365\365\372\363\363\363\371\365\365\365\373" - "\364\364\364\372\364\364\364\372\370\370\370\374\367\367\367\373\370\370" - "\370\374\366\366\366\373\367\367\367\373\370\370\370\374\365\365\365\373" - "\372\372\372\375\363\363\363\372\363\363\363\371\356\356\356\367\362\362" - "\362\371\364\364\364\372\367\367\367\373\365\365\365\372\370\370\370\374" - "\373\373\373\376\374\374\374\376\370\370\370\374\372\372\372\375\370\370" - "\370\374\370\370\370\374\365\365\365\373\366\366\366\373\366\366\366\373" - "\364\364\364\372\357\357\357\370\370\370\370\374\365\365\365\373\371\371" - "\371\374\371\371\371\374\375\375\375\376\371\371\371\375\371\371\371\375" - "\371\371\371\375\363\363\363\372\365\365\365\373\370\370\370\374\362\362" - "\362\371\364\364\364\372\365\365\365\373\371\371\371\374\373\373\373\376" - "\377\377\377\377\371\371\371\375\371\371\371\375\371\371\371\374\366\366" - "\366\373\366\366\366\373\365\365\365\372\356\356\356\367\364\364\364\372" - "\364\364\364\372\364\364\364\372\363\363\363\372\367\367\367\373\371\371" - "\371\374\372\372\372\375\372\372\372\375\365\365\365\372\365\365\365\372" - "\360\360\360\370\366\366\366\373\365\365\365\372\366\366\366\373\363\363" - "\363\372\362\362\362\371\365\365\365\372\362\362\362\371\361\361\361\371" - "\370\370\370\374\370\370\370\374\366\366\366\373\373\373\373\376\371\371" - "\371\374\365\365\365\373\367\367\367\373\371\371\371\374\364\364\364\372" - "\362\362\362\371\366\366\366\373\366\366\366\373\365\365\365\372\373\373" - "\373\375\371\371\371\375\366\366\366\373\367\367\367\373\371\371\371\375" - "\370\370\370\374\366\366\366\373\357\357\357\370\364\364\364\372\364\364" - "\364\372\366\366\366\373\356\356\356\367\362\362\362\371\367\367\367\373" - "\364\364\364\372\365\365\365\373\365\365\365\373\370\370\370\374\371\371" - "\371\374\364\364\364\372\356\356\356\367\364\364\364\372\364\364\364\372" - "\356\356\356\367\354\354\354\366\356\356\356\367\361\361\361\371\353\353" - "\353\366\363\363\363\372\371\371\371\375\370\370\370\374\364\364\364\372" - "\372\372\372\375\371\371\371\375\367\367\367\373\366\366\366\373\365\365" - "\365\372\364\364\364\372\362\362\362\371\361\361\361\371\362\362\362\371" - "\367\367\367\373\363\363\363\372\362\362\362\371\364\364\364\372\366\366" - "\366\373\370\370\370\374\370\370\370\374\367\367\367\373\365\365\365\372" - "\366\366\366\373\362\362\362\371\365\365\365\373\364\364\364\372\365\365" - "\365\372\365\365\365\373\357\357\357\370\360\360\360\370\365\365\365\373" - "\366\366\366\373\365\365\365\372\370\370\370\374\371\371\371\374\377\377" - "\377\377\374\374\374\376\373\373\373\375\371\371\371\374\370\370\370\374" - "\371\371\371\375\364\364\364\372\363\363\363\371\363\363\363\372\371\371" - "\371\374\372\372\372\375\370\370\370\374\371\371\371\375\366\366\366\373" - "\372\372\372\375\366\366\366\373\364\364\364\372\366\366\366\373\367\367" - "\367\373\367\367\367\373\363\363\363\371\364\364\364\372\362\362\362\371" - "\366\366\366\373\373\373\373\375\374\374\374\376\374\374\374\376\367\367" - "\367\373\363\363\363\371\374\374\374\376\367\367\367\373\366\366\366\373" - "\362\362\362\371\364\364\364\372\362\362\362\371\362\362\362\371\365\365" - "\365\372\362\362\362\371\366\366\366\373\367\367\367\373\372\372\372\375" - "\364\364\364\372\363\363\363\371\363\363\363\371\364\364\364\372\365\365" - "\365\372\371\371\371\374\357\357\357\370\364\364\364\372\356\356\356\367" - "\360\360\360\370\363\363\363\371\367\367\367\373\370\370\370\374\371\371" - "\371\375\366\366\366\373\367\367\367\373\366\366\366\373\371\371\371\374" - "\364\364\364\372\365\365\365\373\357\357\357\370\363\363\363\372\365\365" - "\365\373\370\370\370\374\366\366\366\373\366\366\366\373\364\364\364\372" - "\365\365\365\372\364\364\364\372\364\364\364\372\371\371\371\374\365\365" - "\365\372\373\373\373\376\365\365\365\372\363\363\363\372\364\364\364\372" - "\366\366\366\373\365\365\365\372\367\367\367\373\365\365\365\373\371\371" - "\371\374\367\367\367\373\365\365\365\373\363\363\363\372\356\356\356\367" - "\362\362\362\371\360\360\360\370\355\355\355\366\363\363\363\371\362\362" - "\362\371\361\361\361\371\364\364\364\372\365\365\365\373\367\367\367\373" - "\372\372\372\375\352\352\352\365\374\374\374\376\374\374\374\376\365\365" - "\365\373\364\364\364\372\370\370\370\374\366\366\366\373\360\360\360\370" - "\362\362\362\371\362\362\362\371\370\370\370\374\363\363\363\372\362\362" - "\362\371\365\365\365\372\363\363\363\372\370\370\370\374\366\366\366\373" - "\370\370\370\374\370\370\370\374\371\371\371\375\372\372\372\375\367\367" - "\367\373\366\366\366\373\366\366\366\373\374\374\374\376\350\350\350\364" - "\361\361\361\371\364\364\364\372\365\365\365\372\363\363\363\371\373\373" - "\373\376\371\371\371\375\373\373\373\375\373\373\373\375\374\374\374\376" - "\365\365\365\373\365\365\365\373\365\365\365\372\370\370\370\374\365\365" - "\365\373\375\375\375\376\365\365\365\373\371\371\371\374\366\366\366\373" - "\366\366\366\373\371\371\371\374\373\373\373\375\364\364\364\372\365\365" - "\365\372\373\373\373\375\364\364\364\372\363\363\363\372\361\361\361\371" - "\362\362\362\371\364\364\364\372\366\366\366\373\370\370\370\374\375\375" - "\375\377\367\367\367\373\365\365\365\372\362\362\362\371\372\372\372\375" - "\370\370\370\374\365\365\365\372\357\357\357\370\362\362\362\371\364\364" - "\364\372\362\362\362\371\363\363\363\372\363\363\363\372\365\365\365\372" - "\376\376\376\377\377\377\377\377\366\366\366\373\364\364\364\372\365\365" - "\365\373\362\362\362\371\366\366\366\373\366\366\366\373\370\370\370\374" - "\364\364\364\372\357\357\357\370\355\355\355\366\364\364\364\372\373\373" - "\373\375\371\371\371\374\371\371\371\375\371\371\371\375\365\365\365\373" - "\366\366\366\373\373\373\373\376\370\370\370\374\361\361\361\371\364\364" - "\364\372\363\363\363\372\362\362\362\371\362\362\362\371\371\371\371\374" - "\367\367\367\373\370\370\370\374\366\366\366\373\372\372\372\375\371\371" - "\371\374\362\362\362\371\364\364\364\372\364\364\364\372\364\364\364\372" - "\364\364\364\372\366\366\366\373\362\362\362\371\364\364\364\372\365\365" - "\365\372\370\370\370\374\366\366\366\373\366\366\366\373\364\364\364\372" - "\366\366\366\373\363\363\363\372\365\365\365\372\357\357\357\370\360\360" - "\360\370\356\356\356\367\360\360\360\370\362\362\362\371\364\364\364\372" - "\372\372\372\375\365\365\365\373\366\366\366\373\367\367\367\373\372\372" - "\372\375\373\373\373\375\372\372\372\375\363\363\363\372\363\363\363\371" - "\365\365\365\372\364\364\364\372\363\363\363\372\370\370\370\374\363\363" - "\363\372\366\366\366\373\364\364\364\372\364\364\364\372\365\365\365\372" - "\367\367\367\373\371\371\371\375\370\370\370\374\367\367\367\373\365\365" - "\365\373\366\366\366\373\364\364\364\372\370\370\370\374\370\370\370\374" - "\370\370\370\374\366\366\366\373\362\362\362\371\364\364\364\372\366\366" - "\366\373\371\371\371\374\370\370\370\374\373\373\373\375\371\371\371\374" - "\372\372\372\375\373\373\373\376\371\371\371\374\363\363\363\371\365\365" - "\365\372\367\367\367\373\363\363\363\372\362\362\362\371\373\373\373\375" - "\371\371\371\375\371\371\371\374\366\366\366\373\372\372\372\375\366\366" - "\366\373\365\365\365\372\366\366\366\373\363\363\363\372\360\360\360\370" - "\364\364\364\372\357\357\357\370\366\366\366\373\366\366\366\373\370\370" - "\370\374\375\375\375\376\374\374\374\376\374\374\374\376\367\367\367\373" - "\365\365\365\372\375\375\375\376\374\374\374\376\373\373\373\376\370\370" - "\370\374\362\362\362\371\363\363\363\371\363\363\363\372\361\361\361\371" - "\362\362\362\371\367\367\367\373\367\367\367\373\370\370\370\374\362\362" - "\362\371\364\364\364\372\364\364\364\372\364\364\364\372\366\366\366\373" - "\365\365\365\372\365\365\365\373\364\364\364\372\363\363\363\371\364\364" - "\364\372\364\364\364\372\371\371\371\374\375\375\375\376\371\371\371\375" - "\373\373\373\375\370\370\370\374\362\362\362\371\363\363\363\372\366\366" - "\366\373\362\362\362\371\355\355\355\367\365\365\365\373\365\365\365\373" - "\354\354\354\366\362\362\362\371\366\366\366\373\373\373\373\375\373\373" - "\373\376\374\374\374\376\365\365\365\372\363\363\363\372\364\364\364\372" - "\356\356\356\367\363\363\363\371\365\365\365\373\372\372\372\375\364\364" - "\364\372\357\357\357\370\375\375\375\376\366\366\366\373\374\374\374\376" - "\370\370\370\374\365\365\365\373\370\370\370\374\364\364\364\372\367\367" - "\367\373\366\366\366\373\367\367\367\373\351\351\351\365\364\364\364\372" - "\356\356\356\367\364\364\364\372\371\371\371\375\365\365\365\373\371\371" - "\371\374\372\372\372\375\372\372\372\375\371\371\371\375\363\363\363\371" - "\366\366\366\373\367\367\367\373\365\365\365\372\365\365\365\372\361\361" - "\361\371\363\363\363\372\370\370\370\374\367\367\367\373\364\364\364\372" - "\363\363\363\371\365\365\365\372\364\364\364\372\367\367\367\373\370\370" - "\370\374\366\366\366\373\370\370\370\374\365\365\365\373\363\363\363\372" - "\370\370\370\374\370\370\370\374\367\367\367\373\373\373\373\375\366\366" - "\366\373\366\366\366\373\366\366\366\373\370\370\370\374\366\366\366\373" - "\373\373\373\375\375\375\375\376\374\374\374\376\363\363\363\371\373\373" - "\373\376\374\374\374\376\363\363\363\372\365\365\365\372\366\366\366\373" - "\370\370\370\374\370\370\370\374\371\371\371\375\370\370\370\374\371\371" - "\371\374\371\371\371\374\364\364\364\372\371\371\371\374\370\370\370\374" - "\364\364\364\372\374\374\374\376\364\364\364\372\354\354\354\366\363\363" - "\363\372\366\366\366\373\370\370\370\374\371\371\371\374\371\371\371\374" - "\371\371\371\375\371\371\371\375\366\366\366\373\377\377\377\377\370\370" - "\370\374\372\372\372\375\370\370\370\374\364\364\364\372\362\362\362\371" - "\362\362\362\371\363\363\363\372\363\363\363\371\370\370\370\374\372\372" - "\372\375\374\374\374\376\364\364\364\372\367\367\367\373\370\370\370\374" - "\365\365\365\372\372\372\372\375\364\364\364\372\370\370\370\374\361\361" - "\361\371\364\364\364\372\362\362\362\371\364\364\364\372\372\372\372\375" - "\375\375\375\376\371\371\371\374\373\373\373\375\370\370\370\374\367\367" - "\367\373\363\363\363\371\363\363\363\371\367\367\367\373\351\351\351\365" - "\352\352\352\365\364\364\364\372\357\357\357\370\363\363\363\372\364\364" - "\364\372\366\366\366\373\375\375\375\377\367\367\367\373\367\367\367\373" - "\367\367\367\373\365\365\365\372\362\362\362\371\363\363\363\371\366\366" - "\366\373\366\366\366\373\366\366\366\373\364\364\364\372\366\366\366\373" - "\371\371\371\374\370\370\370\374\370\370\370\374\364\364\364\372\365\365" - "\365\373\367\367\367\373\371\371\371\374\365\365\365\372\365\365\365\373" - "\360\360\360\370\363\363\363\372\370\370\370\374\366\366\366\373\366\366" - "\366\373\373\373\373\376\373\373\373\376\373\373\373\375\372\372\372\375" - "\370\370\370\374\366\366\366\373\364\364\364\372\371\371\371\375\364\364" - "\364\372\365\365\365\372\366\366\366\373\367\367\367\373\370\370\370\374" - "\370\370\370\374\363\363\363\371\364\364\364\372\370\370\370\374\366\366" - "\366\373\364\364\364\372\375\375\375\377\370\370\370\374\373\373\373\375" - "\370\370\370\374\371\371\371\374\370\370\370\374\370\370\370\374\364\364" - "\364\372\366\366\366\373\363\363\363\371\365\365\365\373\365\365\365\373" - "\371\371\371\374\373\373\373\375\377\377\377\377\371\371\371\375\371\371" - "\371\374\370\370\370\374\365\365\365\373\370\370\370\374\371\371\371\374" - "\370\370\370\374\366\366\366\373\363\363\363\372\364\364\364\372\370\370" - "\370\374\371\371\371\374\371\371\371\374\371\371\371\374\365\365\365\372" - "\371\371\371\374\366\366\366\373\373\373\373\375\371\371\371\374\363\363" - "\363\371\363\363\363\371\366\366\366\373\365\365\365\373\367\367\367\373" - "\373\373\373\375\367\367\367\373\371\371\371\375\375\375\375\377\372\372" - "\372\375\371\371\371\374\371\371\371\374\367\367\367\373\366\366\366\373" - "\362\362\362\371\360\360\360\370\361\361\361\371\363\363\363\371\363\363" - "\363\372\367\367\367\373\366\366\366\373\366\366\366\373\366\366\366\373" - "\367\367\367\373\371\371\371\374\365\365\365\372\371\371\371\374\365\365" - "\365\372\370\370\370\374\362\362\362\371\366\366\366\373\363\363\363\371" - "\364\364\364\372\377\377\377\377\377\377\377\377\373\373\373\376\375\375" - "\375\376\373\373\373\375\374\374\374\376\365\365\365\372\362\362\362\371" - "\367\367\367\373\357\357\357\370\361\361\361\371\356\356\356\367\363\363" - "\363\371\371\371\371\375\370\370\370\374\366\366\366\373\370\370\370\374" - "\362\362\362\371\370\370\370\374\356\356\356\367\370\370\370\374\364\364" - "\364\372\361\361\361\371\365\365\365\373\363\363\363\371\364\364\364\372" - "\371\371\371\374\372\372\372\375\371\371\371\374\370\370\370\374\370\370" - "\370\374\366\366\366\373\371\371\371\374\364\364\364\372\364\364\364\372" - "\364\364\364\372\365\365\365\373\363\363\363\372\363\363\363\372\366\366" - "\366\373\366\366\366\373\374\374\374\376\373\373\373\375\373\373\373\376" - "\374\374\374\376\372\372\372\375\374\374\374\376\370\370\370\374\365\365" - "\365\373\366\366\366\373\366\366\366\373\371\371\371\375\364\364\364\372" - "\364\364\364\372\365\365\365\373\366\366\366\373\366\366\366\373\365\365" - "\365\372\373\373\373\375\371\371\371\374\371\371\371\374\371\371\371\374" - "\374\374\374\376\372\372\372\375\371\371\371\374\371\371\371\374\370\370" - "\370\374\366\366\366\373\371\371\371\374\364\364\364\372\363\363\363\371" - "\365\365\365\373\371\371\371\375\366\366\366\373\377\377\377\377\375\375" - "\375\377\377\377\377\377\372\372\372\375\366\366\366\373\373\373\373\375" - "\371\371\371\374\374\374\374\376\371\371\371\374\370\370\370\374\365\365" - "\365\373\362\362\362\371\366\366\366\373\370\370\370\374\367\367\367\373" - "\371\371\371\374\373\373\373\375\367\367\367\373\362\362\362\371\372\372" - "\372\375\364\364\364\372\373\373\373\375\367\367\367\373\364\364\364\372" - "\367\367\367\373\371\371\371\375\371\371\371\375\370\370\370\374\366\366" - "\366\373\375\375\375\376\371\371\371\375\371\371\371\374\374\374\374\376" - "\370\370\370\374\366\366\366\373\364\364\364\372\365\365\365\372\365\365" - "\365\373\363\363\363\371\365\365\365\372\373\373\373\376\371\371\371\374" - "\372\372\372\375\364\364\364\372\363\363\363\371\367\367\367\373\371\371" - "\371\374\364\364\364\372\370\370\370\374\362\362\362\371\363\363\363\372" - "\360\360\360\370\365\365\365\372\370\370\370\374\377\377\377\377\373\373" - "\373\376\373\373\373\376\374\374\374\376\365\365\365\373\371\371\371\374" - "\367\367\367\373\364\364\364\372\364\364\364\372\360\360\360\370\363\363" - "\363\372\364\364\364\372\365\365\365\373\371\371\371\374\364\364\364\372" - "\370\370\370\374\371\371\371\375\371\371\371\374\370\370\370\374\364\364" - "\364\372\370\370\370\374\366\366\366\373\367\367\367\373\366\366\366\373" - "\363\363\363\371\365\365\365\372\363\363\363\371\371\371\371\375\366\366" - "\366\373\370\370\370\374\366\366\366\373\366\366\366\373\365\365\365\372" - "\371\371\371\375\371\371\371\374\370\370\370\374\370\370\370\374\363\363" - "\363\371\362\362\362\371\362\362\362\371\371\371\371\374\377\377\377\377" - "\374\374\374\376\372\372\372\375\370\370\370\374\366\366\366\373\364\364" - "\364\372\362\362\362\371\362\362\362\371\370\370\370\374\373\373\373\375" - "\367\367\367\373\365\365\365\373\365\365\365\373\362\362\362\371\364\364" - "\364\372\366\366\366\373\365\365\365\373\370\370\370\374\374\374\374\376" - "\370\370\370\374\370\370\370\374\374\374\374\376\373\373\373\375\374\374" - "\374\376\375\375\375\376\374\374\374\376\370\370\370\374\370\370\370\374" - "\363\363\363\372\371\371\371\374\357\357\357\370\370\370\370\374\376\376" - "\376\377\373\373\373\375\374\374\374\376\375\375\375\377\371\371\371\375" - "\375\375\375\376\366\366\366\373\377\377\377\377\374\374\374\376\372\372" - "\372\375\377\377\377\377\371\371\371\375\357\357\357\370\360\360\360\370" - "\370\370\370\374\357\357\357\370\373\373\373\376\365\365\365\373\362\362" - "\362\371\366\366\366\373\371\371\371\374\370\370\370\374\367\367\367\373" - "\367\367\367\373\371\371\371\375\363\363\363\372\374\374\374\376\373\373" - "\373\375\374\374\374\376\370\370\370\374\367\367\367\373\366\366\366\373" - "\365\365\365\372\370\370\370\374\373\373\373\375\364\364\364\372\364\364" - "\364\372\366\366\366\373\373\373\373\375\373\373\373\375\362\362\362\371" - "\372\372\372\375\371\371\371\375\370\370\370\374\361\361\361\371\365\365" - "\365\372\370\370\370\374\365\365\365\373\366\366\366\373\371\371\371\374" - "\361\361\361\371\357\357\357\370\361\361\361\371\363\363\363\371\371\371" - "\371\374\374\374\374\376\367\367\367\373\376\376\376\377\372\372\372\375" - "\366\366\366\373\371\371\371\375\364\364\364\372\366\366\366\373\365\365" - "\365\372\365\365\365\372\362\362\362\371\370\370\370\374\370\370\370\374" - "\371\371\371\375\364\364\364\372\371\371\371\374\365\365\365\373\374\374" - "\374\376\365\365\365\372\365\365\365\373\366\366\366\373\370\370\370\374" - "\364\364\364\372\366\366\366\373\366\366\366\373\366\366\366\373\370\370" - "\370\374\373\373\373\375\367\367\367\373\373\373\373\375\374\374\374\376" - "\371\371\371\374\367\367\367\373\374\374\374\376\374\374\374\376\374\374" - "\374\376\377\377\377\377\363\363\363\371\367\367\367\373\372\372\372\375" - "\371\371\371\374\377\377\377\377\373\373\373\376\375\375\375\376\365\365" - "\365\372\374\374\374\376\365\365\365\372\365\365\365\373\366\366\366\373" - "\366\366\366\373\367\367\367\373\370\370\370\374\373\373\373\376\366\366" - "\366\373\361\361\361\371\356\356\356\367\364\364\364\372\363\363\363\371" - "\371\371\371\375\370\370\370\374\365\365\365\373\372\372\372\375\375\375" - "\375\376\370\370\370\374\375\375\375\377\373\373\373\375\371\371\371\375" - "\363\363\363\372\370\370\370\374\365\365\365\373\362\362\362\371\363\363" - "\363\371\371\371\371\374\366\366\366\373\377\377\377\377\374\374\374\376" - "\370\370\370\374\371\371\371\374\373\373\373\376\370\370\370\374\377\377" - "\377\377\374\374\374\376\371\371\371\374\366\366\366\373\370\370\370\374" - "\362\362\362\371\364\364\364\372\364\364\364\372\364\364\364\372\364\364" - "\364\372\365\365\365\372\367\367\367\373\373\373\373\376\374\374\374\376" - "\371\371\371\375\370\370\370\374\366\366\366\373\371\371\371\375\364\364" - "\364\372\371\371\371\375\373\373\373\375\371\371\371\375\376\376\376\377" - "\365\365\365\373\371\371\371\375\373\373\373\375\372\372\372\375\371\371" - "\371\374\362\362\362\371\362\362\362\371\365\365\365\373\362\362\362\371" - "\363\363\363\371\366\366\366\373\370\370\370\374\372\372\372\375\367\367" - "\367\373\372\372\372\375\371\371\371\374\371\371\371\374\364\364\364\372" - "\371\371\371\374\370\370\370\374\370\370\370\374\366\366\366\373\361\361" - "\361\371\364\364\364\372\365\365\365\372\365\365\365\373\374\374\374\376" - "\375\375\375\376\377\377\377\377\370\370\370\374\371\371\371\375\367\367" - "\367\373\370\370\370\374\371\371\371\375\366\366\366\373\371\371\371\374" - "\364\364\364\372\365\365\365\372\370\370\370\374\364\364\364\372\371\371" - "\371\374\362\362\362\371\375\375\375\376\366\366\366\373\366\366\366\373" - "\366\366\366\373\364\364\364\372\367\367\367\373\364\364\364\372\363\363" - "\363\371\365\365\365\373\365\365\365\373\371\371\371\374\370\370\370\374" - "\370\370\370\374\375\375\375\376\373\373\373\375\366\366\366\373\377\377" - "\377\377\377\377\377\377\375\375\375\376\377\377\377\377\377\377\377\377" - "\367\367\367\373\375\375\375\376\372\372\372\375\372\372\372\375\376\376" - "\376\377\367\367\367\373\372\372\372\375\363\363\363\372\357\357\357\370" - "\362\362\362\371\361\361\361\371\366\366\366\373\366\366\366\373\365\365" - "\365\373\365\365\365\373\366\366\366\373\362\362\362\371\365\365\365\373" - "\364\364\364\372\366\366\366\373\366\366\366\373\370\370\370\374\371\371" - "\371\375\377\377\377\377\377\377\377\377\377\377\377\377\370\370\370\374" - "\372\372\372\375\371\371\371\374\363\363\363\371\366\366\366\373\364\364" - "\364\372\361\361\361\371\365\365\365\373\367\367\367\373\377\377\377\377" - "\374\374\374\376\374\374\374\376\375\375\375\377\372\372\372\375\373\373" - "\373\375\373\373\373\376\375\375\375\376\364\364\364\372\366\366\366\373" - "\363\363\363\372\366\366\366\373\364\364\364\372\357\357\357\370\374\374" - "\374\376\371\371\371\374\366\366\366\373\376\376\376\377\366\366\366\373" - "\373\373\373\375\371\371\371\375\372\372\372\375\366\366\366\373\370\370" - "\370\374\362\362\362\371\364\364\364\372\373\373\373\376\373\373\373\375" - "\371\371\371\374\377\377\377\377\366\366\366\373\375\375\375\376\367\367" - "\367\373\370\370\370\374\371\371\371\375\363\363\363\372\362\362\362\371" - "\362\362\362\371\356\356\356\367\363\363\363\372\373\373\373\376\373\373" - "\373\375\370\370\370\374\370\370\370\374\372\372\372\375\366\366\366\373" - "\364\364\364\372\364\364\364\372\371\371\371\374\366\366\366\373\366\366" - "\366\373\362\362\362\371\362\362\362\371\370\370\370\374\361\361\361\371" - "\366\366\366\373\371\371\371\375\373\373\373\376\371\371\371\375\370\370" - "\370\374\371\371\371\375\371\371\371\375\366\366\366\373\367\367\367\373" - "\365\365\365\373\367\367\367\373\364\364\364\372\356\356\356\367\366\366" - "\366\373\366\366\366\373\367\367\367\373\362\362\362\371\371\371\371\375" - "\373\373\373\375\366\366\366\373\365\365\365\372\357\357\357\370\366\366" - "\366\373\361\361\361\371\360\360\360\370\370\370\370\374\364\364\364\372" - "\363\363\363\372\372\372\372\375\361\361\361\371\374\374\374\376\375\375" - "\375\376\371\371\371\375\374\374\374\376\372\372\372\375\374\374\374\376" - "\374\374\374\376\373\373\373\375\371\371\371\374\367\367\367\373\370\370" - "\370\374\374\374\374\376\373\373\373\376\366\366\366\373\371\371\371\375" - "\366\366\366\373\365\365\365\373\363\363\363\371\366\366\366\373\365\365" - "\365\372\365\365\365\373\360\360\360\370\362\362\362\371\365\365\365\372" - "\365\365\365\373\362\362\362\371\367\367\367\373\371\371\371\375\367\367" - "\367\373\375\375\375\376\373\373\373\375\377\377\377\377\375\375\375\377" - "\375\375\375\376\367\367\367\373\373\373\373\375\367\367\367\373\362\362" - "\362\371\366\366\366\373\363\363\363\372\363\363\363\372\362\362\362\371" - "\376\376\376\377\373\373\373\376\374\374\374\376\375\375\375\376\370\370" - "\370\374\377\377\377\377\374\374\374\376\377\377\377\377\373\373\373\376" - "\365\365\365\373\364\364\364\372\364\364\364\372\363\363\363\372\362\362" - "\362\371\362\362\362\371\377\377\377\377\377\377\377\377\367\367\367\373" - "\374\374\374\376\372\372\372\375\367\367\367\373\364\364\364\372\365\365" - "\365\372\364\364\364\372\366\366\366\373\365\365\365\372\365\365\365\373" - "\371\371\371\375\373\373\373\375\373\373\373\376\374\374\374\376\372\372" - "\372\375\370\370\370\374\373\373\373\375\354\354\354\366\355\355\355\367" - "\371\371\371\374\365\365\365\372\356\356\356\367\357\357\357\370\357\357" - "\357\370\373\373\373\375\371\371\371\375\371\371\371\375\366\366\366\373" - "\366\366\366\373\367\367\367\373\364\364\364\372\364\364\364\372\366\366" - "\366\373\364\364\364\372\370\370\370\374\361\361\361\371\356\356\356\367" - "\371\371\371\374\362\362\362\371\367\367\367\373\375\375\375\377\373\373" - "\373\375\371\371\371\375\374\374\374\376\374\374\374\376\371\371\371\374" - "\366\366\366\373\370\370\370\374\363\363\363\372\366\366\366\373\363\363" - "\363\372\360\360\360\370\365\365\365\372\374\374\374\376\364\364\364\372" - "\370\370\370\374\371\371\371\374\365\365\365\372\370\370\370\374\363\363" - "\363\371\365\365\365\373\367\367\367\373\362\362\362\371\362\362\362\371" - "\370\370\370\374\366\366\366\373\371\371\371\375\367\367\367\373\371\371" - "\371\374\375\375\375\376\377\377\377\377\377\377\377\377\371\371\371\375" - "\374\374\374\376\374\374\374\376\366\366\366\373\366\366\366\373\364\364" - "\364\372\366\366\366\373\372\372\372\375\374\374\374\376\366\366\366\373" - "\375\375\375\377\362\362\362\371\366\366\366\373\364\364\364\372\366\366" - "\366\373\370\370\370\374\366\366\366\373\362\362\362\371\360\360\360\370" - "\364\364\364\372\366\366\366\373\364\364\364\372\363\363\363\372\371\371" - "\371\374\377\377\377\377\371\371\371\374\375\375\375\377\371\371\371\375" - "\373\373\373\376\371\371\371\375\370\370\370\374\377\377\377\377\370\370" - "\370\374\371\371\371\374\363\363\363\372\366\366\366\373\363\363\363\371" - "\363\363\363\371\371\371\371\374\371\371\371\374\372\372\372\375\370\370" - "\370\374\375\375\375\376\375\375\375\377\376\376\376\377\370\370\370\374" - "\370\370\370\374\371\371\371\374\363\363\363\372\357\357\357\370\364\364" - "\364\372\362\362\362\371\365\365\365\372\372\372\372\375\370\370\370\374" - "\374\374\374\376\367\367\367\373\370\370\370\374\372\372\372\375\371\371" - "\371\374\365\365\365\373\365\365\365\372\361\361\361\371\370\370\370\374" - "\364\364\364\372\365\365\365\373\367\367\367\373\370\370\370\374\376\376" - "\376\377\374\374\374\376\377\377\377\377\373\373\373\376\364\364\364\372" - "\364\364\364\372\363\363\363\372\355\355\355\366\365\365\365\372\365\365" - "\365\373\355\355\355\366\362\362\362\371\366\366\366\373\370\370\370\374" - "\362\362\362\371\373\373\373\375\371\371\371\374\370\370\370\374\363\363" - "\363\372\360\360\360\370\363\363\363\372\371\371\371\374\367\367\367\373" - "\362\362\362\371\363\363\363\372\371\371\371\374\370\370\370\374\373\373" - "\373\376\375\375\375\377\374\374\374\376\373\373\373\375\374\374\374\376" - "\374\374\374\376\371\371\371\374\370\370\370\374\370\370\370\374\367\367" - "\367\373\375\375\375\376\367\367\367\373\370\370\370\374\370\370\370\374" - "\373\373\373\376\370\370\370\374\367\367\367\373\370\370\370\374\364\364" - "\364\372\365\365\365\373\367\367\367\373\371\371\371\374\366\366\366\373" - "\362\362\362\371\367\367\367\373\365\365\365\372\365\365\365\372\366\366" - "\366\373\373\373\373\376\371\371\371\374\375\375\375\377\377\377\377\377" - "\377\377\377\377\371\371\371\375\373\373\373\375\376\376\376\377\364\364" - "\364\372\365\365\365\373\372\372\372\375\370\370\370\374\366\366\366\373" - "\373\373\373\375\370\370\370\374\366\366\366\373\356\356\356\367\367\367" - "\367\373\373\373\373\375\371\371\371\374\367\367\367\373\365\365\365\373" - "\362\362\362\371\363\363\363\372\364\364\364\372\364\364\364\372\365\365" - "\365\373\367\367\367\373\366\366\366\373\374\374\374\376\367\367\367\373" - "\373\373\373\375\372\372\372\375\376\376\376\377\371\371\371\375\372\372" - "\372\375\377\377\377\377\370\370\370\374\365\365\365\373\364\364\364\372" - "\372\372\372\375\367\367\367\373\366\366\366\373\371\371\371\374\371\371" - "\371\374\371\371\371\375\377\377\377\377\373\373\373\375\377\377\377\377" - "\370\370\370\374\377\377\377\377\371\371\371\375\370\370\370\374\362\362" - "\362\371\365\365\365\373\363\363\363\371\362\362\362\371\365\365\365\372" - "\373\373\373\375\372\372\372\375\372\372\372\375\367\367\367\373\371\371" - "\371\375\372\372\372\375\373\373\373\375\377\377\377\377\377\377\377\377" - "\373\373\373\376\370\370\370\374\363\363\363\371\367\367\367\373\367\367" - "\367\373\371\371\371\375\376\376\376\377\374\374\374\376\373\373\373\376" - "\372\372\372\375\372\372\372\375\370\370\370\374\363\363\363\371\365\365" - "\365\372\362\362\362\371\364\364\364\372\362\362\362\371\373\373\373\375" - "\373\373\373\376\372\372\372\375\366\366\366\373\370\370\370\374\370\370" - "\370\374\367\367\367\373\364\364\364\372\364\364\364\372\363\363\363\371" - "\367\367\367\373\363\363\363\372\357\357\357\370\367\367\367\373\366\366" - "\366\373\373\373\373\375\373\373\373\376\373\373\373\375\375\375\375\376" - "\372\372\372\375\371\371\371\375\370\370\370\374\370\370\370\374\371\371" - "\371\374\365\365\365\372\366\366\366\373\372\372\372\375\363\363\363\372" - "\362\362\362\371\371\371\371\374\370\370\370\374\364\364\364\372\366\366" - "\366\373\372\372\372\375\361\361\361\371\372\372\372\375\370\370\370\374" - "\366\366\366\373\371\371\371\374\364\364\364\372\365\365\365\372\362\362" - "\362\371\366\366\366\373\375\375\375\376\373\373\373\376\373\373\373\375" - "\375\375\375\377\377\377\377\377\373\373\373\375\373\373\373\376\375\375" - "\375\377\364\364\364\372\362\362\362\371\364\364\364\372\365\365\365\372" - "\365\365\365\372\370\370\370\374\371\371\371\374\371\371\371\375\370\370" - "\370\374\365\365\365\373\370\370\370\374\366\366\366\373\365\365\365\372" - "\364\364\364\372\366\366\366\373\364\364\364\372\363\363\363\372\363\363" - "\363\372\363\363\363\372\366\366\366\373\370\370\370\374\370\370\370\374" - "\371\371\371\375\371\371\371\374\370\370\370\374\373\373\373\376\373\373" - "\373\375\373\373\373\376\372\372\372\375\372\372\372\375\375\375\375\377" - "\367\367\367\373\365\365\365\373\372\372\372\375\374\374\374\376\367\367" - "\367\373\374\374\374\376\376\376\376\377\374\374\374\376\374\374\374\376" - "\371\371\371\374\377\377\377\377\370\370\370\374\367\367\367\373\365\365" - "\365\373\364\364\364\372\367\367\367\373\367\367\367\373\363\363\363\372" - "\362\362\362\371\371\371\371\375\376\376\376\377\366\366\366\373\375\375" - "\375\377\375\375\375\376\375\375\375\377\373\373\373\375\371\371\371\374" - "\366\366\366\373\370\370\370\374\373\373\373\375\377\377\377\377\370\370" - "\370\374\365\365\365\372\370\370\370\374\370\370\370\374\374\374\374\376" - "\375\375\375\376\372\372\372\375\370\370\370\374\371\371\371\375\362\362" - "\362\371\355\355\355\366\363\363\363\372\352\352\352\365\362\362\362\371" - "\367\367\367\373\372\372\372\375\371\371\371\374\371\371\371\374\370\370" - "\370\374\370\370\370\374\370\370\370\374\370\370\370\374\365\365\365\372" - "\364\364\364\372\370\370\370\374\363\363\363\371\364\364\364\372\356\356" - "\356\367\371\371\371\374\366\366\366\373\365\365\365\372\371\371\371\374" - "\371\371\371\374\376\376\376\377\370\370\370\374\366\366\366\373\371\371" - "\371\374\365\365\365\372\366\366\366\373\366\366\366\373\361\361\361\371" - "\360\360\360\370\363\363\363\371\361\361\361\371\365\365\365\373\370\370" - "\370\374\367\367\367\373\371\371\371\374\366\366\366\373\365\365\365\372" - "\371\371\371\374\370\370\370\374\366\366\366\373\362\362\362\371\356\356" - "\356\367\363\363\363\372\363\363\363\372\362\362\362\371\373\373\373\375" - "\370\370\370\374\374\374\374\376\375\375\375\376\376\376\376\377\377\377" - "\377\377\366\366\366\373\372\372\372\375\365\365\365\373\354\354\354\366" - "\361\361\361\371\362\362\362\371\367\367\367\373\365\365\365\372\366\366" - "\366\373\364\364\364\372\371\371\371\374\372\372\372\375\372\372\372\375" - "\356\356\356\367\370\370\370\374\375\375\375\376\357\357\357\370\357\357" - "\357\370\362\362\362\371\362\362\362\371\366\366\366\373\371\371\371\375" - "\373\373\373\376\366\366\366\373\371\371\371\374\371\371\371\374\371\371" - "\371\375\370\370\370\374\374\374\374\376\364\364\364\372\371\371\371\375" - "\374\374\374\376\375\375\375\377\374\374\374\376\363\363\363\371\367\367" - "\367\373\365\365\365\372\371\371\371\375\373\373\373\375\374\374\374\376" - "\374\374\374\376\364\364\364\372\377\377\377\377\377\377\377\377\371\371" - "\371\374\370\370\370\374\365\365\365\373\362\362\362\371\362\362\362\371" - "\352\352\352\365\366\366\366\373\363\363\363\372\371\371\371\374\374\374" - "\374\376\373\373\373\375\377\377\377\377\374\374\374\376\377\377\377\377" - "\366\366\366\373\371\371\371\374\371\371\371\374\370\370\370\374\370\370" - "\370\374\366\366\366\373\365\365\365\373\375\375\375\377\364\364\364\372" - "\370\370\370\374\377\377\377\377\372\372\372\375\374\374\374\376\367\367" - "\367\373\371\371\371\375\370\370\370\374\371\371\371\374\362\362\362\371" - "\351\351\351\365\357\357\357\370\370\370\370\374\373\373\373\376\361\361" - "\361\371\366\366\366\373\373\373\373\375\365\365\365\372\372\372\372\375" - "\370\370\370\374\366\366\366\373\362\362\362\371\365\365\365\373\370\370" - "\370\374\362\362\362\371\357\357\357\370\362\362\362\371\366\366\366\373" - "\363\363\363\371\366\366\366\373\366\366\366\373\367\367\367\373\371\371" - "\371\374\363\363\363\372\366\366\366\373\366\366\366\373\363\363\363\371" - "\360\360\360\370\362\362\362\371\364\364\364\372\356\356\356\367\361\361" - "\361\371\367\367\367\373\367\367\367\373\371\371\371\375\370\370\370\374" - "\366\366\366\373\371\371\371\375\363\363\363\371\366\366\366\373\366\366" - "\366\373\367\367\367\373\371\371\371\375\370\370\370\374\365\365\365\372" - "\370\370\370\374\366\366\366\373\374\374\374\376\374\374\374\376\372\372" - "\372\375\371\371\371\375\363\363\363\372\364\364\364\372\371\371\371\374" - "\372\372\372\375\373\373\373\376\363\363\363\371\364\364\364\372\362\362" - "\362\371\364\364\364\372\371\371\371\374\365\365\365\372\367\367\367\373" - "\365\365\365\373\366\366\366\373\364\364\364\372\356\356\356\367\361\361" - "\361\371\362\362\362\371\361\361\361\371\352\352\352\365\363\363\363\372" - "\366\366\366\373\375\375\375\377\373\373\373\376\370\370\370\374\370\370" - "\370\374\373\373\373\376\377\377\377\377\371\371\371\375\371\371\371\375" - "\371\371\371\374\371\371\371\375\366\366\366\373\366\366\366\373\370\370" - "\370\374\366\366\366\373\374\374\374\376\362\362\362\371\363\363\363\372" - "\371\371\371\375\365\365\365\373\371\371\371\374\373\373\373\375\371\371" - "\371\375\365\365\365\372\370\370\370\374\371\371\371\374\370\370\370\374" - "\357\357\357\370\374\374\374\376\363\363\363\371\370\370\370\374\374\374" - "\374\376\373\373\373\376\370\370\370\374\375\375\375\377\374\374\374\376" - "\373\373\373\376\376\376\376\377\370\370\370\374\371\371\371\375\367\367" - "\367\373\366\366\366\373\370\370\370\374\367\367\367\373\370\370\370\374" - "\370\370\370\374\372\372\372\375\373\373\373\375\374\374\374\376\374\374" - "\374\376\363\363\363\372\362\362\362\371\366\366\366\373\365\365\365\372" - "\363\363\363\371\362\362\362\371\363\363\363\371\363\363\363\371\363\363" - "\363\371\364\364\364\372\365\365\365\373\370\370\370\374\370\370\370\374" - "\370\370\370\374\371\371\371\374\371\371\371\374\367\367\367\373\364\364" - "\364\372\372\372\372\375\366\366\366\373\363\363\363\371\362\362\362\371" - "\362\362\362\371\366\366\366\373\370\370\370\374\367\367\367\373\365\365" - "\365\373\372\372\372\375\364\364\364\372\366\366\366\373\364\364\364\372" - "\371\371\371\375\364\364\364\372\362\362\362\371\362\362\362\371\362\362" - "\362\371\360\360\360\370\363\363\363\371\364\364\364\372\371\371\371\375" - "\370\370\370\374\370\370\370\374\365\365\365\372\373\373\373\376\367\367" - "\367\373\370\370\370\374\365\365\365\373\372\372\372\375\366\366\366\373" - "\364\364\364\372\371\371\371\374\370\370\370\374\372\372\372\375\373\373" - "\373\375\377\377\377\377\377\377\377\377\377\377\377\377\370\370\370\374" - "\371\371\371\374\366\366\366\373\371\371\371\374\363\363\363\372\357\357" - "\357\370\362\362\362\371\360\360\360\370\363\363\363\372\366\366\366\373" - "\365\365\365\373\365\365\365\372\366\366\366\373\371\371\371\375\365\365" - "\365\372\364\364\364\372\364\364\364\372\362\362\362\371\364\364\364\372" - "\357\357\357\370\370\370\370\374\373\373\373\376\373\373\373\376\366\366" - "\366\373\371\371\371\374\364\364\364\372\370\370\370\374\376\376\376\377" - "\373\373\373\375\370\370\370\374\372\372\372\375\364\364\364\372\372\372" - "\372\375\366\366\366\373\362\362\362\371\371\371\371\375\367\367\367\373" - "\357\357\357\370\351\351\351\365\357\357\357\370\357\357\357\370\377\377" - "\377\377\371\371\371\374\371\371\371\374\370\370\370\374\367\367\367\373" - "\370\370\370\374\370\370\370\374\360\360\360\370\376\376\376\377\365\365" - "\365\373\366\366\366\373\353\353\353\366\370\370\370\374\366\366\366\373" - "\371\371\371\375\373\373\373\375\373\373\373\375\372\372\372\375\371\371" - "\371\374\361\361\361\371\364\364\364\372\361\361\361\371\365\365\365\372" - "\371\371\371\375\370\370\370\374\367\367\367\373\372\372\372\375\373\373" - "\373\376\373\373\373\376\365\365\365\372\372\372\372\375\371\371\371\375" - "\366\366\366\373\364\364\364\372\364\364\364\372\360\360\360\370\356\356" - "\356\367\362\362\362\371\366\366\366\373\365\365\365\373\364\364\364\372" - "\370\370\370\374\367\367\367\373\366\366\366\373\370\370\370\374\372\372" - "\372\375\367\367\367\373\364\364\364\372\372\372\372\375\365\365\365\373" - "\361\361\361\371\360\360\360\370\363\363\363\371\365\365\365\373\366\366" - "\366\373\366\366\366\373\366\366\366\373\371\371\371\375\371\371\371\374" - "\372\372\372\375\371\371\371\375\365\365\365\372\360\360\360\370\366\366" - "\366\373\360\360\360\370\365\365\365\372\366\366\366\373\363\363\363\371" - "\366\366\366\373\373\373\373\375\370\370\370\374\373\373\373\375\365\365" - "\365\372\367\367\367\373\371\371\371\374\366\366\366\373\365\365\365\372" - "\371\371\371\374\366\366\366\373\370\370\370\374\371\371\371\374\371\371" - "\371\375\377\377\377\377\375\375\375\376\377\377\377\377\377\377\377\377" - "\377\377\377\377\375\375\375\377\365\365\365\372\365\365\365\372\366\366" - "\366\373\364\364\364\372\363\363\363\371\364\364\364\372\355\355\355\366" - "\360\360\360\370\365\365\365\373\366\366\366\373\365\365\365\373\366\366" - "\366\373\371\371\371\374\364\364\364\372\366\366\366\373\366\366\366\373" - "\367\367\367\373\366\366\366\373\356\356\356\367\362\362\362\371\362\362" - "\362\371\370\370\370\374\366\366\366\373\370\370\370\374\366\366\366\373" - "\371\371\371\374\372\372\372\375\371\371\371\374\365\365\365\373\370\370" - "\370\374\370\370\370\374\371\371\371\374\367\367\367\373\371\371\371\374" - "\367\367\367\373\370\370\370\374\363\363\363\371\356\356\356\367\365\365" - "\365\372\364\364\364\372\373\373\373\375\372\372\372\375\363\363\363\372" - "\363\363\363\372\366\366\366\373\371\371\371\374\373\373\373\376\357\357" - "\357\370\374\374\374\376\362\362\362\371\356\356\356\367\351\351\351\365" - "\362\362\362\371\370\370\370\374\371\371\371\375\370\370\370\374\374\374" - "\374\376\375\375\375\377\371\371\371\374\370\370\370\374\365\365\365\373" - "\366\366\366\373\363\363\363\371\362\362\362\371\367\367\367\373\367\367" - "\367\373\373\373\373\375\373\373\373\375\377\377\377\377\371\371\371\374" - "\372\372\372\375\370\370\370\374\370\370\370\374\365\365\365\373\365\365" - "\365\372\364\364\364\372\355\355\355\367\360\360\360\370\364\364\364\372" - "\363\363\363\372\364\364\364\372\367\367\367\373\367\367\367\373\366\366" - "\366\373\371\371\371\374\373\373\373\376\371\371\371\374\365\365\365\373" - "\365\365\365\372\366\366\366\373\357\357\357\370\356\356\356\367\357\357" - "\357\370\370\370\370\374\365\365\365\372\364\364\364\372\364\364\364\372" - "\365\365\365\373\366\366\366\373\367\367\367\373\371\371\371\374\364\364" - "\364\372\365\365\365\372\357\357\357\370\357\357\357\370\360\360\360\370" - "\365\365\365\372\361\361\361\371\364\364\364\372\370\370\370\374\364\364" - "\364\372\371\371\371\374\371\371\371\374\364\364\364\372\370\370\370\374" - "\370\370\370\374\357\357\357\370\362\362\362\371\374\374\374\376\371\371" - "\371\375\367\367\367\373\374\374\374\376\377\377\377\377\376\376\376\377" - "\374\374\374\376\371\371\371\374\371\371\371\374\366\366\366\373\363\363" - "\363\372\364\364\364\372\357\357\357\370\360\360\360\370\356\356\356\367" - "\362\362\362\371\357\357\357\370\361\361\361\371\366\366\366\373\366\366" - "\366\373\367\367\367\373\365\365\365\372\371\371\371\375\365\365\365\373" - "\371\371\371\374\370\370\370\374\356\356\356\367\357\357\357\370\355\355" - "\355\366\355\355\355\367\357\357\357\370\363\363\363\371\367\367\367\373" - "\371\371\371\374\370\370\370\374\370\370\370\374\371\371\371\375\373\373" - "\373\376\366\366\366\373\370\370\370\374\366\366\366\373\370\370\370\374" - "\367\367\367\373\370\370\370\374\370\370\370\374\364\364\364\372\364\364" - "\364\372\371\371\371\375\371\371\371\375\373\373\373\376\374\374\374\376" - "\373\373\373\376\374\374\374\376\365\365\365\373\370\370\370\374\367\367" - "\367\373\371\371\371\375\355\355\355\367\363\363\363\371\371\371\371\375" - "\371\371\371\374\371\371\371\374\365\365\365\373\365\365\365\372\370\370" - "\370\374\371\371\371\374\374\374\374\376\370\370\370\374\363\363\363\372" - "\370\370\370\374\371\371\371\375\366\366\366\373\364\364\364\372\367\367" - "\367\373\373\373\373\375\364\364\364\372\376\376\376\377\373\373\373\376" - "\370\370\370\374\374\374\374\376\366\366\366\373\366\366\366\373\373\373" - "\373\375\367\367\367\373\363\363\363\372\373\373\373\376\363\363\363\372" - "\352\352\352\365\357\357\357\370\357\357\357\370\371\371\371\374\366\366" - "\366\373\373\373\373\375\366\366\366\373\370\370\370\374\377\377\377\377" - "\373\373\373\375\370\370\370\374\365\365\365\373\363\363\363\372\366\366" - "\366\373\355\355\355\366\362\362\362\371\363\363\363\371\370\370\370\374" - "\363\363\363\371\364\364\364\372\364\364\364\372\371\371\371\375\371\371" - "\371\374\370\370\370\374\371\371\371\374\367\367\367\373\360\360\360\370" - "\351\351\351\365\351\351\351\365\363\363\363\372\364\364\364\372\363\363" - "\363\372\366\366\366\373\371\371\371\374\365\365\365\373\371\371\371\375" - "\364\364\364\372\367\367\367\373\370\370\370\374\367\367\367\373\370\370" - "\370\374\370\370\370\374\373\373\373\375\370\370\370\374\372\372\372\375" - "\377\377\377\377\371\371\371\374\375\375\375\376\371\371\371\374\371\371" - "\371\374\367\367\367\373\367\367\367\373\365\365\365\373\367\367\367\373" - "\365\365\365\373\364\364\364\372\356\356\356\367\365\365\365\372\356\356" - "\356\367\365\365\365\373\363\363\363\372\366\366\366\373\366\366\366\373" - "\375\375\375\377\371\371\371\374\371\371\371\375\371\371\371\375\357\357" - "\357\370\361\361\361\371\364\364\364\372\362\362\362\371\365\365\365\372" - "\364\364\364\372\364\364\364\372\365\365\365\373\364\364\364\372\371\371" - "\371\375\366\366\366\373\366\366\366\373\375\375\375\377\365\365\365\373" - "\370\370\370\374\372\372\372\375\364\364\364\372\370\370\370\374\360\360" - "\360\370\366\366\366\373\362\362\362\371\367\367\367\373\363\363\363\372" - "\371\371\371\374\366\366\366\373\372\372\372\375\366\366\366\373\367\367" - "\367\373\374\374\374\376\374\374\374\376\364\364\364\372\363\363\363\371" - "\346\346\346\363\363\363\363\371\365\365\365\373\355\355\355\366\357\357" - "\357\370\370\370\370\374\371\371\371\374\365\365\365\372\371\371\371\375" - "\375\375\375\377\374\374\374\376\370\370\370\374\364\364\364\372\370\370" - "\370\374\366\366\366\373\371\371\371\374\360\360\360\370\365\365\365\372" - "\362\362\362\371\370\370\370\374\365\365\365\372\370\370\370\374\377\377" - "\377\377\371\371\371\375\364\364\364\372\370\370\370\374\371\371\371\374" - "\362\362\362\371\366\366\366\373\365\365\365\373\354\354\354\366\351\351" - "\351\365\355\355\355\367\365\365\365\373\365\365\365\372\364\364\364\372" - "\373\373\373\375\372\372\372\375\374\374\374\376\366\366\366\373\367\367" - "\367\373\366\366\366\373\366\366\366\373\361\361\361\371\362\362\362\371" - "\361\361\361\371\370\370\370\374\364\364\364\372\371\371\371\375\370\370" - "\370\374\371\371\371\374\374\374\374\376\373\373\373\375\370\370\370\374" - "\371\371\371\374\366\366\366\373\356\356\356\367\356\356\356\367\357\357" - "\357\370\365\365\365\372\365\365\365\372\367\367\367\373\373\373\373\375" - "\371\371\371\375\373\373\373\375\373\373\373\376\374\374\374\376\375\375" - "\375\376\374\374\374\376\371\371\371\374\371\371\371\374\370\370\370\374" - "\371\371\371\374\375\375\375\376\375\375\375\376\375\375\375\376\374\374" - "\374\376\373\373\373\375\367\367\367\373\364\364\364\372\371\371\371\375" - "\366\366\366\373\371\371\371\374\367\367\367\373\364\364\364\372\365\365" - "\365\373\371\371\371\374\366\366\366\373\367\367\367\373\371\371\371\375" - "\373\373\373\376\372\372\372\375\371\371\371\374\372\372\372\375\370\370" - "\370\374\363\363\363\371\364\364\364\372\356\356\356\367\363\363\363\372" - "\365\365\365\373\363\363\363\371\363\363\363\372\366\366\366\373\365\365" - "\365\372\364\364\364\372\367\367\367\373\374\374\374\376\373\373\373\375" - "\375\375\375\377\374\374\374\376\370\370\370\374\373\373\373\376\366\366" - "\366\373\370\370\370\374\357\357\357\370\360\360\360\370\370\370\370\374" - "\362\362\362\371\371\371\371\374\371\371\371\375\366\366\366\373\374\374" - "\374\376\370\370\370\374\374\374\374\376\377\377\377\377\371\371\371\375" - "\360\360\360\370\365\365\365\373\351\351\351\365\362\362\362\371\370\370" - "\370\374\357\357\357\370\367\367\367\373\371\371\371\374\373\373\373\376" - "\374\374\374\376\367\367\367\373\375\375\375\376\373\373\373\376\371\371" - "\371\374\365\365\365\373\364\364\364\372\374\374\374\376\366\366\366\373" - "\363\363\363\371\370\370\370\374\371\371\371\374\370\370\370\374\371\371" - "\371\375\371\371\371\375\376\376\376\377\375\375\375\376\375\375\375\376" - "\371\371\371\374\370\370\370\374\365\365\365\373\364\364\364\372\363\363" - "\363\372\362\362\362\371\360\360\360\370\370\370\370\374\372\372\372\375" - "\374\374\374\376\371\371\371\375\376\376\376\377\370\370\370\374\373\373" - "\373\375\367\367\367\373\366\366\366\373\364\364\364\372\361\361\361\371" - "\363\363\363\372\365\365\365\372\362\362\362\371\366\366\366\373\366\366" - "\366\373\364\364\364\372\370\370\370\374\373\373\373\376\371\371\371\375" - "\371\371\371\374\362\362\362\371\365\365\365\372\364\364\364\372\364\364" - "\364\372\360\360\360\370\362\362\362\371\361\361\361\371\372\372\372\375" - "\367\367\367\373\373\373\373\376\373\373\373\375\375\375\375\376\377\377" - "\377\377\374\374\374\376\373\373\373\375\374\374\374\376\366\366\366\373" - "\371\371\371\374\375\375\375\376\372\372\372\375\377\377\377\377\366\366" - "\366\373\370\370\370\374\370\370\370\374\374\374\374\376\366\366\366\373" - "\364\364\364\372\365\365\365\373\366\366\366\373\366\366\366\373\365\365" - "\365\373\371\371\371\374\365\365\365\372\370\370\370\374\367\367\367\373" - "\373\373\373\375\371\371\371\375\372\372\372\375\373\373\373\376\374\374" - "\374\376\371\371\371\374\367\367\367\373\366\366\366\373\365\365\365\372" - "\357\357\357\370\363\363\363\371\363\363\363\372\362\362\362\371\365\365" - "\365\372\367\367\367\373\365\365\365\373\371\371\371\375\364\364\364\372" - "\374\374\374\376\371\371\371\375\371\371\371\375\376\376\376\377\371\371" - "\371\374\374\374\374\376\370\370\370\374\371\371\371\374\365\365\365\372" - "\367\367\367\373\361\361\361\371\370\370\370\374\371\371\371\374\372\372" - "\372\375\371\371\371\375\365\365\365\372\373\373\373\375\373\373\373\376" - "\374\374\374\376\370\370\370\374\371\371\371\375\372\372\372\375\354\354" - "\354\366\363\363\363\371\367\367\367\373\364\364\364\372\362\362\362\371" - "\373\373\373\375\374\374\374\376\365\365\365\373\372\372\372\375\377\377" - "\377\377\377\377\377\377\372\372\372\375\372\372\372\375\370\370\370\374" - "\376\376\376\377\366\366\366\373\373\373\373\375\371\371\371\374\375\375" - "\375\377\375\375\375\377\367\367\367\373\367\367\367\373\367\367\367\373" - "\374\374\374\376\372\372\372\375\366\366\366\373\365\365\365\373\365\365" - "\365\372\366\366\366\373\366\366\366\373\357\357\357\370\361\361\361\371" - "\370\370\370\374\370\370\370\374\373\373\373\376\366\366\366\373\366\366" - "\366\373\374\374\374\376\377\377\377\377\362\362\362\371\365\365\365\373" - "\370\370\370\374\365\365\365\372\362\362\362\371\362\362\362\371\361\361" - "\361\371\364\364\364\372\367\367\367\373\366\366\366\373\371\371\371\374" - "\370\370\370\374\371\371\371\375\372\372\372\375\365\365\365\372\361\361" - "\361\371\360\360\360\370\364\364\364\372\365\365\365\373\362\362\362\371" - "\356\356\356\367\357\357\357\370\363\363\363\372\367\367\367\373\364\364" - "\364\372\370\370\370\374\375\375\375\377\373\373\373\375\372\372\372\375" - "\364\364\364\372\371\371\371\374\363\363\363\372\371\371\371\374\365\365" - "\365\373\372\372\372\375\375\375\375\377\373\373\373\376\365\365\365\372" - "\370\370\370\374\364\364\364\372\367\367\367\373\364\364\364\372\372\372" - "\372\375\364\364\364\372\365\365\365\372\363\363\363\371\363\363\363\372" - "\364\364\364\372\366\366\366\373\377\377\377\377\374\374\374\376\373\373" - "\373\376\372\372\372\375\365\365\365\373\363\363\363\372\367\367\367\373" - "\365\365\365\372\365\365\365\373\353\353\353\366\362\362\362\371\365\365" - "\365\372\365\365\365\373\362\362\362\371\367\367\367\373\366\366\366\373" - "\371\371\371\374\365\365\365\373\375\375\375\376\375\375\375\377\373\373" - "\373\376\372\372\372\375\373\373\373\376\375\375\375\376\372\372\372\375" - "\366\366\366\373\370\370\370\374\363\363\363\372\356\356\356\367\365\365" - "\365\372\373\373\373\376\373\373\373\375\373\373\373\375\366\366\366\373" - "\373\373\373\375\370\370\370\374\367\367\367\373\377\377\377\377\365\365" - "\365\372\366\366\366\373\360\360\360\370\370\370\370\374\360\360\360\370" - "\364\364\364\372\355\355\355\366\375\375\375\376\374\374\374\376\372\372" - "\372\375\375\375\375\376\374\374\374\376\377\377\377\377\373\373\373\375" - "\366\366\366\373\373\373\373\375\366\366\366\373\371\371\371\375\364\364" - "\364\372\364\364\364\372\365\365\365\373\365\365\365\373\370\370\370\374" - "\367\367\367\373\372\372\372\375\370\370\370\374\370\370\370\374\371\371" - "\371\374\363\363\363\372\366\366\366\373\357\357\357\370\363\363\363\371" - "\370\370\370\374\365\365\365\373\360\360\360\370\366\366\366\373\373\373" - "\373\376\364\364\364\372\373\373\373\375\374\374\374\376\370\370\370\374" - "\371\371\371\374\361\361\361\371\363\363\363\371\362\362\362\371\366\366" - "\366\373\362\362\362\371\365\365\365\373\365\365\365\373\367\367\367\373" - "\370\370\370\374\367\367\367\373\367\367\367\373\372\372\372\375\367\367" - "\367\373\370\370\370\374\363\363\363\372\370\370\370\374\371\371\371\374" - "\364\364\364\372\366\366\366\373\366\366\366\373\363\363\363\371\364\364" - "\364\372\372\372\372\375\373\373\373\376\376\376\376\377\374\374\374\376" - "\375\375\375\376\375\375\375\377\374\374\374\376\370\370\370\374\372\372" - "\372\375\370\370\370\374\365\365\365\373\367\367\367\373\371\371\371\375" - "\374\374\374\376\373\373\373\376\377\377\377\377\370\370\370\374\365\365" - "\365\373\371\371\371\374\365\365\365\373\372\372\372\375\365\365\365\372" - "\366\366\366\373\365\365\365\373\367\367\367\373\371\371\371\375\366\366" - "\366\373\374\374\374\376\371\371\371\374\367\367\367\373\365\365\365\373" - "\367\367\367\373\365\365\365\372\371\371\371\375\363\363\363\372\362\362" - "\362\371\363\363\363\371\360\360\360\370\370\370\370\374\361\361\361\371" - "\371\371\371\375\367\367\367\373\367\367\367\373\373\373\373\375\372\372" - "\372\375\375\375\375\377\375\375\375\376\370\370\370\374\370\370\370\374" - "\372\372\372\375\365\365\365\373\364\364\364\372\370\370\370\374\362\362" - "\362\371\366\366\366\373\361\361\361\371\366\366\366\373\370\370\370\374" - "\375\375\375\376\371\371\371\374\373\373\373\376\375\375\375\376\376\376" - "\376\377\371\371\371\374\366\366\366\373\363\363\363\371\357\357\357\370" - "\362\362\362\371\355\355\355\366\351\351\351\365\365\365\365\372\372\372" - "\372\375\364\364\364\372\370\370\370\374\377\377\377\377\375\375\375\377" - "\374\374\374\376\372\372\372\375\373\373\373\376\364\364\364\372\370\370" - "\370\374\360\360\360\370\365\365\365\373\370\370\370\374\367\367\367\373" - "\365\365\365\373\372\372\372\375\366\366\366\373\370\370\370\374\366\366" - "\366\373\374\374\374\376\373\373\373\376\366\366\366\373\364\364\364\372" - "\353\353\353\366\355\355\355\367\370\370\370\374\363\363\363\371\365\365" - "\365\373\370\370\370\374\361\361\361\371\373\373\373\376\375\375\375\376" - "\372\372\372\375\371\371\371\374\363\363\363\371\365\365\365\373\362\362" - "\362\371\366\366\366\373\364\364\364\372\363\363\363\371\372\372\372\375" - "\370\370\370\374\365\365\365\372\367\367\367\373\367\367\367\373\371\371" - "\371\374\371\371\371\375\370\370\370\374\367\367\367\373\366\366\366\373" - "\367\367\367\373\365\365\365\372\362\362\362\371\371\371\371\374\370\370" - "\370\374\370\370\370\374\367\367\367\373\371\371\371\375\373\373\373\375" - "\377\377\377\377\375\375\375\376\375\375\375\377\374\374\374\376\370\370" - "\370\374\370\370\370\374\370\370\370\374\370\370\370\374\362\362\362\371" - "\371\371\371\374\374\374\374\376\373\373\373\375\373\373\373\375\363\363" - "\363\372\370\370\370\374\363\363\363\372\370\370\370\374\371\371\371\374" - "\371\371\371\375\370\370\370\374\361\361\361\371\366\366\366\373\366\366" - "\366\373\372\372\372\375\373\373\373\376\365\365\365\372\371\371\371\375" - "\371\371\371\374\371\371\371\375\370\370\370\374\367\367\367\373\370\370" - "\370\374\367\367\367\373\362\362\362\371\363\363\363\372\357\357\357\370" - "\362\362\362\371\365\365\365\372\370\370\370\374\364\364\364\372\365\365" - "\365\372\373\373\373\376\372\372\372\375\373\373\373\376\374\374\374\376" - "\366\366\366\373\373\373\373\376\367\367\367\373\370\370\370\374\366\366" - "\366\373\366\366\366\373\361\361\361\371\365\365\365\373\363\363\363\372" - "\364\364\364\372\372\372\372\375\374\374\374\376\373\373\373\376\370\370" - "\370\374\370\370\370\374\371\371\371\374\370\370\370\374\370\370\370\374" - "\365\365\365\373\362\362\362\371\356\356\356\367\357\357\357\370\352\352" - "\352\365\370\370\370\374\371\371\371\374\370\370\370\374\377\377\377\377" - "\374\374\374\376\374\374\374\376\373\373\373\376\372\372\372\375\360\360" - "\360\370\370\370\370\374\362\362\362\371\373\373\373\375\360\360\360\370" - "\365\365\365\372\371\371\371\374\367\367\367\373\367\367\367\373\364\364" - "\364\372\366\366\366\373\365\365\365\372\377\377\377\377\371\371\371\375" - "\366\366\366\373\362\362\362\371\371\371\371\375\362\362\362\371\363\363" - "\363\371\363\363\363\371\363\363\363\372\370\370\370\374\375\375\375\377" - "\373\373\373\376\377\377\377\377\372\372\372\375\371\371\371\375\363\363" - "\363\371\372\372\372\375\363\363\363\371\365\365\365\373\363\363\363\371" - "\360\360\360\370\371\371\371\374\367\367\367\373\363\363\363\372\366\366" - "\366\373\367\367\367\373\370\370\370\374\373\373\373\375\367\367\367\373" - "\365\365\365\373\364\364\364\372\364\364\364\372\366\366\366\373\373\373" - "\373\375\365\365\365\372\367\367\367\373\371\371\371\374\373\373\373\375" - "\374\374\374\376\372\372\372\375\376\376\376\377\374\374\374\376\374\374" - "\374\376\371\371\371\374\371\371\371\374\371\371\371\375\371\371\371\374" - "\367\367\367\373\363\363\363\372\366\366\366\373\370\370\370\374\374\374" - "\374\376\365\365\365\373\366\366\366\373\371\371\371\375\366\366\366\373" - "\367\367\367\373\371\371\371\374\365\365\365\372\371\371\371\375\371\371" - "\371\374\365\365\365\372\374\374\374\376\365\365\365\373\370\370\370\374" - "\365\365\365\372\370\370\370\374\364\364\364\372\373\373\373\376\364\364" - "\364\372\364\364\364\372\366\366\366\373\370\370\370\374\361\361\361\371" - "\362\362\362\371\360\360\360\370\361\361\361\371\370\370\370\374\370\370" - "\370\374\364\364\364\372\372\372\372\375\371\371\371\375\375\375\375\377" - "\374\374\374\376\373\373\373\376\366\366\366\373\371\371\371\375\372\372" - "\372\375\370\370\370\374\364\364\364\372\366\366\366\373\362\362\362\371" - "\361\361\361\371\364\364\364\372\364\364\364\372\366\366\366\373\371\371" - "\371\375\377\377\377\377\370\370\370\374\377\377\377\377\367\367\367\373" - "\374\374\374\376\370\370\370\374\371\371\371\374\357\357\357\370\350\350" - "\350\364\352\352\352\365\356\356\356\367\364\364\364\372\371\371\371\375" - "\367\367\367\373\373\373\373\375\374\374\374\376\374\374\374\376\375\375" - "\375\376\372\372\372\375\371\371\371\374\370\370\370\374\367\367\367\373" - "\355\355\355\366\364\364\364\372\365\365\365\373\366\366\366\373\370\370" - "\370\374\362\362\362\371\375\375\375\377\375\375\375\377\364\364\364\372" - "\373\373\373\376\371\371\371\374\367\367\367\373\357\357\357\370\362\362" - "\362\371\364\364\364\372\360\360\360\370\364\364\364\372\355\355\355\366" - "\363\363\363\372\374\374\374\376\371\371\371\375\376\376\376\377\370\370" - "\370\374\365\365\365\372\363\363\363\372\371\371\371\375\365\365\365\372" - "\362\362\362\371\355\355\355\366\356\356\356\367\367\367\367\373\365\365" - "\365\373\370\370\370\374\371\371\371\374\367\367\367\373\367\367\367\373" - "\373\373\373\376\371\371\371\374\370\370\370\374\366\366\366\373\363\363" - "\363\372\364\364\364\372\373\373\373\375\372\372\372\375\363\363\363\371" - "\373\373\373\376\371\371\371\375\370\370\370\374\374\374\374\376\373\373" - "\373\376\372\372\372\375\371\371\371\374\365\365\365\373\364\364\364\372" - "\371\371\371\375\366\366\366\373\366\366\366\373\360\360\360\370\371\371" - "\371\375\371\371\371\374\371\371\371\375\370\370\370\374\366\366\366\373" - "\364\364\364\372\365\365\365\372\364\364\364\372\364\364\364\372\364\364" - "\364\372\370\370\370\374\367\367\367\373\374\374\374\376\371\371\371\375" - "\374\374\374\376\370\370\370\374\370\370\370\374\370\370\370\374\371\371" - "\371\374\366\366\366\373\370\370\370\374\374\374\374\376\366\366\366\373" - "\364\364\364\372\362\362\362\371\353\353\353\366\354\354\354\366\362\362" - "\362\371\366\366\366\373\367\367\367\373\365\365\365\372\367\367\367\373" - "\373\373\373\375\374\374\374\376\375\375\375\377\375\375\375\376\371\371" - "\371\375\371\371\371\374\370\370\370\374\371\371\371\374\363\363\363\372" - "\371\371\371\374\364\364\364\372\365\365\365\373\370\370\370\374\376\376" - "\376\377\377\377\377\377\374\374\374\376\373\373\373\375\373\373\373\376" - "\374\374\374\376\375\375\375\377\367\367\367\373\363\363\363\371\367\367" - "\367\373\360\360\360\370\363\363\363\371\355\355\355\366\351\351\351\365" - "\365\365\365\372\375\375\375\376\361\361\361\371\373\373\373\376\376\376" - "\376\377\373\373\373\375\377\377\377\377\371\371\371\375\364\364\364\372" - "\363\363\363\372\360\360\360\370\356\356\356\367\366\366\366\373\365\365" - "\365\373\366\366\366\373\371\371\371\374\364\364\364\372\375\375\375\376" - "\373\373\373\375\363\363\363\371\373\373\373\376\373\373\373\376\366\366" - "\366\373\367\367\367\373\366\366\366\373\367\367\367\373\357\357\357\370" - "\363\363\363\372\371\371\371\374\367\367\367\373\371\371\371\374\372\372" - "\372\375\370\370\370\374\366\366\366\373\371\371\371\374\362\362\362\371" - "\366\366\366\373\366\366\366\373\364\364\364\372\356\356\356\367\364\364" - "\364\372\361\361\361\371\365\365\365\373\367\367\367\373\370\370\370\374" - "\364\364\364\372\370\370\370\374\372\372\372\375\373\373\373\375\367\367" - "\367\373\364\364\364\372\365\365\365\372\370\370\370\374\363\363\363\372" - "\375\375\375\377\373\373\373\375\374\374\374\376\375\375\375\376\375\375" - "\375\376\373\373\373\375\377\377\377\377\374\374\374\376\370\370\370\374" - "\367\367\367\373\370\370\370\374\367\367\367\373\370\370\370\374\360\360" - "\360\370\365\365\365\373\371\371\371\375\370\370\370\374\371\371\371\375" - "\364\364\364\372\371\371\371\375\367\367\367\373\370\370\370\374\366\366" - "\366\373\373\373\373\376\374\374\374\376\366\366\366\373\373\373\373\375" - "\367\367\367\373\370\370\370\374\374\374\374\376\370\370\370\374\376\376" - "\376\377\374\374\374\376\373\373\373\375\374\374\374\376\372\372\372\375" - "\374\374\374\376\364\364\364\372\366\366\366\373\362\362\362\371\356\356" - "\356\367\357\357\357\370\357\357\357\370\364\364\364\372\373\373\373\376" - "\362\362\362\371\373\373\373\375\375\375\375\376\371\371\371\374\376\376" - "\376\377\371\371\371\375\374\374\374\376\375\375\375\376\366\366\366\373" - "\365\365\365\373\365\365\365\373\366\366\366\373\370\370\370\374\362\362" - "\362\371\364\364\364\372\362\362\362\371\376\376\376\377\372\372\372\375" - "\366\366\366\373\373\373\373\375\370\370\370\374\372\372\372\375\371\371" - "\371\375\364\364\364\372\371\371\371\374\362\362\362\371\366\366\366\373" - "\362\362\362\371\363\363\363\371\363\363\363\371\371\371\371\374\374\374" - "\374\376\375\375\375\376\366\366\366\373\366\366\366\373\365\365\365\373" - "\366\366\366\373\371\371\371\375\365\365\365\373\365\365\365\373\355\355" - "\355\367\370\370\370\374\371\371\371\374\371\371\371\375\366\366\366\373" - "\371\371\371\375\371\371\371\375\367\367\367\373\367\367\367\373\366\366" - "\366\373\362\362\362\371\366\366\366\373\372\372\372\375\370\370\370\374" - "\366\366\366\373\371\371\371\374\364\364\364\372\365\365\365\373\370\370" - "\370\374\367\367\367\373\377\377\377\377\365\365\365\372\373\373\373\376" - "\366\366\366\373\370\370\370\374\366\366\366\373\374\374\374\376\363\363" - "\363\371\354\354\354\366\365\365\365\372\362\362\362\371\366\366\366\373" - "\363\363\363\371\367\367\367\373\371\371\371\375\373\373\373\375\366\366" - "\366\373\373\373\373\375\370\370\370\374\366\366\366\373\365\365\365\372" - "\364\364\364\372\363\363\363\372\373\373\373\376\374\374\374\376\365\365" - "\365\373\374\374\374\376\377\377\377\377\370\370\370\374\371\371\371\374" - "\375\375\375\376\372\372\372\375\370\370\370\374\371\371\371\375\364\364" - "\364\372\362\362\362\371\356\356\356\367\364\364\364\372\366\366\366\373" - "\364\364\364\372\371\371\371\374\366\366\366\373\364\364\364\372\366\366" - "\366\373\365\365\365\372\367\367\367\373\371\371\371\374\366\366\366\373" - "\364\364\364\372\366\366\366\373\362\362\362\371\363\363\363\372\373\373" - "\373\376\371\371\371\375\371\371\371\374\371\371\371\375\374\374\374\376" - "\364\364\364\372\365\365\365\372\366\366\366\373\364\364\364\372\365\365" - "\365\372\363\363\363\372\363\363\363\372\363\363\363\372\365\365\365\373" - "\365\365\365\373\370\370\370\374\371\371\371\374\371\371\371\375\370\370" - "\370\374\370\370\370\374\367\367\367\373\373\373\373\375\375\375\375\376" - "\374\374\374\376\367\367\367\373\373\373\373\375\367\367\367\373\366\366" - "\366\373\366\366\366\373\366\366\366\373\367\367\367\373\366\366\366\373" - "\377\377\377\377\373\373\373\376\377\377\377\377\377\377\377\377\375\375" - "\375\376\373\373\373\375\371\371\371\374\367\367\367\373\363\363\363\371" - "\371\371\371\374\364\364\364\372\357\357\357\370\365\365\365\372\357\357" - "\357\370\374\374\374\376\373\373\373\375\377\377\377\377\372\372\372\375" - "\373\373\373\375\366\366\366\373\373\373\373\375\365\365\365\372\367\367" - "\367\373\366\366\366\373\362\362\362\371\357\357\357\370\364\364\364\372" - "\357\357\357\370\364\364\364\372\373\373\373\375\370\370\370\374\371\371" - "\371\375\364\364\364\372\364\364\364\372\364\364\364\372\371\371\371\374" - "\367\367\367\373\370\370\370\374\361\361\361\371\366\366\366\373\363\363" - "\363\371\372\372\372\375\366\366\366\373\364\364\364\372\373\373\373\375" - "\370\370\370\374\363\363\363\372\370\370\370\374\366\366\366\373\362\362" - "\362\371\366\366\366\373\356\356\356\367\357\357\357\370\364\364\364\372" - "\362\362\362\371\364\364\364\372\366\366\366\373\371\371\371\374\371\371" - "\371\375\366\366\366\373\366\366\366\373\366\366\366\373\365\365\365\373" - "\364\364\364\372\366\366\366\373\357\357\357\370\360\360\360\370\367\367" - "\367\373\366\366\366\373\373\373\373\375\373\373\373\375\373\373\373\376" - "\374\374\374\376\367\367\367\373\371\371\371\374\370\370\370\374\367\367" - "\367\373\367\367\367\373\365\365\365\372\366\366\366\373\364\364\364\372" - "\357\357\357\370\363\363\363\372\370\370\370\374\373\373\373\376\370\370" - "\370\374\365\365\365\373\364\364\364\372\370\370\370\374\375\375\375\376" - "\366\366\366\373\363\363\363\371\365\365\365\372\364\364\364\372\365\365" - "\365\373\367\367\367\373\371\371\371\375\373\373\373\375\372\372\372\375" - "\375\375\375\376\373\373\373\375\366\366\366\373\364\364\364\372\364\364" - "\364\372\364\364\364\372\364\364\364\372\367\367\367\373\362\362\362\371" - "\360\360\360\370\366\366\366\373\366\366\366\373\373\373\373\376\371\371" - "\371\374\373\373\373\376\370\370\370\374\373\373\373\375\376\376\376\377" - "\374\374\374\376\373\373\373\375\373\373\373\376\371\371\371\374\357\357" - "\357\370\365\365\365\373\367\367\367\373\364\364\364\372\366\366\366\373" - "\370\370\370\374\371\371\371\374\371\371\371\374\374\374\374\376\374\374" - "\374\376\373\373\373\375\375\375\375\377\374\374\374\376\371\371\371\375" - "\373\373\373\375\365\365\365\372\366\366\366\373\373\373\373\376\364\364" - "\364\372\363\363\363\372\365\365\365\373\366\366\366\373\370\370\370\374" - "\376\376\376\377\370\370\370\374\364\364\364\372\365\365\365\372\365\365" - "\365\372\365\365\365\373\366\366\366\373\364\364\364\372\354\354\354\366" - "\350\350\350\364\351\351\351\365\367\367\367\373\365\365\365\373\367\367" - "\367\373\364\364\364\372\370\370\370\374\371\371\371\375\365\365\365\373" - "\362\362\362\371\365\365\365\372\364\364\364\372\372\372\372\375\366\366" - "\366\373\356\356\356\367\357\357\357\370\371\371\371\374\366\366\366\373" - "\370\370\370\374\374\374\374\376\370\370\370\374\374\374\374\376\364\364" - "\364\372\370\370\370\374\365\365\365\373\367\367\367\373\363\363\363\372" - "\354\354\354\366\367\367\367\373\360\360\360\370\363\363\363\371\367\367" - "\367\373\373\373\373\375\371\371\371\375\365\365\365\372\371\371\371\374" - "\371\371\371\375\367\367\367\373\365\365\365\373\370\370\370\374\360\360" - "\360\370\362\362\362\371\366\366\366\373\370\370\370\374\373\373\373\375" - "\377\377\377\377\377\377\377\377\370\370\370\374\365\365\365\373\370\370" - "\370\374\365\365\365\372\367\367\367\373\363\363\363\372\365\365\365\373" - "\371\371\371\375\362\362\362\371\353\353\353\366\357\357\357\370\365\365" - "\365\372\375\375\375\377\372\372\372\375\374\374\374\376\370\370\370\374" - "\376\376\376\377\377\377\377\377\364\364\364\372\363\363\363\371\364\364" - "\364\372\370\370\370\374\363\363\363\372\363\363\363\371\370\370\370\374" - "\371\371\371\375\373\373\373\376\374\374\374\376\375\375\375\377\374\374" - "\374\376\371\371\371\375\367\367\367\373\373\373\373\375\364\364\364\372" - "\366\366\366\373\371\371\371\374\362\362\362\371\366\366\366\373\367\367" - "\367\373\366\366\366\373\366\366\366\373\370\370\370\374\370\370\370\374" - "\366\366\366\373\375\375\375\376\370\370\370\374\373\373\373\375\371\371" - "\371\375\367\367\367\373\370\370\370\374\366\366\366\373\365\365\365\372" - "\370\370\370\374\374\374\374\376\374\374\374\376\373\373\373\376\374\374" - "\374\376\373\373\373\376\374\374\374\376\373\373\373\375\377\377\377\377" - "\372\372\372\375\365\365\365\372\373\373\373\375\363\363\363\372\363\363" - "\363\371\370\370\370\374\363\363\363\372\361\361\361\371\364\364\364\372" - "\370\370\370\374\375\375\375\376\377\377\377\377\373\373\373\376\363\363" - "\363\372\371\371\371\374\370\370\370\374\362\362\362\371\361\361\361\371" - "\363\363\363\371\354\354\354\366\355\355\355\366\360\360\360\370\357\357" - "\357\370\364\364\364\372\371\371\371\375\365\365\365\373\365\365\365\373" - "\371\371\371\374\367\367\367\373\370\370\370\374\371\371\371\374\367\367" - "\367\373\370\370\370\374\366\366\366\373\365\365\365\373\363\363\363\371" - "\366\366\366\373\366\366\366\373\371\371\371\375\371\371\371\374\370\370" - "\370\374\376\376\376\377\362\362\362\371\366\366\366\373\365\365\365\372" - "\367\367\367\373\370\370\370\374\357\357\357\370\363\363\363\371\365\365" - "\365\372\362\362\362\371\365\365\365\373\365\365\365\372\374\374\374\376" - "\370\370\370\374\370\370\370\374\371\371\371\375\370\370\370\374\367\367" - "\367\373\370\370\370\374\374\374\374\376\366\366\366\373\366\366\366\373" - "\377\377\377\377\370\370\370\374\373\373\373\375\376\376\376\377\371\371" - "\371\375\376\376\376\377\367\367\367\373\365\365\365\373\366\366\366\373" - "\365\365\365\372\363\363\363\372\357\357\357\370\362\362\362\371\361\361" - "\361\371\365\365\365\372\371\371\371\374\371\371\371\375\375\375\375\377" - "\370\370\370\374\371\371\371\374\376\376\376\377\372\372\372\375\374\374" - "\374\376\366\366\366\373\371\371\371\374\366\366\366\373\357\357\357\370" - "\372\372\372\375\366\366\366\373\372\372\372\375\377\377\377\377\375\375" - "\375\377\366\366\366\373\374\374\374\376\374\374\374\376\372\372\372\375" - "\367\367\367\373\365\365\365\372\364\364\364\372\366\366\366\373\364\364" - "\364\372\365\365\365\373\365\365\365\373\365\365\365\372\373\373\373\376" - "\366\366\366\373\372\372\372\375\370\370\370\374\373\373\373\376\374\374" - "\374\376\373\373\373\375\374\374\374\376\374\374\374\376\367\367\367\373" - "\366\366\366\373\362\362\362\371\364\364\364\372\365\365\365\373\367\367" - "\367\373\374\374\374\376\376\376\376\377\365\365\365\373\371\371\371\374" - "\370\370\370\374\371\371\371\375\374\374\374\376\364\364\364\372\363\363" - "\363\371\370\370\370\374\362\362\362\371\364\364\364\372\357\357\357\370" - "\356\356\356\367\364\364\364\372\366\366\366\373\371\371\371\374\374\374" - "\374\376\372\372\372\375\366\366\366\373\365\365\365\373\355\355\355\366" - "\366\366\366\373\361\361\361\371\364\364\364\372\357\357\357\370\355\355" - "\355\366\360\360\360\370\366\366\366\373\366\366\366\373\371\371\371\374" - "\372\372\372\375\375\375\375\377\376\376\376\377\366\366\366\373\364\364" - "\364\372\363\363\363\371\371\371\371\374\366\366\366\373\366\366\366\373" - "\363\363\363\372\363\363\363\372\370\370\370\374\363\363\363\371\374\374" - "\374\376\365\365\365\373\367\367\367\373\370\370\370\374\366\366\366\373" - "\365\365\365\373\371\371\371\375\371\371\371\374\366\366\366\373\363\363" - "\363\372\351\351\351\365\364\364\364\372\366\366\366\373\361\361\361\371" - "\364\364\364\372\373\373\373\376\370\370\370\374\371\371\371\374\367\367" - "\367\373\363\363\363\372\363\363\363\372\365\365\365\373\373\373\373\375" - "\365\365\365\373\365\365\365\372\377\377\377\377\375\375\375\376\373\373" - "\373\376\374\374\374\376\373\373\373\375\371\371\371\375\366\366\366\373" - "\366\366\366\373\363\363\363\371\366\366\366\373\353\353\353\366\352\352" - "\352\365\350\350\350\364\362\362\362\371\365\365\365\373\364\364\364\372" - "\372\372\372\375\372\372\372\375\371\371\371\374\371\371\371\374\371\371" - "\371\375\370\370\370\374\366\366\366\373\366\366\366\373\372\372\372\375" - "\370\370\370\374\360\360\360\370\370\370\370\374\370\370\370\374\372\372" - "\372\375\376\376\376\377\377\377\377\377\370\370\370\374\373\373\373\375" - "\375\375\375\377\371\371\371\375\370\370\370\374\365\365\365\372\366\366" - "\366\373\364\364\364\372\361\361\361\371\365\365\365\373\364\364\364\372" - "\365\365\365\372\370\370\370\374\365\365\365\373\366\366\366\373\371\371" - "\371\374\371\371\371\374\366\366\366\373\371\371\371\374\373\373\373\375" - "\373\373\373\375\371\371\371\375\372\372\372\375\366\366\366\373\366\366" - "\366\373\364\364\364\372\367\367\367\373\371\371\371\375\374\374\374\376" - "\366\366\366\373\371\371\371\374\366\366\366\373\365\365\365\372\372\372" - "\372\375\371\371\371\375\365\365\365\373\375\375\375\376\371\371\371\374" - "\367\367\367\373\366\366\366\373\363\363\363\371\364\364\364\372\364\364" - "\364\372\370\370\370\374\373\373\373\375\374\374\374\376\373\373\373\375" - "\373\373\373\375\362\362\362\371\365\365\365\373\366\366\366\373\364\364" - "\364\372\361\361\361\371\355\355\355\366\363\363\363\372\361\361\361\371" - "\371\371\371\374\370\370\370\374\374\374\374\376\371\371\371\375\372\372" - "\372\375\365\365\365\372\372\372\372\375\364\364\364\372\370\370\370\374" - "\366\366\366\373\366\366\366\373\361\361\361\371\362\362\362\371\361\361" - "\361\371\373\373\373\375\375\375\375\376\372\372\372\375\370\370\370\374" - "\365\365\365\373\371\371\371\375\366\366\366\373\366\366\366\373\367\367" - "\367\373\365\365\365\373\361\361\361\371\364\364\364\372\357\357\357\370" - "\361\361\361\371\364\364\364\372\366\366\366\373\371\371\371\375\367\367" - "\367\373\370\370\370\374\365\365\365\372\371\371\371\375\371\371\371\374" - "\364\364\364\372\366\366\366\373\364\364\364\372\365\365\365\372\372\372" - "\372\375\373\373\373\376\371\371\371\375\371\371\371\375\371\371\371\375" - "\373\373\373\376\371\371\371\374\371\371\371\375\365\365\365\373\365\365" - "\365\373\350\350\350\364\361\361\361\371\344\344\344\362\365\365\365\372" - "\364\364\364\372\371\371\371\375\370\370\370\374\375\375\375\377\370\370" - "\370\374\371\371\371\374\374\374\374\376\362\362\362\371\363\363\363\372" - "\364\364\364\372\371\371\371\374\365\365\365\372\363\363\363\372\364\364" - "\364\372\365\365\365\373\365\365\365\372\373\373\373\375\372\372\372\375" - "\373\373\373\375\373\373\373\375\372\372\372\375\366\366\366\373\363\363" - "\363\371\366\366\366\373\370\370\370\374\366\366\366\373\364\364\364\372" - "\367\367\367\373\365\365\365\373\363\363\363\372\370\370\370\374\371\371" - "\371\374\373\373\373\375\370\370\370\374\370\370\370\374\372\372\372\375" - "\372\372\372\375\373\373\373\376\375\375\375\377\374\374\374\376\372\372" - "\372\375\370\370\370\374\367\367\367\373\366\366\366\373\370\370\370\374" - "\365\365\365\372\371\371\371\375\370\370\370\374\372\372\372\375\364\364" - "\364\372\363\363\363\372\371\371\371\375\371\371\371\374\375\375\375\376" - "\363\363\363\372\375\375\375\376\371\371\371\375\365\365\365\372\362\362" - "\362\371\356\356\356\367\365\365\365\373\364\364\364\372\374\374\374\376" - "\365\365\365\372\365\365\365\373\374\374\374\376\366\366\366\373\367\367" - "\367\373\371\371\371\374\364\364\364\372\355\355\355\367\360\360\360\370" - "\362\362\362\371\365\365\365\373\364\364\364\372\366\366\366\373\364\364" - "\364\372\364\364\364\372\371\371\371\375\364\364\364\372\371\371\371\375" - "\365\365\365\372\366\366\366\373\357\357\357\370\362\362\362\371\355\355" - "\355\366\365\365\365\372\357\357\357\370\375\375\375\376\377\377\377\377" - "\374\374\374\376\363\363\363\371\361\361\361\371\365\365\365\372\366\366" - "\366\373\365\365\365\373\366\366\366\373\366\366\366\373\364\364\364\372" - "\366\366\366\373\363\363\363\372\366\366\366\373\362\362\362\371\371\371" - "\371\375\370\370\370\374\373\373\373\375\363\363\363\372\370\370\370\374" - "\370\370\370\374\366\366\366\373\365\365\365\373\367\367\367\373\363\363" - "\363\371\364\364\364\372\367\367\367\373\373\373\373\375\375\375\375\376" - "\370\370\370\374\370\370\370\374\374\374\374\376\370\370\370\374\371\371" - "\371\374\370\370\370\374\366\366\366\373\363\363\363\371\355\355\355\366" - "\363\363\363\371\355\355\355\367\365\365\365\373\370\370\370\374\376\376" - "\376\377\370\370\370\374\373\373\373\376\373\373\373\376\365\365\365\373" - "\373\373\373\375\370\370\370\374\356\356\356\367\362\362\362\371\364\364" - "\364\372\365\365\365\373\362\362\362\371\367\367\367\373\363\363\363\371" - "\370\370\370\374\377\377\377\377\375\375\375\377\373\373\373\375\372\372" - "\372\375\371\371\371\375\366\366\366\373\364\364\364\372\373\373\373\375" - "\366\366\366\373\364\364\364\372\366\366\366\373\362\362\362\371\360\360" - "\360\370\364\364\364\372\367\367\367\373\366\366\366\373\363\363\363\371" - "\371\371\371\375\374\374\374\376\365\365\365\373\371\371\371\374\370\370" - "\370\374\372\372\372\375\370\370\370\374\372\372\372\375\366\366\366\373" - "\357\357\357\370\366\366\366\373\363\363\363\372\373\373\373\376\372\372" - "\372\375\371\371\371\375\367\367\367\373\363\363\363\372\363\363\363\372" - "\370\370\370\374\374\374\374\376\364\364\364\372\373\373\373\375\365\365" - "\365\373\363\363\363\372\350\350\350\364\357\357\357\370\371\371\371\374" - "\364\364\364\372\367\367\367\373\367\367\367\373\375\375\375\377\374\374" - "\374\376\373\373\373\376\374\374\374\376\367\367\367\373\365\365\365\372" - "\353\353\353\366\365\365\365\372\356\356\356\367\367\367\367\373\364\364" - "\364\372\373\373\373\375\366\366\366\373\371\371\371\375\371\371\371\374" - "\371\371\371\374\366\366\366\373\367\367\367\373\366\366\366\373\352\352" - "\352\365\361\361\361\371\362\362\362\371\363\363\363\371\363\363\363\371" - "\370\370\370\374\374\374\374\376\371\371\371\375\374\374\374\376\370\370" - "\370\374\371\371\371\375\366\366\366\373\362\362\362\371\365\365\365\372" - "\364\364\364\372\370\370\370\374\366\366\366\373\366\366\366\373\364\364" - "\364\372\371\371\371\374\374\374\374\376\365\365\365\373\370\370\370\374" - "\372\372\372\375\370\370\370\374\370\370\370\374\365\365\365\373\366\366" - "\366\373\370\370\370\374\364\364\364\372\374\374\374\376\361\361\361\371" - "\371\371\371\374\374\374\374\376\371\371\371\375\372\372\372\375\376\376" - "\376\377\370\370\370\374\367\367\367\373\362\362\362\371\370\370\370\374" - "\365\365\365\373\360\360\360\370\364\364\364\372\362\362\362\371\366\366" - "\366\373\371\371\371\375\370\370\370\374\363\363\363\372\373\373\373\375" - "\371\371\371\374\373\373\373\375\373\373\373\376\374\374\374\376\367\367" - "\367\373\372\372\372\375\362\362\362\371\366\366\366\373\364\364\364\372" - "\370\370\370\374\365\365\365\373\373\373\373\375\375\375\375\377\377\377" - "\377\377\367\367\367\373\372\372\372\375\366\366\366\373\370\370\370\374" - "\364\364\364\372\366\366\366\373\366\366\366\373\362\362\362\371\357\357" - "\357\370\364\364\364\372\365\365\365\372\361\361\361\371\365\365\365\372" - "\366\366\366\373\373\373\373\376\373\373\373\375\372\372\372\375\366\366" - "\366\373\373\373\373\375\370\370\370\374\371\371\371\375\370\370\370\374" - "\370\370\370\374\370\370\370\374\363\363\363\372\365\365\365\373\363\363" - "\363\371\371\371\371\374\372\372\372\375\365\365\365\373\366\366\366\373" - "\365\365\365\373\367\367\367\373\367\367\367\373\370\370\370\374\370\370" - "\370\374\352\352\352\365\367\367\367\373\363\363\363\371\366\366\366\373" - "\365\365\365\372\362\362\362\371\372\372\372\375\371\371\371\375\371\371" - "\371\375\370\370\370\374\377\377\377\377\366\366\366\373\373\373\373\375" - "\370\370\370\374\365\365\365\373\365\365\365\373\357\357\357\370\346\346" - "\346\363\366\366\366\373\363\363\363\372\362\362\362\371\366\366\366\373" - "\367\367\367\373\366\366\366\373\367\367\367\373\365\365\365\372\373\373" - "\373\375\366\366\366\373\355\355\355\367\357\357\357\370\362\362\362\371" - "\350\350\350\364\366\366\366\373\367\367\367\373\372\372\372\375\373\373" - "\373\376\372\372\372\375\367\367\367\373\371\371\371\375\370\370\370\374" - "\361\361\361\371\362\362\362\371\362\362\362\371\362\362\362\371\367\367" - "\367\373\364\364\364\372\362\362\362\371\371\371\371\375\367\367\367\373" - "\367\367\367\373\366\366\366\373\370\370\370\374\374\374\374\376\371\371" - "\371\374\371\371\371\375\370\370\370\374\371\371\371\374\365\365\365\373" - "\370\370\370\374\364\364\364\372\376\376\376\377\366\366\366\373\370\370" - "\370\374\375\375\375\376\372\372\372\375\370\370\370\374\367\367\367\373" - "\365\365\365\372\366\366\366\373\366\366\366\373\366\366\366\373\360\360" - "\360\370\362\362\362\371\367\367\367\373\373\373\373\375\370\370\370\374" - "\375\375\375\376\373\373\373\376\375\375\375\377\375\375\375\376\375\375" - "\375\376\365\365\365\373\372\372\372\375\373\373\373\376\363\363\363\372" - "\365\365\365\373\363\363\363\372\372\372\372\375\372\372\372\375\373\373" - "\373\376\370\370\370\374\375\375\375\376\371\371\371\375\365\365\365\373" - "\371\371\371\375\365\365\365\373\367\367\367\373\357\357\357\370\365\365" - "\365\372\364\364\364\372\371\371\371\375\365\365\365\373\365\365\365\373" - "\366\366\366\373\366\366\366\373\370\370\370\374\371\371\371\375\373\373" - "\373\376\371\371\371\374\374\374\374\376\371\371\371\374\365\365\365\373" - "\364\364\364\372\364\364\364\372\362\362\362\371\371\371\371\375\363\363" - "\363\371\365\365\365\373\363\363\363\372\365\365\365\372\372\372\372\375" - "\364\364\364\372\372\372\372\375\371\371\371\374\371\371\371\374\365\365" - "\365\373\366\366\366\373\365\365\365\373\357\357\357\370\362\362\362\371" - "\365\365\365\372\366\366\366\373\366\366\366\373\366\366\366\373\371\371" - "\371\375\370\370\370\374\371\371\371\374\371\371\371\375\373\373\373\375" - "\363\363\363\372\370\370\370\374\371\371\371\374\366\366\366\373\365\365" - "\365\372\362\362\362\371\362\362\362\371\362\362\362\371\364\364\364\372" - "\374\374\374\376\374\374\374\376\377\377\377\377\375\375\375\376\371\371" - "\371\374\365\365\365\372\367\367\367\373\365\365\365\372\361\361\361\371" - "\362\362\362\371\362\362\362\371\361\361\361\371\366\366\366\373\370\370" - "\370\374\372\372\372\375\371\371\371\375\375\375\375\376\372\372\372\375" - "\371\371\371\375\361\361\361\371\362\362\362\371\364\364\364\372\367\367" - "\367\373\357\357\357\370\370\370\370\374\364\364\364\372\364\364\364\372" - "\371\371\371\374\370\370\370\374\370\370\370\374\366\366\366\373\371\371" - "\371\375\373\373\373\376\371\371\371\375\371\371\371\374\366\366\366\373" - "\367\367\367\373\366\366\366\373\364\364\364\372\366\366\366\373\372\372" - "\372\375\372\372\372\375\371\371\371\374\371\371\371\374\371\371\371\374" - "\366\366\366\373\370\370\370\374\366\366\366\373\365\365\365\373\364\364" - "\364\372\373\373\373\375\363\363\363\372\365\365\365\373\366\366\366\373" - "\370\370\370\374\374\374\374\376\373\373\373\376\376\376\376\377\377\377" - "\377\377\377\377\377\377\371\371\371\374\370\370\370\374\375\375\375\377" - "\372\372\372\375\366\366\366\373\363\363\363\372\371\371\371\375\365\365" - "\365\373\373\373\373\375\373\373\373\376\370\370\370\374\373\373\373\375" - "\364\364\364\372\364\364\364\372\366\366\366\373\366\366\366\373\366\366" - "\366\373\364\364\364\372\364\364\364\372\364\364\364\372\373\373\373\375" - "\366\366\366\373\366\366\366\373\370\370\370\374\370\370\370\374\370\370" - "\370\374\371\371\371\374\371\371\371\375\365\365\365\373\370\370\370\374" - "\365\365\365\372\372\372\372\375\364\364\364\372\364\364\364\372\370\370" - "\370\374\357\357\357\370\364\364\364\372\363\363\363\372\363\363\363\372" - "\364\364\364\372\366\366\366\373\366\366\366\373\373\373\373\376\376\376" - "\376\377\366\366\366\373\365\365\365\372\371\371\371\375\367\367\367\373" - "\363\363\363\371\361\361\361\371\360\360\360\370\357\357\357\370\365\365" - "\365\372\370\370\370\374\370\370\370\374\367\367\367\373\366\366\366\373" - "\365\365\365\373\370\370\370\374\363\363\363\371\366\366\366\373\363\363" - "\363\371\370\370\370\374\370\370\370\374\352\352\352\365\362\362\362\371" - "\365\365\365\372\361\361\361\371\373\373\373\375\373\373\373\376\366\366" - "\366\373\373\373\373\376\366\366\366\373\365\365\365\373\365\365\365\373" - "\364\364\364\372\365\365\365\372\360\360\360\370\364\364\364\372\357\357" - "\357\370\366\366\366\373\370\370\370\374\373\373\373\375\371\371\371\375" - "\377\377\377\377\373\373\373\375\374\374\374\376\365\365\365\373\370\370" - "\370\374\371\371\371\374\370\370\370\374\374\374\374\376\364\364\364\372" - "\365\365\365\373\371\371\371\374\366\366\366\373\365\365\365\373\370\370" - "\370\374\365\365\365\373\366\366\366\373\377\377\377\377\374\374\374\376" - "\371\371\371\374\370\370\370\374\364\364\364\372\367\367\367\373\365\365" - "\365\372\371\371\371\374\365\365\365\373\372\372\372\375\365\365\365\373" - "\371\371\371\374\371\371\371\375\367\367\367\373\374\374\374\376\370\370" - "\370\374\363\363\363\372\367\367\367\373\366\366\366\373\367\367\367\373" - "\364\364\364\372\371\371\371\374\372\372\372\375\375\375\375\377\364\364" - "\364\372\370\370\370\374\371\371\371\374\373\373\373\376\374\374\374\376" - "\372\372\372\375\372\372\372\375\366\366\366\373\363\363\363\371\363\363" - "\363\372\366\366\366\373\367\367\367\373\373\373\373\375\370\370\370\374" - "\370\370\370\374\370\370\370\374\365\365\365\372\370\370\370\374\362\362" - "\362\371\365\365\365\372\365\365\365\373\367\367\367\373\365\365\365\373" - "\364\364\364\372\364\364\364\372\367\367\367\373\363\363\363\372\370\370" - "\370\374\370\370\370\374\367\367\367\373\364\364\364\372\366\366\366\373" - "\370\370\370\374\370\370\370\374\371\371\371\374\373\373\373\376\370\370" - "\370\374\373\373\373\376\370\370\370\374\374\374\374\376\363\363\363\371" - "\363\363\363\372\364\364\364\372\364\364\364\372\366\366\366\373\371\371" - "\371\374\374\374\374\376\370\370\370\374\367\367\367\373\371\371\371\375" - "\371\371\371\375\366\366\366\373\365\365\365\373\370\370\370\374\363\363" - "\363\371\357\357\357\370\364\364\364\372\370\370\370\374\372\372\372\375" - "\373\373\373\375\365\365\365\373\363\363\363\371\373\373\373\376\365\365" - "\365\373\372\372\372\375\362\362\362\371\366\366\366\373\367\367\367\373" - "\364\364\364\372\370\370\370\374\362\362\362\371\360\360\360\370\365\365" - "\365\372\366\366\366\373\365\365\365\372\366\366\366\373\363\363\363\371" - "\364\364\364\372\367\367\367\373\370\370\370\374\365\365\365\373\365\365" - "\365\372\364\364\364\372\365\365\365\373\370\370\370\374\366\366\366\373" - "\372\372\372\375\371\371\371\375\370\370\370\374\373\373\373\376\370\370" - "\370\374\365\365\365\372\366\366\366\373\362\362\362\371\367\367\367\373" - "\371\371\371\374\367\367\367\373\364\364\364\372\364\364\364\372\374\374" - "\374\376\371\371\371\374\374\374\374\376\367\367\367\373\365\365\365\372" - "\372\372\372\375\373\373\373\376\367\367\367\373\372\372\372\375\364\364" - "\364\372\362\362\362\371\366\366\366\373\367\367\367\373\373\373\373\375" - "\371\371\371\374\371\371\371\375\372\372\372\375\366\366\366\373\364\364" - "\364\372\364\364\364\372\366\366\366\373\367\367\367\373\365\365\365\373" - "\370\370\370\374\362\362\362\371\365\365\365\372\364\364\364\372\365\365" - "\365\373\370\370\370\374\372\372\372\375\371\371\371\375\373\373\373\375" - "\371\371\371\375\364\364\364\372\371\371\371\374\365\365\365\373\364\364" - "\364\372\363\363\363\371\360\360\360\370\365\365\365\372\373\373\373\375" - "\372\372\372\375\373\373\373\375\377\377\377\377\373\373\373\376\364\364" - "\364\372\364\364\364\372\371\371\371\375\370\370\370\374\366\366\366\373" - "\370\370\370\374\370\370\370\374\366\366\366\373\364\364\364\372\365\365" - "\365\372\366\366\366\373\370\370\370\374\366\366\366\373\366\366\366\373" - "\363\363\363\372\365\365\365\372\371\371\371\374\371\371\371\375\371\371" - "\371\375\367\367\367\373\365\365\365\373\373\373\373\376\366\366\366\373" - "\370\370\370\374\365\365\365\373\364\364\364\372\365\365\365\372\372\372" - "\372\375\373\373\373\376\371\371\371\375\377\377\377\377\372\372\372\375" - "\372\372\372\375\371\371\371\374\371\371\371\374\366\366\366\373\363\363" - "\363\372\360\360\360\370\360\360\360\370\357\357\357\370\362\362\362\371" - "\365\365\365\373\362\362\362\371\362\362\362\371\364\364\364\372\370\370" - "\370\374\363\363\363\371\371\371\371\374\363\363\363\372\370\370\370\374" - "\367\367\367\373\364\364\364\372\364\364\364\372\370\370\370\374\370\370" - "\370\374\373\373\373\375\371\371\371\375\367\367\367\373\371\371\371\375" - "\364\364\364\372\360\360\360\370\366\366\366\373\365\365\365\372\366\366" - "\366\373\363\363\363\372\366\366\366\373\364\364\364\372\367\367\367\373" - "\365\365\365\373\365\365\365\372\371\371\371\374\372\372\372\375\373\373" - "\373\375\366\366\366\373\365\365\365\372\371\371\371\375\367\367\367\373" - "\370\370\370\374\371\371\371\374\364\364\364\372\364\364\364\372\365\365" - "\365\372\364\364\364\372\377\377\377\377\375\375\375\377\374\374\374\376" - "\375\375\375\376\371\371\371\375\370\370\370\374\374\374\374\376\371\371" - "\371\375\372\372\372\375\366\366\366\373\366\366\366\373\372\372\372\375" - "\370\370\370\374\373\373\373\375\372\372\372\375\373\373\373\375\371\371" - "\371\375\374\374\374\376\371\371\371\374\371\371\371\375\367\367\367\373" - "\372\372\372\375\366\366\366\373\364\364\364\372\362\362\362\371\364\364" - "\364\372\363\363\363\372\370\370\370\374\373\373\373\375\377\377\377\377" - "\377\377\377\377\374\374\374\376\374\374\374\376\374\374\374\376\365\365" - "\365\372\370\370\370\374\363\363\363\372\365\365\365\372\357\357\357\370" - "\370\370\370\374\374\374\374\376\373\373\373\375\366\366\366\373\370\370" - "\370\374\366\366\366\373\366\366\366\373\366\366\366\373\372\372\372\375" - "\371\371\371\375\371\371\371\375\367\367\367\373\365\365\365\372\364\364" - "\364\372\363\363\363\372\357\357\357\370\364\364\364\372\362\362\362\371" - "\364\364\364\372\365\365\365\373\371\371\371\374\365\365\365\372\375\375" - "\375\377\370\370\370\374\372\372\372\375\367\367\367\373\363\363\363\372" - "\364\364\364\372\366\366\366\373\366\366\366\373\363\363\363\372\365\365" - "\365\373\370\370\370\374\370\370\370\374\372\372\372\375\372\372\372\375" - "\374\374\374\376\370\370\370\374\375\375\375\376\371\371\371\375\366\366" - "\366\373\371\371\371\374\366\366\366\373\356\356\356\367\366\366\366\373" - "\365\365\365\372\370\370\370\374\365\365\365\373\370\370\370\374\370\370" - "\370\374\366\366\366\373\362\362\362\371\370\370\370\374\366\366\366\373" - "\363\363\363\371\365\365\365\373\370\370\370\374\366\366\366\373\364\364" - "\364\372\363\363\363\372\362\362\362\371\365\365\365\372\373\373\373\375" - "\370\370\370\374\370\370\370\374\363\363\363\372\360\360\360\370\366\366" - "\366\373\366\366\366\373\371\371\371\374\365\365\365\373\370\370\370\374" - "\366\366\366\373\363\363\363\372\374\374\374\376\376\376\376\377\370\370" - "\370\374\372\372\372\375\373\373\373\376\367\367\367\373\372\372\372\375" - "\365\365\365\372\371\371\371\374\371\371\371\375\372\372\372\375\362\362" - "\362\371\370\370\370\374\363\363\363\372\366\366\366\373\371\371\371\374" - "\373\373\373\375\371\371\371\375\375\375\375\376\373\373\373\376\373\373" - "\373\375\373\373\373\375\372\372\372\375\372\372\372\375\364\364\364\372" - "\365\365\365\372\366\366\366\373\365\365\365\372\367\367\367\373\366\366" - "\366\373\372\372\372\375\371\371\371\374\372\372\372\375\374\374\374\376" - "\372\372\372\375\366\366\366\373\371\371\371\374\364\364\364\372\363\363" - "\363\372\355\355\355\367\365\365\365\372\363\363\363\372\364\364\364\372" - "\371\371\371\374\373\373\373\376\375\375\375\377\371\371\371\375\371\371" - "\371\375\373\373\373\376\365\365\365\373\371\371\371\375\367\367\367\373" - "\366\366\366\373\356\356\356\367\365\365\365\372\367\367\367\373\362\362" - "\362\371\363\363\363\372\366\366\366\373\363\363\363\372\370\370\370\374" - "\365\365\365\372\371\371\371\374\370\370\370\374\366\366\366\373\365\365" - "\365\373\361\361\361\371\365\365\365\372\365\365\365\373\364\364\364\372" - "\365\365\365\373\370\370\370\374\371\371\371\374\370\370\370\374\366\366" - "\366\373\372\372\372\375\374\374\374\376\375\375\375\377\374\374\374\376" - "\375\375\375\377\371\371\371\374\365\365\365\373\367\367\367\373\366\366" - "\366\373\364\364\364\372\365\365\365\373\365\365\365\372\367\367\367\373" - "\372\372\372\375\375\375\375\377\375\375\375\377\373\373\373\376\377\377" - "\377\377\374\374\374\376\370\370\370\374\373\373\373\376\367\367\367\373" - "\364\364\364\372\364\364\364\372\367\367\367\373\366\366\366\373\365\365" - "\365\372\371\371\371\375\374\374\374\376\365\365\365\373\370\370\370\374" - "\371\371\371\374\370\370\370\374\362\362\362\371\361\361\361\371\370\370" - "\370\374\366\366\366\373\374\374\374\376\371\371\371\375\371\371\371\375" - "\371\371\371\375\374\374\374\376\370\370\370\374\370\370\370\374\367\367" - "\367\373\366\366\366\373\367\367\367\373\365\365\365\372\366\366\366\373" - "\365\365\365\372\367\367\367\373\372\372\372\375\366\366\366\373\374\374" - "\374\376\375\375\375\376\366\366\366\373\365\365\365\373\366\366\366\373" - "\367\367\367\373\371\371\371\374\370\370\370\374\372\372\372\375\371\371" - "\371\374\372\372\372\375\370\370\370\374\363\363\363\371\362\362\362\371" - "\362\362\362\371\373\373\373\376\374\374\374\376\377\377\377\377\377\377" - "\377\377\375\375\375\376\376\376\376\377\370\370\370\374\373\373\373\376" - "\365\365\365\372\363\363\363\372\370\370\370\374\362\362\362\371\364\364" - "\364\372\364\364\364\372\365\365\365\372\373\373\373\375\374\374\374\376" - "\371\371\371\375\372\372\372\375\370\370\370\374\375\375\375\377\366\366" - "\366\373\366\366\366\373\367\367\367\373\363\363\363\372\371\371\371\374" - "\366\366\366\373\365\365\365\372\371\371\371\375\377\377\377\377\375\375" - "\375\377\375\375\375\376\373\373\373\375\372\372\372\375\373\373\373\375" - "\373\373\373\375\374\374\374\376\371\371\371\374\363\363\363\372\363\363" - "\363\372\365\365\365\372\375\375\375\377\373\373\373\375\372\372\372\375" - "\371\371\371\375\370\370\370\374\372\372\372\375\374\374\374\376\374\374" - "\374\376\370\370\370\374\365\365\365\373\363\363\363\372\364\364\364\372" - "\366\366\366\373\366\366\366\373\365\365\365\373\370\370\370\374\372\372" - "\372\375\371\371\371\374\367\367\367\373\370\370\370\374\373\373\373\376" - "\374\374\374\376\377\377\377\377\374\374\374\376\370\370\370\374\367\367" - "\367\373\366\366\366\373\366\366\366\373\364\364\364\372\363\363\363\371" - "\364\364\364\372\374\374\374\376\371\371\371\375\372\372\372\375\367\367" - "\367\373\377\377\377\377\377\377\377\377\373\373\373\376\371\371\371\375" - "\365\365\365\372\370\370\370\374\370\370\370\374\364\364\364\372\367\367" - "\367\373\366\366\366\373\365\365\365\372\370\370\370\374\371\371\371\374" - "\371\371\371\374\371\371\371\374\372\372\372\375\367\367\367\373\362\362" - "\362\371\355\355\355\367\364\364\364\372\366\366\366\373\363\363\363\371" - "\363\363\363\372\366\366\366\373\367\367\367\373\370\370\370\374\363\363" - "\363\372\366\366\366\373\366\366\366\373\366\366\366\373\366\366\366\373" - "\366\366\366\373\370\370\370\374\370\370\370\374\367\367\367\373\370\370" - "\370\374\366\366\366\373\363\363\363\372\371\371\371\374\374\374\374\376" - "\375\375\375\376\366\366\366\373\373\373\373\375\363\363\363\371\370\370" - "\370\374\366\366\366\373\366\366\366\373\373\373\373\376\371\371\371\375" - "\370\370\370\374\371\371\371\375\375\375\375\376\367\367\367\373\377\377" - "\377\377\377\377\377\377\377\377\377\377\373\373\373\375\377\377\377\377" - "\371\371\371\374\372\372\372\375\371\371\371\375\373\373\373\375\366\366" - "\366\373\360\360\360\370\370\370\370\374\364\364\364\372\370\370\370\374" - "\373\373\373\376\377\377\377\377\373\373\373\376\377\377\377\377\373\373" - "\373\375\370\370\370\374\371\371\371\375\366\366\366\373\366\366\366\373" - "\362\362\362\371\365\365\365\372\365\365\365\373\371\371\371\374\373\373" - "\373\375\371\371\371\375\374\374\374\376\373\373\373\375\366\366\366\373" - "\377\377\377\377\373\373\373\375\374\374\374\376\367\367\367\373\370\370" - "\370\374\373\373\373\375\373\373\373\375\373\373\373\375\371\371\371\374" - "\362\362\362\371\363\363\363\372\371\371\371\375\376\376\376\377\374\374" - "\374\376\374\374\374\376\365\365\365\372\363\363\363\372\362\362\362\371" - "\364\364\364\372\363\363\363\371\367\367\367\373\365\365\365\373\365\365" - "\365\372\366\366\366\373\371\371\371\374\373\373\373\375\371\371\371\374" - "\371\371\371\375\376\376\376\377\371\371\371\375\377\377\377\377\367\367" - "\367\373\374\374\374\376\371\371\371\374\366\366\366\373\364\364\364\372" - "\363\363\363\372\372\372\372\375\370\370\370\374\371\371\371\374\367\367" - "\367\373\375\375\375\377\373\373\373\375\374\374\374\376\373\373\373\375" - "\371\371\371\374\373\373\373\376\370\370\370\374\371\371\371\374\372\372" - "\372\375\371\371\371\374\367\367\367\373\371\371\371\375\370\370\370\374" - "\374\374\374\376\372\372\372\375\370\370\370\374\366\366\366\373\373\373" - "\373\375\373\373\373\376\370\370\370\374\366\366\366\373\364\364\364\372" - "\372\372\372\375\372\372\372\375\365\365\365\372\367\367\367\373\364\364" - "\364\372\371\371\371\374\370\370\370\374\375\375\375\376\370\370\370\374" - "\373\373\373\375\371\371\371\375\371\371\371\374\371\371\371\375\361\361" - "\361\371\371\371\371\374\366\366\366\373\375\375\375\377\366\366\366\373" - "\366\366\366\373\373\373\373\376\377\377\377\377\370\370\370\374\371\371" - "\371\375\367\367\367\373\365\365\365\373\364\364\364\372\371\371\371\374" - "\372\372\372\375\373\373\373\375\373\373\373\376\367\367\367\373\374\374" - "\374\376\375\375\375\377\377\377\377\377\377\377\377\377\375\375\375\376" - "\376\376\376\377\377\377\377\377\374\374\374\376\373\373\373\376\370\370" - "\370\374\373\373\373\376\367\367\367\373\370\370\370\374\374\374\374\376" - "\370\370\370\374\374\374\374\376\374\374\374\376\377\377\377\377\371\371" - "\371\375\377\377\377\377\377\377\377\377\365\365\365\373\374\374\374\376" - "\370\370\370\374\366\366\366\373\363\363\363\372\362\362\362\371\370\370" - "\370\374\370\370\370\374\374\374\374\376\377\377\377\377\374\374\374\376" - "\377\377\377\377\372\372\372\375\377\377\377\377\373\373\373\375\371\371" - "\371\375\375\375\375\377\373\373\373\375\373\373\373\376\373\373\373\375" - "\370\370\370\374\377\377\377\377\370\370\370\374\371\371\371\375\374\374" - "\374\376\373\373\373\375\372\372\372\375\375\375\375\377\366\366\366\373" - "\357\357\357\370\365\365\365\373\366\366\366\373\366\366\366\373\370\370" - "\370\374\370\370\370\374\371\371\371\375\372\372\372\375\370\370\370\374" - "\373\373\373\375\370\370\370\374\373\373\373\375\374\374\374\376\377\377" - "\377\377\376\376\376\377\371\371\371\374\372\372\372\375\370\370\370\374" - "\365\365\365\373\371\371\371\375\364\364\364\372\370\370\370\374\371\371" - "\371\375\377\377\377\377\375\375\375\376\370\370\370\374\373\373\373\376" - "\373\373\373\375\371\371\371\374\371\371\371\375\373\373\373\376\373\373" - "\373\376\365\365\365\372\362\362\362\371\365\365\365\372\365\365\365\373" - "\371\371\371\375\372\372\372\375\372\372\372\375\376\376\376\377\370\370" - "\370\374\370\370\370\374\372\372\372\375\373\373\373\376\374\374\374\376" - "\365\365\365\373\366\366\366\373\370\370\370\374\363\363\363\371\362\362" - "\362\371\367\367\367\373\370\370\370\374\372\372\372\375\371\371\371\374" - "\372\372\372\375\365\365\365\372\370\370\370\374\362\362\362\371\364\364" - "\364\372\361\361\361\371\364\364\364\372\370\370\370\374\370\370\370\374" - "\377\377\377\377\374\374\374\376\370\370\370\374\366\366\366\373\366\366" - "\366\373\364\364\364\372\363\363\363\371\366\366\366\373\371\371\371\375" - "\365\365\365\373\372\372\372\375\366\366\366\373\370\370\370\374\365\365" - "\365\373\370\370\370\374\376\376\376\377\375\375\375\377\373\373\373\376" - "\373\373\373\375\374\374\374\376\373\373\373\375\377\377\377\377\374\374" - "\374\376\375\375\375\377\374\374\374\376\370\370\370\374\365\365\365\373" - "\367\367\367\373\376\376\376\377\377\377\377\377\372\372\372\375\372\372" - "\372\375\375\375\375\376\377\377\377\377\377\377\377\377\375\375\375\376" - "\363\363\363\372\373\373\373\376\371\371\371\375\370\370\370\374\362\362" - "\362\371\366\366\366\373\373\373\373\375\373\373\373\375\375\375\375\376" - "\373\373\373\376\374\374\374\376\377\377\377\377\377\377\377\377\370\370" - "\370\374\377\377\377\377\374\374\374\376\370\370\370\374\370\370\370\374" - "\366\366\366\373\366\366\366\373\372\372\372\375\374\374\374\376\367\367" - "\367\373\375\375\375\376\377\377\377\377\371\371\371\374\370\370\370\374" - "\371\371\371\375\371\371\371\374\365\365\365\372\366\366\366\373\362\362" - "\362\371\365\365\365\372\370\370\370\374\372\372\372\375\371\371\371\374" - "\373\373\373\376\374\374\374\376\371\371\371\375\370\370\370\374\372\372" - "\372\375\373\373\373\376\377\377\377\377\373\373\373\375\375\375\375\376" - "\372\372\372\375\370\370\370\374\370\370\370\374\371\371\371\375\366\366" - "\366\373\364\364\364\372\365\365\365\373\371\371\371\375\374\374\374\376" - "\367\367\367\373\373\373\373\375\375\375\375\376\374\374\374\376\371\371" - "\371\375\371\371\371\375\370\370\370\374\371\371\371\375\362\362\362\371" - "\366\366\366\373\366\366\366\373\373\373\373\375\370\370\370\374\377\377" - "\377\377\374\374\374\376\371\371\371\374\374\374\374\376\370\370\370\374" - "\370\370\370\374\375\375\375\376\366\366\366\373\366\366\366\373\366\366" - "\366\373\364\364\364\372\362\362\362\371\371\371\371\374\366\366\366\373" - "\375\375\375\376\371\371\371\375\371\371\371\374\365\365\365\373\370\370" - "\370\374\371\371\371\374\366\366\366\373\364\364\364\372\365\365\365\373" - "\365\365\365\372\373\373\373\376\371\371\371\374\374\374\374\376\371\371" - "\371\374\370\370\370\374\371\371\371\374\363\363\363\372\365\365\365\373" - "\363\363\363\371\366\366\366\373\365\365\365\373\373\373\373\375\365\365" - "\365\372\366\366\366\373\373\373\373\375\373\373\373\375\371\371\371\375" - "\370\370\370\374\374\374\374\376\375\375\375\376\371\371\371\374\374\374" - "\374\376\371\371\371\374\366\366\366\373\377\377\377\377\371\371\371\374" - "\365\365\365\372\366\366\366\373\367\367\367\373\377\377\377\377\377\377" - "\377\377\374\374\374\376\373\373\373\375\373\373\373\376\371\371\371\374" - "\374\374\374\376\372\372\372\375\374\374\374\376\372\372\372\375\367\367" - "\367\373\367\367\367\373\361\361\361\371\363\363\363\372\366\366\366\373" - "\371\371\371\375\371\371\371\374\365\365\365\372\373\373\373\375\372\372" - "\372\375\377\377\377\377\374\374\374\376\375\375\375\376\372\372\372\375" - "\371\371\371\374\363\363\363\372\370\370\370\374\365\365\365\373\356\356" - "\356\367\374\374\374\376\367\367\367\373\367\367\367\373\374\374\374\376" - "\370\370\370\374\374\374\374\376\367\367\367\373\365\365\365\372\364\364" - "\364\372\370\370\370\374\356\356\356\367\366\366\366\373\367\367\367\373" - "\370\370\370\374\370\370\370\374\375\375\375\377\373\373\373\375\371\371" - "\371\375\373\373\373\375\372\372\372\375\373\373\373\375\374\374\374\376" - "\372\372\372\375\377\377\377\377\377\377\377\377\373\373\373\375\377\377" - "\377\377\371\371\371\375\370\370\370\374\373\373\373\375\373\373\373\376" - "\371\371\371\375\373\373\373\375\374\374\374\376\370\370\370\374\374\374" - "\374\376\367\367\367\373\364\364\364\372\370\370\370\374\365\365\365\373" - "\365\365\365\372\364\364\364\372\370\370\370\374\364\364\364\372\371\371" - "\371\374\371\371\371\375\374\374\374\376\376\376\376\377\374\374\374\376" - "\375\375\375\376\366\366\366\373\364\364\364\372\371\371\371\375\365\365" - "\365\372\365\365\365\373\371\371\371\374\355\355\355\367\364\364\364\372" - "\363\363\363\371\365\365\365\372\373\373\373\375\370\370\370\374\374\374" - "\374\376\374\374\374\376\365\365\365\373\371\371\371\375\367\367\367\373" - "\363\363\363\372\374\374\374\376\365\365\365\372\373\373\373\375\363\363" - "\363\372\365\365\365\373\371\371\371\374\373\373\373\375\374\374\374\376" - "\371\371\371\374\366\366\366\373\364\364\364\372\363\363\363\371\370\370" - "\370\374\366\366\366\373\362\362\362\371\364\364\364\372\375\375\375\376" - "\375\375\375\376\375\375\375\376\377\377\377\377\374\374\374\376\377\377" - "\377\377\365\365\365\373\374\374\374\376\374\374\374\376\367\367\367\373" - "\371\371\371\375\363\363\363\372\371\371\371\375\373\373\373\376\370\370" - "\370\374\371\371\371\374\366\366\366\373\373\373\373\376\374\374\374\376" - "\373\373\373\375\372\372\372\375\366\366\366\373\373\373\373\375\365\365" - "\365\372\364\364\364\372\366\366\366\373\367\367\367\373\364\364\364\372" - "\363\363\363\372\365\365\365\373\371\371\371\374\372\372\372\375\366\366" - "\366\373\377\377\377\377\372\372\372\375\373\373\373\376\377\377\377\377" - "\372\372\372\375\374\374\374\376\374\374\374\376\377\377\377\377\373\373" - "\373\375\367\367\367\373\364\364\364\372\377\377\377\377\374\374\374\376" - "\375\375\375\377\371\371\371\375\366\366\366\373\365\365\365\373\371\371" - "\371\374\370\370\370\374\365\365\365\372\362\362\362\371\365\365\365\373" - "\365\365\365\373\366\366\366\373\365\365\365\373\371\371\371\375\371\371" - "\371\375\371\371\371\375\371\371\371\374\374\374\374\376\376\376\376\377" - "\374\374\374\376\372\372\372\375\367\367\367\373\374\374\374\376\377\377" - "\377\377\372\372\372\375\374\374\374\376\373\373\373\375\365\365\365\372" - "\364\364\364\372\366\366\366\373\366\366\366\373\373\373\373\375\373\373" - "\373\375\372\372\372\375\371\371\371\374\371\371\371\375\364\364\364\372" - "\366\366\366\373\355\355\355\367\364\364\364\372\357\357\357\370\363\363" - "\363\372\364\364\364\372\372\372\372\375\374\374\374\376\374\374\374\376" - "\372\372\372\375\372\372\372\375\367\367\367\373\372\372\372\375\377\377" - "\377\377\366\366\366\373\365\365\365\373\364\364\364\372\363\363\363\371" - "\364\364\364\372\367\367\367\373\362\362\362\371\370\370\370\374\364\364" - "\364\372\374\374\374\376\374\374\374\376\362\362\362\371\371\371\371\374" - "\367\367\367\373\365\365\365\372\373\373\373\376\364\364\364\372\371\371" - "\371\375\365\365\365\372\364\364\364\372\367\367\367\373\371\371\371\375" - "\370\370\370\374\364\364\364\372\366\366\366\373\367\367\367\373\370\370" - "\370\374\365\365\365\372\365\365\365\372\364\364\364\372\370\370\370\374" - "\371\371\371\374\372\372\372\375\375\375\375\377\375\375\375\376\377\377" - "\377\377\372\372\372\375\374\374\374\376\373\373\373\375\365\365\365\373" - "\372\372\372\375\366\366\366\373\371\371\371\374\364\364\364\372\365\365" - "\365\372\365\365\365\372\364\364\364\372\366\366\366\373\372\372\372\375" - "\372\372\372\375\371\371\371\374\372\372\372\375\377\377\377\377\370\370" - "\370\374\371\371\371\374\364\364\364\372\361\361\361\371\364\364\364\372" - "\364\364\364\372\363\363\363\372\364\364\364\372\365\365\365\372\374\374" - "\374\376\371\371\371\375\377\377\377\377\377\377\377\377\372\372\372\375" - "\377\377\377\377\373\373\373\376\373\373\373\376\373\373\373\375\373\373" - "\373\375\366\366\366\373\364\364\364\372\367\367\367\373\373\373\373\375" - "\370\370\370\374\370\370\370\374\365\365\365\372\364\364\364\372\365\365" - "\365\372\370\370\370\374\372\372\372\375\370\370\370\374\363\363\363\372" - "\365\365\365\373\363\363\363\371\365\365\365\373\364\364\364\372\366\366" - "\366\373\371\371\371\374\366\366\366\373\366\366\366\373\371\371\371\374" - "\366\366\366\373\374\374\374\376\377\377\377\377\370\370\370\374\365\365" - "\365\373\376\376\376\377\372\372\372\375\374\374\374\376\367\367\367\373" - "\374\374\374\376\367\367\367\373\370\370\370\374\365\365\365\372\372\372" - "\372\375\371\371\371\375\373\373\373\375\371\371\371\375\370\370\370\374" - "\376\376\376\377\365\365\365\372\371\371\371\375\362\362\362\371\357\357" - "\357\370\362\362\362\371\371\371\371\374\365\365\365\373\366\366\366\373" - "\367\367\367\373\365\365\365\373\370\370\370\374\367\367\367\373\371\371" - "\371\375\371\371\371\375\375\375\375\377\366\366\366\373\371\371\371\374" - "\365\365\365\372\356\356\356\367\364\364\364\372\366\366\366\373\370\370" - "\370\374\372\372\372\375\367\367\367\373\373\373\373\375\371\371\371\375" - "\371\371\371\374\374\374\374\376\363\363\363\371\373\373\373\376\371\371" - "\371\374\363\363\363\372\364\364\364\372\366\366\366\373\364\364\364\372" - "\373\373\373\376\365\365\365\373\373\373\373\376\371\371\371\374\371\371" - "\371\374\365\365\365\372\364\364\364\372\362\362\362\371\365\365\365\372" - "\363\363\363\371\372\372\372\375\371\371\371\375\370\370\370\374\365\365" - "\365\372\375\375\375\377\377\377\377\377\373\373\373\376\371\371\371\374" - "\372\372\372\375\367\367\367\373\370\370\370\374\371\371\371\375\373\373" - "\373\375\370\370\370\374\366\366\366\373\370\370\370\374\370\370\370\374" - "\363\363\363\372\371\371\371\375\371\371\371\374\370\370\370\374\371\371" - "\371\374\371\371\371\374\367\367\367\373\372\372\372\375\363\363\363\372" - "\374\374\374\376\371\371\371\375\372\372\372\375\374\374\374\376\364\364" - "\364\372\364\364\364\372\374\374\374\376\373\373\373\376\377\377\377\377" - "\376\376\376\377\372\372\372\375\376\376\376\377\370\370\370\374\366\366" - "\366\373\374\374\374\376\373\373\373\375\366\366\366\373\371\371\371\375" - "\370\370\370\374\370\370\370\374\364\364\364\372\377\377\377\377\364\364" - "\364\372\365\365\365\372\365\365\365\373\366\366\366\373\371\371\371\375" - "\372\372\372\375\364\364\364\372\365\365\365\373\365\365\365\373\371\371" - "\371\374\366\366\366\373\365\365\365\373\363\363\363\372\364\364\364\372" - "\364\364\364\372\370\370\370\374\371\371\371\374\373\373\373\376\374\374" - "\374\376\366\366\366\373\365\365\365\373\374\374\374\376\377\377\377\377" - "\376\376\376\377\367\367\367\373\374\374\374\376\363\363\363\371\364\364" - "\364\372\364\364\364\372\371\371\371\375\373\373\373\375\373\373\373\375" - "\372\372\372\375\377\377\377\377\371\371\371\374\371\371\371\375\362\362" - "\362\371\363\363\363\371\361\361\361\371\363\363\363\372\371\371\371\374" - "\363\363\363\371\362\362\362\371\370\370\370\374\363\363\363\372\370\370" - "\370\374\375\375\375\376\375\375\375\376\367\367\367\373\374\374\374\376" - "\371\371\371\375\366\366\366\373\364\364\364\372\362\362\362\371\362\362" - "\362\371\366\366\366\373\370\370\370\374\371\371\371\374\370\370\370\374" - "\357\357\357\370\366\366\366\373\370\370\370\374\371\371\371\375\366\366" - "\366\373\365\365\365\372\373\373\373\376\365\365\365\373\362\362\362\371" - "\367\367\367\373\362\362\362\371\367\367\367\373\374\374\374\376\373\373" - "\373\375\371\371\371\374\366\366\366\373\364\364\364\372\365\365\365\373" - "\366\366\366\373\357\357\357\370\364\364\364\372\367\367\367\373\370\370" - "\370\374\364\364\364\372\362\362\362\371\366\366\366\373\374\374\374\376" - "\371\371\371\375\365\365\365\372\367\367\367\373\367\367\367\373\362\362" - "\362\371\365\365\365\373\366\366\366\373\372\372\372\375\362\362\362\371" - "\362\362\362\371\364\364\364\372\364\364\364\372\367\367\367\373\371\371" - "\371\375\366\366\366\373\367\367\367\373\374\374\374\376\370\370\370\374" - "\373\373\373\375\371\371\371\375\371\371\371\375\370\370\370\374\365\365" - "\365\373\366\366\366\373\362\362\362\371\367\367\367\373\371\371\371\375" - "\372\372\372\375\377\377\377\377\370\370\370\374\374\374\374\376\367\367" - "\367\373\373\373\373\375\374\374\374\376\363\363\363\372\372\372\372\375" - "\374\374\374\376\373\373\373\375\376\376\376\377\377\377\377\377\377\377" - "\377\377\373\373\373\376\375\375\375\376\371\371\371\374\374\374\374\376" - "\371\371\371\375\373\373\373\376\370\370\370\374\365\365\365\373\363\363" - "\363\372\370\370\370\374\371\371\371\374\363\363\363\371\363\363\363\372" - "\363\363\363\372\363\363\363\372\365\365\365\373\372\372\372\375\371\371" - "\371\374\366\366\366\373\367\367\367\373\364\364\364\372\373\373\373\375" - "\374\374\374\376\373\373\373\376\373\373\373\376\375\375\375\376\366\366" - "\366\373\365\365\365\372\364\364\364\372\370\370\370\374\364\364\364\372" - "\371\371\371\375\377\377\377\377\372\372\372\375\374\374\374\376\371\371" - "\371\374\370\370\370\374\370\370\370\374\356\356\356\367\352\352\352\365" - "\362\362\362\371\364\364\364\372\370\370\370\374\365\365\365\372\363\363" - "\363\372\373\373\373\376\372\372\372\375\373\373\373\376\375\375\375\376" - "\365\365\365\372\363\363\363\372\371\371\371\374\366\366\366\373\363\363" - "\363\372\363\363\363\372\357\357\357\370\371\371\371\375\364\364\364\372" - "\365\365\365\373\377\377\377\377\366\366\366\373\366\366\366\373\373\373" - "\373\375\366\366\366\373\363\363\363\371\356\356\356\367\372\372\372\375" - "\362\362\362\371\363\363\363\371\355\355\355\366\357\357\357\370\370\370" - "\370\374\364\364\364\372\370\370\370\374\370\370\370\374\366\366\366\373" - "\366\366\366\373\366\366\366\373\364\364\364\372\364\364\364\372\371\371" - "\371\374\366\366\366\373\370\370\370\374\371\371\371\374\372\372\372\375" - "\372\372\372\375\374\374\374\376\370\370\370\374\371\371\371\374\376\376" - "\376\377\366\366\366\373\366\366\366\373\362\362\362\371\366\366\366\373" - "\371\371\371\374\364\364\364\372\361\361\361\371\366\366\366\373\365\365" - "\365\373\363\363\363\371\374\374\374\376\371\371\371\374\366\366\366\373" - "\372\372\372\375\364\364\364\372\372\372\372\375\372\372\372\375\367\367" - "\367\373\363\363\363\371\364\364\364\372\364\364\364\372\363\363\363\372" - "\371\371\371\374\377\377\377\377\374\374\374\376\377\377\377\377\377\377" - "\377\377\371\371\371\374\370\370\370\374\376\376\376\377\370\370\370\374" - "\374\374\374\376\371\371\371\375\374\374\374\376\373\373\373\376\375\375" - "\375\377\372\372\372\375\375\375\375\377\371\371\371\375\377\377\377\377" - "\374\374\374\376\376\376\376\377\374\374\374\376\372\372\372\375\371\371" - "\371\374\363\363\363\372\362\362\362\371\365\365\365\373\366\366\366\373" - "\363\363\363\372\361\361\361\371\361\361\361\371\366\366\366\373\366\366" - "\366\373\365\365\365\373\374\374\374\376\367\367\367\373\370\370\370\374" - "\374\374\374\376\366\366\366\373\372\372\372\375\370\370\370\374\374\374" - "\374\376\373\373\373\376\376\376\376\377\370\370\370\374\361\361\361\371" - "\363\363\363\372\365\365\365\372\377\377\377\377\366\366\366\373\370\370" - "\370\374\362\362\362\371\370\370\370\374\362\362\362\371\366\366\366\373" - "\360\360\360\370\370\370\370\374\364\364\364\372\355\355\355\367\371\371" - "\371\374\365\365\365\372\367\367\367\373\366\366\366\373\365\365\365\372" - "\370\370\370\374\365\365\365\372\367\367\367\373\373\373\373\375\365\365" - "\365\373\364\364\364\372\362\362\362\371\363\363\363\371\356\356\356\367" - "\346\346\346\363\367\367\367\373\366\366\366\373\371\371\371\375\366\366" - "\366\373\364\364\364\372\366\366\366\373\376\376\376\377\373\373\373\376" - "\364\364\364\372\370\370\370\374\364\364\364\372\366\366\366\373\370\370" - "\370\374\364\364\364\372\370\370\370\374\370\370\370\374\367\367\367\373" - "\367\367\367\373\371\371\371\374\371\371\371\375\366\366\366\373\360\360" - "\360\370\373\373\373\376\371\371\371\375\373\373\373\376\373\373\373\376" - "\372\372\372\375\371\371\371\375\374\374\374\376\365\365\365\373\372\372" - "\372\375\373\373\373\375\375\375\375\376\371\371\371\374\370\370\370\374" - "\363\363\363\372\366\366\366\373\366\366\366\373\364\364\364\372\364\364" - "\364\372\367\367\367\373\364\364\364\372\367\367\367\373\373\373\373\375" - "\365\365\365\373\373\373\373\376\372\372\372\375\364\364\364\372\371\371" - "\371\374\373\373\373\376\362\362\362\371\366\366\366\373\364\364\364\372" - "\362\362\362\371\365\365\365\372\374\374\374\376\377\377\377\377\372\372" - "\372\375\377\377\377\377\377\377\377\377\376\376\376\377\374\374\374\376" - "\372\372\372\375\374\374\374\376\374\374\374\376\371\371\371\374\374\374" - "\374\376\370\370\370\374\367\367\367\373\377\377\377\377\371\371\371\375" - "\371\371\371\374\374\374\374\376\371\371\371\374\377\377\377\377\373\373" - "\373\375\370\370\370\374\366\366\366\373\364\364\364\372\371\371\371\375" - "\364\364\364\372\365\365\365\372\364\364\364\372\362\362\362\371\363\363" - "\363\372\364\364\364\372\366\366\366\373\365\365\365\372\371\371\371\375" - "\370\370\370\374\371\371\371\375\375\375\375\377\373\373\373\375\371\371" - "\371\374\374\374\374\376\366\366\366\373\373\373\373\375\377\377\377\377" - "\370\370\370\374\366\366\366\373\371\371\371\374\377\377\377\377\364\364" - "\364\372\366\366\366\373\377\377\377\377\371\371\371\375\370\370\370\374" - "\371\371\371\375\370\370\370\374\366\366\366\373\362\362\362\371\373\373" - "\373\376\365\365\365\373\351\351\351\365\367\367\367\373\370\370\370\374" - "\372\372\372\375\371\371\371\374\367\367\367\373\365\365\365\372\371\371" - "\371\375\377\377\377\377\365\365\365\372\354\354\354\366\361\361\361\371" - "\357\357\357\370\353\353\353\366\357\357\357\370\366\366\366\373\367\367" - "\367\373\370\370\370\374\371\371\371\374\371\371\371\375\370\370\370\374" - "\370\370\370\374\362\362\362\371\372\372\372\375\370\370\370\374\371\371" - "\371\374\365\365\365\372\363\363\363\371\370\370\370\374\362\362\362\371" - "\370\370\370\374\371\371\371\374\372\372\372\375\373\373\373\375\370\370" - "\370\374\364\364\364\372\371\371\371\375\371\371\371\374\372\372\372\375" - "\373\373\373\375\366\366\366\373\372\372\372\375\371\371\371\375\375\375" - "\375\376\367\367\367\373\371\371\371\375\371\371\371\375\372\372\372\375" - "\372\372\372\375\365\365\365\372\357\357\357\370\362\362\362\371\370\370" - "\370\374\366\366\366\373\362\362\362\371\361\361\361\371\362\362\362\371" - "\366\366\366\373\370\370\370\374\375\375\375\377\370\370\370\374\373\373" - "\373\376\367\367\367\373\372\372\372\375\371\371\371\374\365\365\365\373" - "\365\365\365\373\363\363\363\371\367\367\367\373\365\365\365\372\374\374" - "\374\376\377\377\377\377\371\371\371\375\377\377\377\377\377\377\377\377" - "\377\377\377\377\377\377\377\377\375\375\375\377\374\374\374\376\367\367" - "\367\373\375\375\375\376\371\371\371\374\366\366\366\373\366\366\366\373" - "\370\370\370\374\373\373\373\375\377\377\377\377\377\377\377\377\375\375" - "\375\377\377\377\377\377\371\371\371\374\375\375\375\377\372\372\372\375" - "\373\373\373\375\366\366\366\373\373\373\373\375\366\366\366\373\354\354" - "\354\366\364\364\364\372\365\365\365\372\366\366\366\373\370\370\370\374" - "\366\366\366\373\366\366\366\373\373\373\373\376\374\374\374\376\371\371" - "\371\374\366\366\366\373\373\373\373\375\372\372\372\375\366\366\366\373" - "\370\370\370\374\365\365\365\372\373\373\373\375\363\363\363\372\363\363" - "\363\372\373\373\373\375\367\367\367\373\371\371\371\374\375\375\375\377" - "\370\370\370\374\366\366\366\373\365\365\365\372\365\365\365\373\373\373" - "\373\375\366\366\366\373\362\362\362\371\362\362\362\371\363\363\363\372" - "\366\366\366\373\372\372\372\375\377\377\377\377\373\373\373\376\373\373" - "\373\376\367\367\367\373\370\370\370\374\371\371\371\374\365\365\365\372" - "\356\356\356\367\355\355\355\366\356\356\356\367\356\356\356\367\364\364" - "\364\372\371\371\371\375\374\374\374\376\373\373\373\375\372\372\372\375" - "\371\371\371\375\370\370\370\374\371\371\371\374\366\366\366\373\373\373" - "\373\375\364\364\364\372\363\363\363\372\370\370\370\374\370\370\370\374" - "\371\371\371\375\371\371\371\374\374\374\374\376\372\372\372\375\370\370" - "\370\374\374\374\374\376\371\371\371\375\366\366\366\373\370\370\370\374" - "\371\371\371\375\372\372\372\375\366\366\366\373\372\372\372\375\371\371" - "\371\375\370\370\370\374\367\367\367\373\371\371\371\374\367\367\367\373" - "\372\372\372\375\370\370\370\374\371\371\371\374\365\365\365\372\363\363" - "\363\371\365\365\365\372\364\364\364\372\363\363\363\371\364\364\364\372" - "\365\365\365\372\367\367\367\373\366\366\366\373\373\373\373\376\370\370" - "\370\374\370\370\370\374\373\373\373\375\371\371\371\374\370\370\370\374" - "\370\370\370\374\373\373\373\376\371\371\371\374\371\371\371\374\371\371" - "\371\375\366\366\366\373\376\376\376\377\377\377\377\377\377\377\377\377" - "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\374" - "\374\376\377\377\377\377\365\365\365\373\370\370\370\374\375\375\375\376" - "\362\362\362\371\374\374\374\376\372\372\372\375\372\372\372\375\377\377" - "\377\377\374\374\374\376\373\373\373\375\375\375\375\376\372\372\372\375" - "\377\377\377\377\365\365\365\373\375\375\375\377\373\373\373\376\365\365" - "\365\372\365\365\365\373\363\363\363\371\365\365\365\373\367\367\367\373" - "\372\372\372\375\366\366\366\373\370\370\370\374\372\372\372\375\371\371" - "\371\375\371\371\371\374\370\370\370\374\371\371\371\374\366\366\366\373" - "\374\374\374\376\371\371\371\375\365\365\365\372\367\367\367\373\373\373" - "\373\376\364\364\364\372\373\373\373\375\366\366\366\373\377\377\377\377" - "\375\375\375\377\373\373\373\376\365\365\365\372\374\374\374\376\371\371" - "\371\374\366\366\366\373\364\364\364\372\371\371\371\374\364\364\364\372" - "\363\363\363\371\361\361\361\371\370\370\370\374\377\377\377\377\372\372" - "\372\375\373\373\373\375\366\366\366\373\371\371\371\374\375\375\375\376" - "\374\374\374\376\373\373\373\375\371\371\371\375\366\366\366\373\367\367" - "\367\373\351\351\351\365\361\361\361\371\370\370\370\374\377\377\377\377" - "\373\373\373\375\373\373\373\376\371\371\371\374\375\375\375\376\363\363" - "\363\372\372\372\372\375\365\365\365\372\367\367\367\373\365\365\365\372" - "\371\371\371\375\365\365\365\372\365\365\365\372\373\373\373\376\373\373" - "\373\375\373\373\373\376\373\373\373\375\374\374\374\376\373\373\373\375" - "\373\373\373\375\374\374\374\376\364\364\364\372\365\365\365\373\370\370" - "\370\374\370\370\370\374\364\364\364\372\370\370\370\374\360\360\360\370" - "\377\377\377\377\365\365\365\372\373\373\373\375\371\371\371\375\373\373" - "\373\376\365\365\365\372\371\371\371\374\371\371\371\375\362\362\362\371" - "\365\365\365\373\364\364\364\372\364\364\364\372\362\362\362\371\370\370" - "\370\374\372\372\372\375\371\371\371\374\375\375\375\377\373\373\373\375" - "\364\364\364\372\377\377\377\377\366\366\366\373\373\373\373\375\371\371" - "\371\374\371\371\371\374\365\365\365\372\371\371\371\374\371\371\371\374" - "\370\370\370\374\373\373\373\375\372\372\372\375\371\371\371\375\377\377" - "\377\377\377\377\377\377\374\374\374\376\366\366\366\373\373\373\373\375" - "\366\366\366\373\365\365\365\373\364\364\364\372\367\367\367\373\372\372" - "\372\375\377\377\377\377\373\373\373\376\377\377\377\377\377\377\377\377" - "\372\372\372\375\371\371\371\375\371\371\371\375\366\366\366\373\371\371" - "\371\374\370\370\370\374\365\365\365\372\364\364\364\372\361\361\361\371" - "\364\364\364\372\373\373\373\375\371\371\371\374\371\371\371\374\370\370" - "\370\374\373\373\373\376\373\373\373\375\376\376\376\377\375\375\375\377" - "\370\370\370\374\376\376\376\377\370\370\370\374\371\371\371\374\365\365" - "\365\373\366\366\366\373\372\372\372\375\373\373\373\376\375\375\375\377" - "\377\377\377\377\370\370\370\374\376\376\376\377\371\371\371\375\372\372" - "\372\375\373\373\373\375\374\374\374\376\377\377\377\377\373\373\373\375" - "\365\365\365\372\366\366\366\373\366\366\366\373\366\366\366\373\371\371" - "\371\374\370\370\370\374\365\365\365\373\370\370\370\374\371\371\371\375" - "\372\372\372\375\375\375\375\377\377\377\377\377\371\371\371\375\371\371" - "\371\375\366\366\366\373\362\362\362\371\361\361\361\371\365\365\365\373" - "\371\371\371\375\366\366\366\373\375\375\375\376\371\371\371\375\371\371" - "\371\374\370\370\370\374\370\370\370\374\370\370\370\374\365\365\365\372" - "\367\367\367\373\365\365\365\373\367\367\367\373\371\371\371\375\363\363" - "\363\371\371\371\371\374\372\372\372\375\370\370\370\374\373\373\373\375" - "\371\371\371\375\373\373\373\376\373\373\373\375\377\377\377\377\371\371" - "\371\375\366\366\366\373\370\370\370\374\364\364\364\372\371\371\371\374" - "\371\371\371\375\365\365\365\373\371\371\371\375\370\370\370\374\371\371" - "\371\374\374\374\374\376\376\376\376\377\370\370\370\374\370\370\370\374" - "\370\370\370\374\363\363\363\372\371\371\371\375\363\363\363\371\364\364" - "\364\372\366\366\366\373\366\366\366\373\367\367\367\373\373\373\373\375" - "\366\366\366\373\374\374\374\376\370\370\370\374\375\375\375\377\372\372" - "\372\375\375\375\375\376\373\373\373\375\371\371\371\374\362\362\362\371" - "\370\370\370\374\370\370\370\374\373\373\373\376\373\373\373\375\374\374" - "\374\376\371\371\371\374\365\365\365\373\370\370\370\374\374\374\374\376" - "\366\366\366\373\371\371\371\374\366\366\366\373\362\362\362\371\370\370" - "\370\374\372\372\372\375\365\365\365\372\372\372\372\375\373\373\373\376" - "\371\371\371\374\374\374\374\376\365\365\365\372\376\376\376\377\377\377" - "\377\377\370\370\370\374\367\367\367\373\370\370\370\374\366\366\366\373" - "\364\364\364\372\366\366\366\373\365\365\365\373\366\366\366\373\370\370" - "\370\374\371\371\371\374\370\370\370\374\373\373\373\376\375\375\375\377" - "\377\377\377\377\377\377\377\377\374\374\374\376\377\377\377\377\365\365" - "\365\372\365\365\365\373\367\367\367\373\366\366\366\373\370\370\370\374" - "\371\371\371\374\370\370\370\374\376\376\376\377\366\366\366\373\377\377" - "\377\377\375\375\375\376\370\370\370\374\377\377\377\377\373\373\373\375" - "\371\371\371\375\377\377\377\377\366\366\366\373\370\370\370\374\364\364" - "\364\372\364\364\364\372\366\366\366\373\365\365\365\373\371\371\371\375" - "\366\366\366\373\373\373\373\375\373\373\373\375\376\376\376\377\375\375" - "\375\376\371\371\371\375\365\365\365\373\371\371\371\374\364\364\364\372" - "\364\364\364\372\370\370\370\374\370\370\370\374\373\373\373\375\374\374" - "\374\376\373\373\373\375\374\374\374\376\372\372\372\375\366\366\366\373" - "\370\370\370\374\367\367\367\373\374\374\374\376\367\367\367\373\363\363" - "\363\372\365\365\365\372\366\366\366\373\370\370\370\374\373\373\373\375" - "\371\371\371\374\374\374\374\376\372\372\372\375\370\370\370\374\367\367" - "\367\373\371\371\371\375\370\370\370\374\370\370\370\374\370\370\370\374" - "\371\371\371\374\361\361\361\371\370\370\370\374\362\362\362\371\367\367" - "\367\373\365\365\365\372\367\367\367\373\371\371\371\374\371\371\371\374" - "\370\370\370\374\370\370\370\374\370\370\370\374\366\366\366\373\363\363" - "\363\372\363\363\363\371\366\366\366\373\365\365\365\373\370\370\370\374" - "\373\373\373\375\370\370\370\374\374\374\374\376\377\377\377\377\370\370" - "\370\374\371\371\371\375\373\373\373\376\365\365\365\372\364\364\364\372" - "\372\372\372\375\371\371\371\375\366\366\366\373\366\366\366\373\375\375" - "\375\377\371\371\371\375\371\371\371\374\375\375\375\376\374\374\374\376" - "\375\375\375\376\366\366\366\373\371\371\371\374\373\373\373\375\367\367" - "\367\373\365\365\365\373\366\366\366\373\371\371\371\375\374\374\374\376" - "\374\374\374\376\373\373\373\376\373\373\373\375\374\374\374\376\370\370" - "\370\374\374\374\374\376\371\371\371\374\371\371\371\375\371\371\371\375" - "\364\364\364\372\370\370\370\374\364\364\364\372\366\366\366\373\363\363" - "\363\372\364\364\364\372\371\371\371\374\364\364\364\372\370\370\370\374" - "\374\374\374\376\373\373\373\376\377\377\377\377\372\372\372\375\373\373" - "\373\376\371\371\371\374\371\371\371\374\365\365\365\373\365\365\365\373" - "\363\363\363\372\366\366\366\373\370\370\370\374\372\372\372\375\371\371" - "\371\375\367\367\367\373\375\375\375\377\375\375\375\376\370\370\370\374" - "\365\365\365\373\372\372\372\375\371\371\371\375\370\370\370\374\363\363" - "\363\371\365\365\365\373\365\365\365\372\366\366\366\373\364\364\364\372" - "\366\366\366\373\375\375\375\376\371\371\371\374\375\375\375\377\374\374" - "\374\376\372\372\372\375\373\373\373\376\365\365\365\372\364\364\364\372" - "\365\365\365\372\365\365\365\372\366\366\366\373\365\365\365\372\364\364" - "\364\372\374\374\374\376\371\371\371\374\373\373\373\375\375\375\375\377" - "\376\376\376\377\357\357\357\370\370\370\370\374\366\366\366\373\357\357" - "\357\370\370\370\370\374\371\371\371\375\371\371\371\375\366\366\366\373" - "\366\366\366\373\371\371\371\375\370\370\370\374\371\371\371\375", -}; diff --git a/3d-viewer/textures/text_silk.h b/3d-viewer/textures/text_silk.h deleted file mode 100644 index 934f3d024a..0000000000 --- a/3d-viewer/textures/text_silk.h +++ /dev/null @@ -1,3675 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/* GIMP RGBA C-Source image dump (text_silk.c) */ - -static const struct { - unsigned int width; - unsigned int height; - unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ - unsigned char pixel_data[128 * 128 * 4 + 1]; -} text_silk = { - 128, 128, 4, - "\357\363\357\377\355\360\355\377\356\361\356\377\356\361\356\377\357\363" - "\357\377\355\361\356\377\356\361\356\377\354\360\355\377\354\363\357\377" - "\351\356\354\377\353\360\356\377\354\360\355\377\353\360\356\377\360\365" - "\361\377\357\363\357\377\356\361\356\377\356\361\356\377\354\363\357\377" - "\354\360\355\377\355\361\356\377\354\360\355\377\357\363\357\377\354\360" - "\355\377\356\363\357\377\353\360\356\377\353\360\356\377\352\357\355\377" - "\354\360\355\377\353\356\352\377\351\355\352\377\346\354\352\377\354\360" - "\355\377\354\363\357\377\354\363\357\377\360\365\361\377\365\371\366\377" - "\366\371\366\377\364\367\363\377\357\361\355\377\357\361\355\377\353\356" - "\352\377\352\355\351\377\353\356\352\377\355\360\355\377\355\360\355\377" - "\360\365\360\377\363\367\364\377\360\365\360\377\364\367\363\377\357\363" - "\357\377\353\360\356\377\347\356\355\377\351\356\354\377\352\356\353\377" - "\352\356\353\377\353\356\352\377\347\353\347\377\350\354\350\377\350\354" - "\351\377\347\354\352\377\353\356\352\377\351\355\352\377\353\357\354\377" - "\353\357\354\377\354\360\355\377\354\357\354\377\350\354\350\377\350\354" - "\351\377\350\355\353\377\346\354\353\377\344\354\354\377\346\354\352\377" - "\351\356\354\377\351\355\352\377\352\355\351\377\354\357\354\377\352\355" - "\351\377\352\356\353\377\353\357\354\377\354\360\355\377\352\356\353\377" - "\354\357\354\377\355\360\355\377\354\357\354\377\356\361\356\377\356\363" - "\357\377\355\361\356\377\355\361\356\377\354\360\355\377\354\357\354\377" - "\354\360\355\377\356\363\357\377\356\363\357\377\356\361\356\377\355\360" - "\354\377\354\357\354\377\353\357\354\377\354\360\355\377\355\361\356\377" - "\354\357\354\377\356\361\356\377\356\361\356\377\356\361\356\377\356\361" - "\356\377\357\361\355\377\360\363\356\377\354\357\353\377\354\357\354\377" - "\352\356\353\377\354\360\355\377\353\357\354\377\355\361\356\377\352\356" - "\353\377\353\357\354\377\352\356\353\377\354\357\354\377\354\357\354\377" - "\354\357\354\377\356\360\354\377\355\357\353\377\353\356\352\377\355\360" - "\355\377\354\357\354\377\353\357\354\377\352\356\353\377\352\356\353\377" - "\353\357\354\377\353\357\354\377\356\361\356\377\355\361\356\377\355\360" - "\355\377\357\363\357\377\357\363\357\377\357\361\355\377\356\361\356\377" - "\355\361\356\377\353\357\354\377\351\355\352\377\350\355\353\377\352\356" - "\353\377\354\363\357\377\355\364\360\377\354\360\355\377\357\363\357\377" - "\354\360\355\377\354\363\357\377\356\363\357\377\355\361\356\377\355\361" - "\356\377\356\361\356\377\356\363\357\377\353\357\354\377\353\357\354\377" - "\353\360\356\377\353\357\354\377\354\360\355\377\354\357\354\377\346\354" - "\352\377\347\355\353\377\352\357\355\377\351\357\355\377\355\361\356\377" - "\356\363\357\377\365\370\364\377\363\366\361\377\363\366\361\377\356\360" - "\354\377\353\356\352\377\351\354\350\377\350\354\351\377\351\355\352\377" - "\355\360\355\377\356\361\356\377\356\361\356\377\356\363\360\377\357\365" - "\361\377\356\363\357\377\355\361\356\377\352\357\355\377\351\356\354\377" - "\347\356\355\377\353\357\354\377\353\357\354\377\352\356\353\377\352\355" - "\351\377\350\354\350\377\350\354\350\377\351\355\352\377\351\355\352\377" - "\353\356\352\377\353\357\354\377\354\360\355\377\355\360\355\377\354\357" - "\354\377\352\355\351\377\351\355\352\377\352\357\355\377\346\354\353\377" - "\343\353\353\377\345\353\352\377\351\356\354\377\352\356\353\377\353\357" - "\354\377\350\356\355\377\353\357\354\377\351\356\354\377\352\355\351\377" - "\350\355\353\377\352\357\355\377\354\360\355\377\354\360\355\377\355\360" - "\355\377\356\361\356\377\356\361\356\377\356\361\356\377\357\363\357\377" - "\356\361\356\377\356\361\356\377\354\360\355\377\355\361\356\377\356\363" - "\357\377\357\363\357\377\357\363\357\377\354\357\354\377\354\357\354\377" - "\356\361\356\377\357\363\357\377\357\363\357\377\357\363\357\377\357\363" - "\357\377\356\361\356\377\357\363\357\377\357\363\357\377\357\363\357\377" - "\357\361\355\377\354\357\354\377\354\357\354\377\354\357\354\377\355\357" - "\353\377\355\361\356\377\353\357\354\377\353\357\354\377\352\357\355\377" - "\352\356\353\377\353\356\353\377\354\357\354\377\355\360\355\377\353\356" - "\352\377\353\356\352\377\356\360\354\377\355\360\355\377\353\356\352\377" - "\355\360\355\377\352\356\353\377\354\360\355\377\354\360\355\377\354\360" - "\355\377\353\357\354\377\353\357\354\377\355\361\356\377\355\360\355\377" - "\357\361\355\377\357\363\357\377\354\360\355\377\355\361\356\377\351\355" - "\352\377\353\357\354\377\353\356\352\377\351\356\354\377\353\357\354\377" - "\355\361\356\377\354\360\355\377\355\361\356\377\356\363\357\377\354\360" - "\355\377\354\360\355\377\356\363\357\377\355\361\356\377\355\361\356\377" - "\355\361\356\377\355\361\356\377\356\361\356\377\350\354\351\377\350\356" - "\353\377\351\355\352\377\347\355\353\377\350\355\353\377\352\357\355\377" - "\353\357\354\377\354\360\355\377\355\360\355\377\356\361\356\377\361\365" - "\360\377\361\365\360\377\365\367\361\377\350\353\347\377\337\342\336\377" - "\326\333\326\377\345\351\346\377\352\355\351\377\355\360\355\377\355\361" - "\357\377\354\360\356\377\354\363\360\377\355\361\357\377\354\360\355\377" - "\352\357\355\377\351\356\354\377\352\356\353\377\352\356\353\377\352\356" - "\353\377\353\357\354\377\357\363\357\377\357\361\355\377\355\360\355\377" - "\352\356\353\377\352\356\353\377\352\355\351\377\352\356\353\377\353\357" - "\354\377\355\360\355\377\355\360\355\377\355\360\355\377\351\356\354\377" - "\351\356\354\377\351\356\354\377\350\355\353\377\346\355\354\377\352\356" - "\353\377\352\356\353\377\352\355\351\377\350\354\350\377\352\355\351\377" - "\350\355\353\377\351\355\352\377\352\357\355\377\351\356\354\377\351\356" - "\354\377\354\360\355\377\355\360\355\377\354\357\354\377\354\357\354\377" - "\355\360\355\377\355\360\355\377\356\361\356\377\357\363\357\377\360\365" - "\361\377\356\363\357\377\356\363\357\377\360\363\356\377\357\363\357\377" - "\360\363\356\377\360\363\356\377\360\363\356\377\360\363\356\377\354\357" - "\354\377\354\357\354\377\354\357\354\377\355\360\355\377\354\357\354\377" - "\355\360\355\377\355\360\355\377\356\361\356\377\360\363\356\377\356\360" - "\354\377\356\360\354\377\355\360\355\377\355\360\355\377\351\355\352\377" - "\351\356\354\377\351\355\352\377\353\357\355\377\352\356\354\377\354\357" - "\354\377\354\357\354\377\356\360\354\377\352\355\351\377\352\355\351\377" - "\353\356\352\377\353\357\354\377\354\360\355\377\352\356\353\377\354\360" - "\355\377\352\356\353\377\354\363\357\377\353\360\356\377\354\360\355\377" - "\354\357\354\377\357\361\355\377\357\361\355\377\356\361\356\377\355\360" - "\355\377\354\357\354\377\351\355\352\377\347\353\350\377\353\356\352\377" - "\354\360\355\377\355\361\356\377\355\361\356\377\354\360\355\377\355\361" - "\356\377\357\363\357\377\357\363\357\377\356\363\357\377\354\360\355\377" - "\356\363\357\377\355\361\356\377\355\361\356\377\354\360\355\377\353\356" - "\352\377\347\353\347\377\354\357\354\377\350\356\354\377\350\354\351\377" - "\352\356\353\377\351\355\352\377\352\356\353\377\354\360\355\377\352\355" - "\351\377\350\354\350\377\353\356\352\377\357\363\357\377\360\365\360\377" - "\360\363\356\377\360\361\355\377\355\357\353\377\354\356\351\377\352\355" - "\351\377\354\357\354\377\355\360\355\377\355\361\357\377\355\361\357\377" - "\355\361\357\377\356\361\356\377\354\360\355\377\353\360\356\377\354\360" - "\355\377\354\357\354\377\355\360\355\377\355\360\355\377\355\360\355\377" - "\355\360\354\377\352\356\353\377\352\356\353\377\354\357\354\377\353\357" - "\354\377\355\360\355\377\354\360\355\377\354\357\354\377\354\360\355\377" - "\354\360\355\377\351\356\354\377\351\356\354\377\347\355\353\377\347\355" - "\353\377\346\354\352\377\353\357\354\377\352\355\351\377\354\357\354\377" - "\354\357\354\377\355\360\355\377\352\356\353\377\353\356\352\377\351\356" - "\354\377\353\356\352\377\351\356\354\377\353\357\354\377\356\361\356\377" - "\356\361\356\377\353\357\354\377\354\360\355\377\357\363\357\377\356\361" - "\356\377\360\363\356\377\356\361\356\377\357\363\357\377\357\363\357\377" - "\360\365\360\377\356\361\356\377\355\360\354\377\355\361\356\377\360\363" - "\356\377\357\361\355\377\357\363\357\377\354\357\354\377\356\361\356\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\354\357\354\377\353\356" - "\352\377\354\357\354\377\356\360\354\377\355\357\353\377\355\360\355\377" - "\354\357\354\377\352\356\353\377\347\355\353\377\346\353\350\377\346\354" - "\354\377\350\354\351\377\352\356\353\377\355\360\355\377\354\357\354\377" - "\353\356\352\377\352\356\353\377\352\355\351\377\352\356\353\377\352\356" - "\353\377\353\356\352\377\354\360\355\377\353\357\354\377\353\360\356\377" - "\353\357\354\377\354\360\355\377\353\361\356\377\354\360\355\377\353\356" - "\352\377\357\363\357\377\357\363\357\377\354\357\354\377\342\345\341\377" - "\346\351\346\377\355\357\353\377\353\356\352\377\355\360\355\377\355\361" - "\356\377\355\361\356\377\354\360\355\377\355\361\356\377\356\363\357\377" - "\355\361\356\377\354\360\355\377\354\360\355\377\355\361\356\377\356\363" - "\357\377\354\360\355\377\347\351\345\377\342\345\341\377\347\353\350\377" - "\347\355\353\377\350\356\354\377\353\356\352\377\351\355\352\377\352\356" - "\353\377\355\360\355\377\354\357\354\377\352\355\351\377\355\357\353\377" - "\360\363\356\377\371\373\367\377\372\374\370\377\376\377\373\377\363\366" - "\361\377\361\365\360\377\357\361\355\377\355\360\355\377\356\361\356\377" - "\355\361\356\377\354\360\355\377\356\361\356\377\355\360\355\377\354\360" - "\355\377\350\356\355\377\352\356\353\377\354\357\354\377\355\361\356\377" - "\355\360\355\377\354\357\354\377\355\360\355\377\351\355\352\377\352\356" - "\353\377\354\357\354\377\353\356\352\377\356\360\354\377\356\361\356\377" - "\356\360\354\377\354\360\355\377\352\356\353\377\353\360\356\377\352\356" - "\353\377\354\360\355\377\352\356\353\377\352\356\353\377\352\355\351\377" - "\353\356\352\377\354\357\354\377\354\357\354\377\354\357\354\377\353\357" - "\354\377\353\356\352\377\354\357\354\377\352\355\351\377\353\356\352\377" - "\353\357\354\377\354\357\354\377\355\360\355\377\354\360\355\377\355\360" - "\355\377\356\361\356\377\357\363\357\377\355\360\355\377\357\361\355\377" - "\357\363\357\377\357\363\357\377\355\360\355\377\355\360\354\377\353\357" - "\354\377\353\357\354\377\354\360\355\377\357\363\357\377\356\361\356\377" - "\357\363\357\377\354\360\355\377\354\360\355\377\353\357\354\377\354\357" - "\354\377\353\356\352\377\352\355\351\377\352\355\351\377\352\355\351\377" - "\353\356\352\377\353\356\352\377\352\356\353\377\352\356\353\377\351\356" - "\354\377\343\351\347\377\353\357\355\377\351\355\352\377\354\357\354\377" - "\354\357\353\377\354\357\354\377\354\357\354\377\354\360\355\377\353\356" - "\352\377\352\356\353\377\353\356\352\377\352\356\353\377\350\354\351\377" - "\351\355\352\377\353\360\356\377\353\360\356\377\351\356\354\377\347\355" - "\353\377\347\355\353\377\345\351\346\377\357\363\357\377\366\371\365\377" - "\365\370\364\377\365\370\364\377\352\355\351\377\352\355\351\377\353\356" - "\352\377\352\356\353\377\354\360\355\377\353\357\354\377\354\360\355\377" - "\354\357\354\377\354\357\354\377\354\360\355\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\361\366\361\377\366\371\366\377\373\376\373\377" - "\357\363\357\377\352\356\353\377\353\357\354\377\351\357\355\377\353\357" - "\354\377\352\356\353\377\355\361\356\377\356\361\356\377\354\357\354\377" - "\353\356\352\377\347\351\345\377\356\360\354\377\366\370\363\377\367\371" - "\365\377\364\367\363\377\364\367\363\377\357\363\357\377\360\363\356\377" - "\356\360\354\377\355\360\355\377\356\361\356\377\354\357\354\377\354\357" - "\354\377\352\356\353\377\352\356\353\377\350\356\356\377\351\356\354\377" - "\352\356\353\377\353\356\352\377\353\356\352\377\353\356\352\377\354\357" - "\354\377\354\357\354\377\353\356\352\377\354\357\354\377\353\356\352\377" - "\352\355\351\377\352\356\353\377\353\356\352\377\354\357\354\377\354\360" - "\355\377\355\360\355\377\352\356\353\377\353\357\354\377\354\360\355\377" - "\351\355\352\377\352\356\353\377\353\357\354\377\353\357\354\377\354\357" - "\354\377\356\361\356\377\356\361\356\377\357\363\357\377\355\360\355\377" - "\350\354\350\377\351\355\352\377\353\356\352\377\354\357\354\377\356\361" - "\356\377\355\360\355\377\355\360\355\377\354\357\354\377\355\360\355\377" - "\356\360\354\377\357\361\355\377\356\360\354\377\355\360\355\377\354\357" - "\354\377\357\363\357\377\357\363\357\377\354\357\354\377\354\360\355\377" - "\355\360\355\377\355\360\355\377\353\357\354\377\355\361\356\377\353\357" - "\354\377\355\360\355\377\354\357\354\377\354\357\353\377\352\355\351\377" - "\351\354\350\377\351\354\350\377\354\357\354\377\353\356\352\377\357\363" - "\357\377\355\361\356\377\354\363\357\377\352\356\353\377\353\357\354\377" - "\352\355\351\377\352\355\351\377\354\357\354\377\352\356\353\377\351\355" - "\352\377\350\354\351\377\350\354\350\377\352\355\351\377\352\355\351\377" - "\353\356\352\377\354\357\354\377\353\357\354\377\354\360\355\377\352\356" - "\353\377\345\353\350\377\342\352\352\377\341\352\352\377\345\354\352\377" - "\352\357\355\377\366\371\366\377\365\370\364\377\367\371\365\377\357\361" - "\355\377\345\350\344\377\347\353\347\377\354\357\354\377\353\357\354\377" - "\354\360\355\377\353\357\354\377\355\361\356\377\356\361\356\377\354\360" - "\355\377\353\361\356\377\353\361\356\377\353\357\354\377\360\365\361\377" - "\360\365\360\377\365\370\364\377\356\363\357\377\354\360\355\377\352\356" - "\353\377\352\356\353\377\354\360\355\377\353\357\354\377\354\360\355\377" - "\355\361\356\377\356\361\356\377\350\353\347\377\351\354\350\377\347\351" - "\345\377\360\361\355\377\361\363\356\377\356\361\356\377\360\365\360\377" - "\356\361\356\377\356\360\354\377\360\363\356\377\354\357\353\377\354\357" - "\353\377\354\357\354\377\354\357\354\377\355\361\356\377\352\357\355\377" - "\352\361\360\377\354\357\354\377\354\357\354\377\352\355\351\377\350\353" - "\347\377\352\355\351\377\353\356\352\377\355\360\355\377\353\356\352\377" - "\352\355\351\377\351\355\351\377\353\356\352\377\354\357\354\377\355\360" - "\355\377\353\356\352\377\353\356\352\377\354\357\354\377\354\357\354\377" - "\354\360\355\377\354\360\355\377\352\356\353\377\352\356\353\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\355\361\356\377\355\361\356\377" - "\361\366\363\377\363\367\364\377\357\361\355\377\350\354\350\377\354\357" - "\354\377\356\361\356\377\356\361\356\377\354\357\354\377\353\356\352\377" - "\354\357\354\377\354\357\354\377\355\360\355\377\354\357\354\377\355\357" - "\353\377\355\357\353\377\355\357\353\377\354\357\354\377\356\361\356\377" - "\356\361\356\377\356\363\357\377\356\361\356\377\356\361\356\377\355\360" - "\355\377\354\360\355\377\355\361\356\377\354\357\354\377\354\357\353\377" - "\356\360\354\377\357\361\355\377\357\361\355\377\356\360\354\377\357\363" - "\357\377\356\361\356\377\356\361\356\377\355\361\356\377\355\361\356\377" - "\353\356\352\377\355\360\355\377\353\356\352\377\353\356\352\377\351\355" - "\352\377\351\355\352\377\352\355\351\377\351\355\352\377\350\354\350\377" - "\350\354\350\377\352\355\351\377\352\355\351\377\353\357\354\377\353\357" - "\354\377\353\361\356\377\354\363\357\377\352\357\355\377\344\354\355\377" - "\343\355\355\377\346\355\354\377\353\360\356\377\355\361\356\377\365\370" - "\364\377\365\370\364\377\361\366\361\377\350\354\350\377\350\354\350\377" - "\354\357\354\377\355\360\355\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\355\360\355\377\354\363\357\377\353\357\354\377\355\361\356\377" - "\355\361\356\377\356\363\357\377\356\363\357\377\360\365\360\377\356\361" - "\356\377\354\357\354\377\353\356\352\377\355\360\355\377\354\357\354\377" - "\354\360\355\377\356\361\356\377\355\360\355\377\351\354\350\377\337\342" - "\336\377\346\350\343\377\353\355\350\377\354\356\351\377\357\361\355\377" - "\356\360\354\377\354\360\355\377\356\361\356\377\357\361\355\377\354\357" - "\354\377\355\360\355\377\354\357\353\377\355\360\355\377\356\361\356\377" - "\353\357\354\377\354\357\354\377\357\363\357\377\356\363\357\377\354\357" - "\354\377\346\351\346\377\337\342\336\377\352\355\351\377\355\357\353\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\352\356\353\377\353\356" - "\352\377\354\357\354\377\353\356\352\377\353\356\352\377\353\356\352\377" - "\353\356\352\377\354\357\354\377\352\356\353\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\352\357\355\377\347\355\353\377\353\357\354\377" - "\351\356\354\377\353\360\356\377\356\365\361\377\360\365\360\377\360\365" - "\360\377\361\365\360\377\360\363\356\377\356\360\354\377\356\361\356\377" - "\354\357\354\377\354\357\354\377\355\357\353\377\355\360\355\377\355\360" - "\354\377\356\361\356\377\357\361\355\377\357\361\355\377\355\360\354\377" - "\354\357\354\377\355\360\355\377\356\361\356\377\354\360\355\377\356\361" - "\356\377\356\363\357\377\354\360\355\377\356\361\356\377\354\357\354\377" - "\356\360\354\377\355\360\355\377\357\363\357\377\356\361\356\377\361\366" - "\363\377\357\363\357\377\355\361\356\377\357\363\357\377\356\361\356\377" - "\354\357\354\377\354\360\355\377\355\360\355\377\354\357\354\377\353\356" - "\352\377\353\356\352\377\352\355\351\377\352\356\353\377\352\355\351\377" - "\353\356\352\377\354\357\354\377\352\355\351\377\351\354\350\377\350\354" - "\350\377\352\356\353\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\352\356\353\377\350\355\353\377\352\357\355\377\353\360\356\377\354\363" - "\357\377\352\357\355\377\363\367\364\377\365\370\364\377\365\370\364\377" - "\356\361\356\377\356\361\356\377\354\357\354\377\355\360\355\377\352\356" - "\353\377\353\357\354\377\350\355\353\377\347\355\354\377\353\357\354\377" - "\354\360\355\377\356\361\356\377\355\360\355\377\354\357\354\377\355\361" - "\356\377\355\360\355\377\356\360\354\377\355\357\353\377\357\363\357\377" - "\353\356\352\377\354\357\354\377\355\360\355\377\361\366\361\377\365\370" - "\364\377\365\370\364\377\367\371\365\377\355\357\353\377\352\354\347\377" - "\351\354\350\377\354\357\354\377\354\357\353\377\354\360\355\377\354\357" - "\353\377\356\361\356\377\354\357\354\377\356\361\356\377\356\361\356\377" - "\356\361\356\377\355\360\355\377\354\363\357\377\353\360\356\377\355\361" - "\356\377\370\373\370\377\370\373\370\377\365\370\364\377\355\360\355\377" - "\357\363\357\377\357\361\355\377\356\361\356\377\356\361\356\377\355\360" - "\355\377\355\361\356\377\354\360\355\377\354\360\355\377\356\360\354\377" - "\355\361\356\377\351\355\352\377\352\356\353\377\352\355\351\377\352\356" - "\353\377\353\356\352\377\354\357\354\377\354\360\355\377\353\356\352\377" - "\352\356\353\377\353\357\354\377\352\357\355\377\353\360\356\377\351\356" - "\354\377\361\366\363\377\356\363\357\377\357\361\355\377\360\363\356\377" - "\360\363\356\377\357\363\357\377\355\360\355\377\355\357\353\377\351\354" - "\350\377\353\356\352\377\354\357\353\377\357\361\355\377\360\363\356\377" - "\360\363\356\377\357\363\357\377\357\363\357\377\356\361\356\377\355\360" - "\355\377\360\365\360\377\357\363\357\377\356\361\356\377\356\363\357\377" - "\355\361\356\377\357\363\357\377\357\363\357\377\360\365\361\377\356\363" - "\357\377\355\361\356\377\355\361\356\377\354\360\355\377\357\363\357\377" - "\357\363\357\377\353\357\354\377\353\357\354\377\354\357\354\377\354\357" - "\354\377\355\360\355\377\354\357\354\377\355\360\355\377\355\360\355\377" - "\352\356\353\377\353\356\352\377\354\357\354\377\354\357\354\377\355\360" - "\355\377\355\360\355\377\351\355\352\377\352\356\353\377\354\360\355\377" - "\355\360\355\377\353\357\354\377\353\356\352\377\353\357\354\377\353\357" - "\354\377\352\356\353\377\355\361\356\377\354\360\355\377\355\361\356\377" - "\360\365\361\377\363\367\364\377\361\366\361\377\357\361\355\377\356\360" - "\354\377\354\357\354\377\353\357\354\377\351\357\355\377\350\355\353\377" - "\345\353\352\377\351\356\354\377\354\357\354\377\356\360\354\377\355\357" - "\353\377\353\356\352\377\355\361\356\377\357\363\357\377\355\360\355\377" - "\356\360\354\377\356\361\355\377\355\360\354\377\355\360\355\377\360\365" - "\360\377\361\366\361\377\364\367\363\377\365\370\364\377\365\370\364\377" - "\363\365\357\377\363\365\357\377\360\363\356\377\355\357\353\377\355\357" - "\353\377\354\357\354\377\355\360\355\377\354\360\355\377\353\356\352\377" - "\354\357\354\377\355\360\355\377\355\360\355\377\354\360\355\377\353\360" - "\356\377\352\357\355\377\355\361\356\377\364\367\363\377\372\376\372\377" - "\367\372\367\377\361\366\363\377\355\360\355\377\356\360\354\377\356\361" - "\356\377\357\363\357\377\356\361\356\377\355\361\356\377\353\357\354\377" - "\354\360\355\377\353\357\354\377\352\355\351\377\350\354\351\377\347\353" - "\350\377\354\357\354\377\351\355\352\377\353\357\354\377\355\360\355\377" - "\353\356\352\377\354\360\355\377\353\357\354\377\354\357\354\377\354\360" - "\355\377\353\357\354\377\354\360\355\377\355\361\356\377\354\363\357\377" - "\354\363\357\377\360\361\355\377\363\365\357\377\363\366\361\377\353\356" - "\352\377\344\346\342\377\335\337\333\377\350\353\347\377\355\357\353\377" - "\355\357\353\377\356\361\356\377\354\357\354\377\355\361\356\377\354\360" - "\355\377\355\360\355\377\356\361\356\377\355\360\355\377\355\361\356\377" - "\353\357\354\377\353\357\354\377\354\357\354\377\355\360\355\377\357\363" - "\357\377\354\363\357\377\353\360\356\377\355\360\355\377\356\361\356\377" - "\355\361\356\377\354\360\355\377\354\360\355\377\355\361\356\377\353\357" - "\354\377\354\360\355\377\352\356\353\377\355\360\355\377\355\360\355\377" - "\355\360\355\377\357\361\355\377\353\356\352\377\355\360\354\377\354\357" - "\354\377\354\357\354\377\355\360\355\377\355\360\355\377\353\356\352\377" - "\352\356\353\377\355\361\356\377\354\360\355\377\355\361\356\377\355\360" - "\355\377\354\357\354\377\353\357\354\377\352\356\353\377\353\357\354\377" - "\353\357\354\377\355\361\356\377\355\361\356\377\357\363\357\377\356\361" - "\356\377\356\360\354\377\352\355\351\377\352\355\351\377\352\355\351\377" - "\354\357\354\377\350\354\351\377\350\355\353\377\351\356\354\377\353\357" - "\354\377\355\360\355\377\354\357\354\377\353\357\354\377\353\357\354\377" - "\351\357\355\377\355\361\356\377\356\361\356\377\357\363\357\377\357\363" - "\357\377\360\365\360\377\361\365\360\377\361\366\361\377\360\365\360\377" - "\360\365\360\377\357\361\355\377\360\363\356\377\363\366\361\377\361\365" - "\360\377\360\365\360\377\353\357\354\377\352\356\353\377\352\357\355\377" - "\354\357\354\377\355\360\355\377\355\360\355\377\354\360\355\377\355\360" - "\355\377\354\360\355\377\351\356\354\377\351\356\354\377\353\360\356\377" - "\356\363\357\377\360\365\361\377\364\370\365\377\360\365\360\377\356\361" - "\356\377\356\360\354\377\356\360\354\377\354\357\354\377\356\361\356\377" - "\357\363\357\377\354\360\355\377\354\360\355\377\356\363\357\377\353\356" - "\352\377\352\355\351\377\353\356\352\377\353\356\352\377\353\356\352\377" - "\355\360\355\377\353\356\352\377\353\357\354\377\354\360\355\377\353\357" - "\354\377\353\357\354\377\354\360\355\377\354\357\354\377\355\360\355\377" - "\356\361\356\377\352\357\355\377\354\363\357\377\357\361\355\377\363\366" - "\361\377\364\367\363\377\375\377\373\377\373\376\372\377\363\366\361\377" - "\355\360\355\377\357\363\357\377\356\361\356\377\357\361\355\377\355\361" - "\356\377\352\357\355\377\353\360\356\377\354\363\357\377\353\357\354\377" - "\356\361\356\377\356\361\356\377\353\357\354\377\353\357\354\377\353\357" - "\354\377\355\361\356\377\352\357\355\377\352\357\355\377\353\360\357\377" - "\355\360\355\377\354\360\355\377\353\357\354\377\355\361\356\377\356\363" - "\357\377\355\361\356\377\355\361\356\377\353\360\356\377\352\357\355\377" - "\351\356\354\377\353\357\354\377\353\357\354\377\353\357\354\377\353\356" - "\352\377\354\357\354\377\356\361\356\377\353\357\354\377\353\357\354\377" - "\353\357\354\377\354\357\354\377\353\357\354\377\355\361\356\377\354\357" - "\354\377\354\357\353\377\355\360\355\377\354\357\354\377\354\357\354\377" - "\352\356\353\377\352\356\353\377\354\360\355\377\352\356\353\377\354\360" - "\355\377\360\365\360\377\352\355\351\377\352\355\351\377\351\354\350\377" - "\352\355\351\377\352\355\351\377\352\355\351\377\352\355\351\377\351\355" - "\352\377\352\356\353\377\353\356\352\377\354\357\354\377\354\357\354\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\351\357\355\377\355\360" - "\355\377\357\363\357\377\357\363\357\377\355\361\356\377\357\363\357\377" - "\361\366\361\377\360\363\356\377\357\361\355\377\353\355\350\377\356\360" - "\354\377\357\361\355\377\360\363\356\377\361\365\360\377\354\360\355\377" - "\353\357\354\377\352\357\355\377\353\356\352\377\354\357\354\377\353\356" - "\352\377\352\356\353\377\353\357\354\377\350\356\355\377\347\356\355\377" - "\350\360\356\377\352\357\355\377\353\360\356\377\354\360\355\377\353\360" - "\356\377\360\365\361\377\357\363\357\377\355\360\355\377\360\363\356\377" - "\357\361\355\377\356\360\354\377\356\360\354\377\356\361\356\377\360\363" - "\356\377\364\367\363\377\365\370\364\377\364\366\360\377\360\363\356\377" - "\356\361\356\377\355\360\355\377\354\357\354\377\353\357\354\377\354\360" - "\355\377\353\357\354\377\354\360\355\377\353\357\354\377\354\360\355\377" - "\353\356\352\377\355\360\355\377\355\360\355\377\355\360\355\377\353\357" - "\354\377\354\357\354\377\360\363\356\377\363\366\361\377\364\367\363\377" - "\370\373\370\377\365\370\364\377\360\365\360\377\356\361\356\377\353\356" - "\352\377\355\360\355\377\354\360\355\377\354\360\355\377\352\357\355\377" - "\354\363\357\377\353\357\354\377\355\361\356\377\356\361\356\377\355\360" - "\354\377\355\360\355\377\354\360\355\377\354\360\355\377\352\357\355\377" - "\351\355\352\377\354\357\354\377\353\356\353\377\353\357\354\377\353\357" - "\354\377\354\360\355\377\355\361\356\377\355\361\356\377\354\360\355\377" - "\354\363\357\377\353\360\356\377\354\360\355\377\352\356\353\377\353\357" - "\354\377\354\357\354\377\354\357\354\377\354\357\354\377\354\357\354\377" - "\353\357\354\377\352\356\353\377\353\357\354\377\355\360\355\377\355\361" - "\357\377\355\361\357\377\353\360\356\377\353\357\354\377\353\356\352\377" - "\355\360\355\377\351\355\352\377\352\356\353\377\352\356\353\377\353\356" - "\352\377\354\360\355\377\355\360\355\377\356\360\354\377\352\354\347\377" - "\353\356\352\377\352\355\351\377\354\357\354\377\352\355\351\377\353\356" - "\352\377\352\355\351\377\353\356\352\377\353\356\352\377\353\356\352\377" - "\352\356\353\377\352\356\353\377\352\356\353\377\353\360\356\377\354\363" - "\357\377\354\360\355\377\353\357\354\377\354\360\355\377\353\360\356\377" - "\355\361\356\377\356\363\357\377\361\365\360\377\360\363\356\377\355\357" - "\353\377\355\357\353\377\356\360\354\377\360\363\356\377\357\363\357\377" - "\360\365\360\377\355\361\356\377\350\356\354\377\353\357\354\377\355\357" - "\353\377\354\357\354\377\353\357\354\377\353\357\354\377\350\355\353\377" - "\347\355\354\377\346\355\354\377\346\357\356\377\351\356\354\377\351\356" - "\354\377\355\361\356\377\354\363\357\377\360\365\360\377\357\363\357\377" - "\357\363\357\377\355\360\355\377\356\361\356\377\355\360\355\377\355\360" - "\355\377\357\363\357\377\361\365\360\377\361\365\360\377\363\366\361\377" - "\364\366\360\377\360\363\356\377\357\363\357\377\355\360\355\377\354\357" - "\354\377\351\355\352\377\352\357\355\377\353\357\354\377\352\356\353\377" - "\352\356\353\377\355\360\355\377\355\360\355\377\355\360\355\377\354\360" - "\355\377\356\361\356\377\354\357\354\377\354\357\354\377\357\361\355\377" - "\360\363\356\377\364\370\365\377\367\373\370\377\361\366\363\377\355\360" - "\355\377\355\360\355\377\354\357\354\377\354\360\355\377\353\357\354\377" - "\354\360\355\377\354\360\355\377\354\360\355\377\353\357\354\377\357\361" - "\355\377\355\357\353\377\355\360\355\377\354\357\354\377\354\357\354\377" - "\354\360\355\377\355\360\355\377\355\360\355\377\352\356\354\377\354\360" - "\356\377\352\356\353\377\354\357\354\377\355\360\355\377\355\361\356\377" - "\353\357\354\377\354\363\357\377\354\360\355\377\354\360\355\377\353\357" - "\354\377\353\357\354\377\354\357\354\377\354\357\354\377\355\360\355\377" - "\356\361\356\377\355\360\355\377\355\360\355\377\352\356\353\377\353\357" - "\354\377\354\360\355\377\352\356\353\377\356\361\356\377\353\357\354\377" - "\352\356\353\377\352\356\353\377\355\360\355\377\351\355\352\377\352\356" - "\353\377\352\356\353\377\354\357\354\377\354\360\355\377\354\360\355\377" - "\355\360\355\377\354\357\353\377\353\356\352\377\352\356\353\377\353\357" - "\354\377\355\364\360\377\354\360\355\377\355\360\355\377\354\357\354\377" - "\353\356\352\377\353\356\352\377\352\356\353\377\352\356\353\377\353\357" - "\354\377\353\361\356\377\352\356\353\377\355\361\356\377\352\356\353\377" - "\355\361\356\377\354\363\357\377\355\361\356\377\360\365\361\377\361\366" - "\361\377\363\366\361\377\357\361\355\377\357\361\355\377\356\360\354\377" - "\356\360\354\377\356\361\356\377\355\360\355\377\356\361\356\377\353\356" - "\352\377\353\356\352\377\353\356\352\377\352\355\351\377\351\355\352\377" - "\351\355\352\377\350\355\353\377\350\355\353\377\351\356\354\377\353\360" - "\356\377\347\355\353\377\354\360\355\377\354\363\357\377\354\363\357\377" - "\356\361\356\377\357\363\357\377\355\360\355\377\354\357\354\377\354\357" - "\354\377\356\361\356\377\356\361\356\377\357\361\355\377\357\361\355\377" - "\357\361\355\377\360\363\356\377\357\363\357\377\357\363\357\377\356\361" - "\356\377\356\361\356\377\354\360\355\377\352\355\351\377\347\353\350\377" - "\350\354\350\377\352\356\353\377\352\356\353\377\354\357\354\377\352\356" - "\353\377\354\357\354\377\355\360\355\377\352\356\353\377\354\357\354\377" - "\356\361\356\377\357\363\357\377\353\357\354\377\356\360\354\377\356\361" - "\356\377\353\357\354\377\355\361\356\377\355\360\355\377\356\360\354\377" - "\354\357\354\377\355\360\355\377\357\361\355\377\356\361\356\377\354\360" - "\355\377\353\357\354\377\353\363\361\377\356\361\356\377\355\360\355\377" - "\354\357\354\377\354\360\355\377\353\357\354\377\353\357\354\377\354\357" - "\354\377\352\356\353\377\351\356\354\377\353\360\356\377\355\360\355\377" - "\354\360\355\377\353\357\354\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\354\360\355\377\352\356\353\377\353\356\352\377" - "\353\356\352\377\355\357\353\377\356\361\356\377\356\361\356\377\356\361" - "\356\377\353\357\354\377\352\356\353\377\352\357\355\377\353\357\354\377" - "\355\361\356\377\354\360\355\377\352\357\355\377\353\360\356\377\355\360" - "\355\377\351\356\354\377\351\355\352\377\351\355\352\377\352\355\351\377" - "\353\356\352\377\353\356\352\377\353\356\352\377\353\356\352\377\355\360" - "\355\377\351\357\355\377\350\356\354\377\353\361\356\377\353\361\356\377" - "\353\357\354\377\353\357\354\377\354\360\355\377\355\360\355\377\355\360" - "\355\377\353\357\354\377\351\357\355\377\352\356\353\377\352\357\355\377" - "\353\357\354\377\353\357\354\377\356\361\356\377\355\360\355\377\355\360" - "\355\377\356\361\356\377\363\366\361\377\361\365\360\377\361\365\360\377" - "\357\363\357\377\355\360\355\377\355\360\355\377\356\361\356\377\354\360" - "\355\377\354\360\355\377\355\361\356\377\354\357\354\377\353\356\352\377" - "\353\356\352\377\354\357\354\377\354\357\354\377\354\357\354\377\355\360" - "\355\377\354\357\354\377\352\356\353\377\354\360\355\377\353\357\354\377" - "\354\363\357\377\354\363\357\377\357\363\357\377\356\361\356\377\356\363" - "\357\377\354\360\355\377\354\357\354\377\355\360\355\377\355\360\355\377" - "\356\361\356\377\360\363\356\377\354\357\354\377\356\361\356\377\355\360" - "\355\377\357\363\357\377\355\360\355\377\356\361\356\377\360\365\360\377" - "\360\365\360\377\360\366\364\377\356\361\356\377\355\360\355\377\351\354" - "\350\377\344\346\342\377\352\355\351\377\354\357\354\377\355\360\355\377" - "\355\360\355\377\356\361\356\377\355\360\355\377\357\361\355\377\355\361" - "\356\377\355\361\356\377\354\360\355\377\354\360\355\377\355\360\355\377" - "\353\357\354\377\355\360\355\377\355\360\355\377\356\360\354\377\357\361" - "\355\377\355\360\355\377\356\361\356\377\353\357\354\377\355\361\356\377" - "\353\357\354\377\354\357\354\377\355\361\356\377\353\357\354\377\354\360" - "\355\377\355\360\355\377\353\356\352\377\353\357\354\377\351\356\354\377" - "\352\356\353\377\352\356\353\377\352\356\353\377\353\360\356\377\353\357" - "\354\377\352\356\353\377\354\360\355\377\352\357\355\377\352\356\353\377" - "\354\357\354\377\355\360\354\377\356\360\354\377\357\361\355\377\355\357" - "\353\377\355\357\353\377\355\357\353\377\350\354\351\377\352\356\353\377" - "\352\357\355\377\354\360\355\377\353\360\356\377\352\356\353\377\354\360" - "\355\377\354\360\355\377\355\360\355\377\352\356\353\377\352\357\355\377" - "\352\356\353\377\354\360\355\377\350\355\353\377\347\355\354\377\353\363" - "\361\377\353\357\354\377\352\356\353\377\354\363\357\377\354\363\357\377" - "\356\361\356\377\355\361\356\377\355\361\356\377\353\360\356\377\355\361" - "\356\377\356\361\356\377\355\360\355\377\354\360\355\377\352\356\353\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\354\360\355\377\356\361" - "\356\377\356\361\356\377\356\363\357\377\356\363\357\377\356\361\356\377" - "\356\361\356\377\360\365\360\377\355\360\355\377\353\356\352\377\354\357" - "\354\377\354\357\354\377\354\360\355\377\354\357\354\377\356\361\356\377" - "\355\360\355\377\356\361\356\377\357\361\355\377\356\361\356\377\355\360" - "\355\377\355\360\355\377\355\360\355\377\350\353\347\377\347\353\347\377" - "\353\357\354\377\353\357\354\377\354\363\357\377\356\363\357\377\355\360" - "\355\377\355\360\355\377\355\360\355\377\354\360\355\377\356\361\356\377" - "\354\357\354\377\355\360\355\377\356\361\356\377\356\361\356\377\355\361" - "\356\377\355\360\355\377\355\360\355\377\357\363\357\377\355\360\355\377" - "\356\363\357\377\356\363\357\377\364\370\365\377\357\363\357\377\360\365" - "\360\377\367\372\367\377\356\361\356\377\352\355\351\377\352\355\351\377" - "\352\356\353\377\355\360\355\377\356\361\356\377\355\360\355\377\355\361" - "\356\377\353\356\352\377\353\357\354\377\351\355\352\377\354\360\355\377" - "\352\356\353\377\353\356\352\377\350\355\353\377\352\356\353\377\352\356" - "\353\377\355\357\353\377\356\360\354\377\355\360\355\377\355\360\355\377" - "\355\360\355\377\355\361\356\377\354\360\355\377\353\356\352\377\353\356" - "\352\377\355\357\353\377\353\356\352\377\353\356\352\377\353\356\352\377" - "\360\365\360\377\347\355\353\377\351\355\352\377\351\355\352\377\351\355" - "\352\377\352\357\355\377\352\356\353\377\354\360\355\377\350\356\354\377" - "\347\355\353\377\351\356\354\377\351\356\354\377\356\361\356\377\356\360" - "\354\377\356\361\356\377\357\361\355\377\353\355\350\377\351\354\350\377" - "\353\356\352\377\354\360\355\377\353\357\354\377\354\357\354\377\355\360" - "\355\377\355\360\355\377\355\361\356\377\355\361\356\377\354\363\357\377" - "\354\363\357\377\351\356\354\377\353\360\356\377\353\357\354\377\350\360" - "\356\377\353\360\356\377\352\357\355\377\354\363\357\377\356\363\357\377" - "\354\363\357\377\354\363\357\377\355\360\355\377\355\361\357\377\352\357" - "\355\377\354\363\357\377\352\357\355\377\354\357\354\377\356\361\356\377" - "\355\360\355\377\353\357\354\377\353\357\354\377\354\360\355\377\354\357" - "\354\377\355\360\355\377\356\361\356\377\356\363\357\377\360\365\361\377" - "\357\363\357\377\360\365\360\377\361\366\361\377\360\365\361\377\357\363" - "\357\377\350\354\351\377\355\360\355\377\356\363\357\377\356\361\356\377" - "\354\357\354\377\353\356\352\377\354\357\354\377\356\361\356\377\357\363" - "\357\377\355\361\356\377\356\363\357\377\360\365\360\377\364\367\363\377" - "\361\366\361\377\356\361\356\377\355\361\356\377\353\357\354\377\354\360" - "\355\377\356\363\357\377\357\363\357\377\357\363\357\377\355\360\355\377" - "\355\360\355\377\354\357\354\377\353\356\352\377\355\357\353\377\356\361" - "\356\377\354\357\354\377\353\357\354\377\353\356\352\377\356\361\356\377" - "\357\363\357\377\356\360\354\377\355\360\355\377\355\360\355\377\356\363" - "\357\377\361\366\361\377\361\366\361\377\366\371\366\377\373\376\373\377" - "\364\370\365\377\356\361\356\377\352\357\355\377\352\357\355\377\353\356" - "\352\377\353\356\352\377\354\360\355\377\355\360\355\377\356\360\354\377" - "\352\356\353\377\353\356\352\377\351\356\354\377\352\357\355\377\354\363" - "\357\377\354\363\357\377\354\360\355\377\355\360\355\377\355\360\355\377" - "\354\357\354\377\354\357\354\377\353\356\352\377\356\361\356\377\356\363" - "\357\377\354\357\354\377\356\361\356\377\357\363\357\377\360\363\356\377" - "\356\360\354\377\353\356\352\377\355\360\355\377\356\363\357\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\353\357\354\377\351\356\354\377" - "\351\356\354\377\352\357\355\377\351\356\354\377\351\355\352\377\351\355" - "\352\377\351\355\351\377\354\357\354\377\357\363\357\377\365\370\364\377" - "\366\371\366\377\361\366\361\377\356\361\356\377\360\363\356\377\357\361" - "\355\377\357\363\357\377\356\361\356\377\352\356\353\377\355\361\356\377" - "\352\356\353\377\352\357\355\377\352\357\355\377\352\357\355\377\355\361" - "\356\377\354\360\355\377\353\360\356\377\354\363\357\377\352\357\355\377" - "\355\361\356\377\354\360\355\377\354\363\357\377\354\363\357\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\352\357\355\377\353\360\356\377" - "\356\361\356\377\355\360\355\377\356\361\356\377\355\361\356\377\354\360" - "\355\377\355\361\356\377\357\363\357\377\356\360\354\377\355\360\355\377" - "\356\363\357\377\355\361\356\377\355\361\356\377\357\363\357\377\355\361" - "\356\377\360\365\361\377\361\366\363\377\357\363\357\377\355\360\355\377" - "\357\363\357\377\356\361\356\377\357\363\357\377\354\357\354\377\354\357" - "\354\377\354\360\355\377\354\360\355\377\355\361\356\377\353\360\356\377" - "\356\363\357\377\360\365\360\377\364\370\365\377\356\363\357\377\356\361" - "\356\377\354\363\357\377\353\357\354\377\356\363\357\377\357\363\357\377" - "\360\365\360\377\360\365\360\377\357\363\357\377\360\363\356\377\355\360" - "\355\377\354\357\354\377\353\357\354\377\356\361\356\377\353\357\354\377" - "\355\361\356\377\355\360\355\377\357\361\355\377\356\360\354\377\355\357" - "\353\377\355\360\355\377\356\361\356\377\361\366\363\377\356\363\357\377" - "\356\365\361\377\356\365\361\377\355\364\360\377\353\361\356\377\347\355" - "\353\377\351\356\354\377\353\357\354\377\353\357\354\377\352\356\353\377" - "\354\357\354\377\356\360\354\377\356\360\354\377\354\357\354\377\353\357" - "\354\377\353\356\352\377\355\361\356\377\354\360\355\377\355\361\356\377" - "\355\361\356\377\356\361\356\377\354\357\354\377\354\357\354\377\353\356" - "\352\377\350\356\354\377\352\356\353\377\355\360\355\377\360\363\356\377" - "\360\365\360\377\361\365\360\377\361\366\361\377\360\365\360\377\356\363" - "\357\377\354\357\354\377\354\360\355\377\353\356\353\377\354\360\355\377" - "\352\355\351\377\352\357\355\377\347\355\353\377\347\355\353\377\352\356" - "\353\377\352\356\353\377\351\355\352\377\347\354\351\377\346\353\351\377" - "\353\357\354\377\363\367\363\377\363\367\364\377\364\367\363\377\360\365" - "\360\377\363\366\361\377\357\361\355\377\355\360\355\377\356\361\356\377" - "\353\357\354\377\353\360\356\377\354\360\355\377\353\357\354\377\353\357" - "\354\377\355\360\355\377\354\360\355\377\355\361\356\377\353\357\354\377" - "\354\360\355\377\353\361\356\377\354\363\357\377\355\361\356\377\354\360" - "\355\377\354\360\355\377\355\361\356\377\354\360\355\377\355\361\356\377" - "\353\357\354\377\353\357\354\377\356\361\356\377\356\361\356\377\355\360" - "\355\377\355\360\355\377\357\363\357\377\355\361\356\377\356\363\357\377" - "\357\363\357\377\356\361\356\377\357\363\357\377\355\361\356\377\355\361" - "\356\377\357\365\363\377\360\365\361\377\355\364\360\377\360\365\361\377" - "\356\363\357\377\360\363\356\377\357\363\357\377\357\363\357\377\357\363" - "\357\377\354\357\354\377\355\357\353\377\355\361\356\377\355\361\356\377" - "\355\361\356\377\355\364\360\377\356\363\357\377\361\366\363\377\356\363" - "\357\377\355\364\360\377\353\360\356\377\351\356\354\377\352\357\355\377" - "\356\363\357\377\354\360\355\377\355\361\356\377\360\363\356\377\360\363" - "\356\377\361\363\356\377\355\360\355\377\354\357\354\377\354\360\355\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\353\357\354\377\356\361" - "\356\377\357\361\355\377\355\360\355\377\355\360\355\377\356\361\356\377" - "\354\360\355\377\355\364\360\377\354\363\357\377\355\364\360\377\360\365" - "\361\377\354\360\355\377\354\357\354\377\352\355\351\377\352\356\353\377" - "\352\356\353\377\354\357\354\377\354\357\354\377\356\360\354\377\355\357" - "\353\377\356\361\356\377\356\361\356\377\356\361\356\377\354\360\355\377" - "\355\360\355\377\353\357\354\377\355\360\355\377\355\360\355\377\353\356" - "\352\377\351\355\352\377\352\356\353\377\354\360\355\377\353\357\354\377" - "\356\361\356\377\356\361\356\377\360\365\360\377\357\363\357\377\357\363" - "\357\377\357\363\357\377\356\363\357\377\356\361\356\377\355\360\355\377" - "\353\356\352\377\353\356\352\377\354\357\354\377\351\356\354\377\351\356" - "\354\377\351\356\354\377\352\355\351\377\353\357\354\377\353\356\352\377" - "\347\353\347\377\346\354\353\377\345\353\352\377\361\366\361\377\361\366" - "\361\377\360\365\361\377\357\363\357\377\357\363\357\377\355\360\355\377" - "\355\360\355\377\356\363\357\377\352\357\355\377\353\360\356\377\353\357" - "\354\377\353\357\354\377\354\360\355\377\354\360\355\377\355\361\356\377" - "\354\360\355\377\354\360\355\377\354\360\355\377\353\360\356\377\353\357" - "\354\377\354\360\355\377\354\360\355\377\353\357\354\377\355\361\356\377" - "\354\357\354\377\353\357\354\377\354\357\354\377\354\360\355\377\354\357" - "\354\377\355\360\355\377\355\360\355\377\354\357\354\377\356\361\356\377" - "\357\363\357\377\355\361\356\377\357\363\357\377\357\363\357\377\357\363" - "\357\377\356\363\357\377\353\360\356\377\360\365\361\377\356\361\356\377" - "\360\365\361\377\356\363\357\377\356\363\357\377\356\361\356\377\356\361" - "\356\377\357\363\357\377\360\363\356\377\360\363\356\377\357\361\355\377" - "\354\357\354\377\355\361\356\377\353\360\356\377\353\360\356\377\353\360" - "\356\377\353\360\356\377\355\361\356\377\355\361\356\377\345\353\351\377" - "\346\353\351\377\345\353\350\377\351\356\354\377\356\361\356\377\354\360" - "\355\377\355\360\355\377\360\361\355\377\361\363\356\377\356\361\356\377" - "\354\360\355\377\350\360\356\377\351\356\354\377\352\357\355\377\351\356" - "\354\377\351\356\354\377\352\357\355\377\354\357\354\377\354\357\354\377" - "\353\356\352\377\354\357\354\377\355\361\356\377\356\363\357\377\353\360" - "\356\377\354\363\357\377\355\364\360\377\357\363\357\377\354\357\354\377" - "\352\355\351\377\353\356\352\377\351\355\352\377\352\355\351\377\354\357" - "\354\377\354\357\354\377\354\360\355\377\354\357\354\377\355\361\356\377" - "\354\360\355\377\353\357\354\377\354\357\354\377\353\356\352\377\356\361" - "\356\377\355\360\355\377\353\356\352\377\351\355\352\377\352\356\353\377" - "\353\357\354\377\353\357\354\377\356\360\354\377\356\361\356\377\357\363" - "\357\377\356\361\356\377\355\361\356\377\355\361\356\377\356\361\356\377" - "\353\357\354\377\354\360\355\377\355\360\355\377\354\357\353\377\355\360" - "\355\377\347\355\353\377\347\355\353\377\347\355\353\377\352\356\353\377" - "\352\355\351\377\353\356\352\377\353\356\352\377\352\355\351\377\354\357" - "\354\377\357\361\355\377\357\361\355\377\354\360\355\377\355\361\356\377" - "\356\361\356\377\355\360\355\377\351\355\352\377\354\360\355\377\347\355" - "\353\377\352\356\353\377\353\357\354\377\355\360\355\377\355\360\355\377" - "\355\361\356\377\353\356\352\377\352\356\353\377\351\357\355\377\347\355" - "\353\377\352\356\353\377\352\357\355\377\355\361\356\377\355\361\356\377" - "\355\361\356\377\354\357\354\377\354\357\354\377\355\361\356\377\352\356" - "\353\377\355\361\356\377\354\360\355\377\355\360\355\377\356\361\356\377" - "\356\361\356\377\355\360\355\377\356\361\356\377\355\360\355\377\356\361" - "\356\377\357\363\357\377\356\363\357\377\355\361\356\377\356\365\361\377" - "\357\363\357\377\356\361\356\377\355\361\356\377\356\363\357\377\361\366" - "\363\377\361\366\361\377\353\356\352\377\347\353\350\377\356\360\354\377" - "\356\360\354\377\357\361\355\377\356\361\356\377\352\356\353\377\352\357" - "\355\377\353\360\356\377\352\357\355\377\354\360\355\377\354\360\355\377" - "\351\355\352\377\350\354\351\377\345\351\346\377\351\355\352\377\351\356" - "\354\377\352\355\351\377\353\356\352\377\346\351\346\377\350\354\350\377" - "\360\361\355\377\352\356\353\377\352\356\353\377\352\356\353\377\347\355" - "\353\377\351\356\354\377\352\357\355\377\351\356\354\377\353\360\356\377" - "\351\355\352\377\354\357\354\377\355\360\355\377\352\356\353\377\351\356" - "\354\377\351\356\354\377\347\355\353\377\353\360\356\377\357\363\357\377" - "\357\363\357\377\360\363\356\377\356\361\356\377\352\355\351\377\352\355" - "\351\377\353\356\352\377\352\355\351\377\352\356\353\377\351\357\355\377" - "\351\357\355\377\353\357\354\377\356\361\356\377\355\360\355\377\354\357" - "\354\377\351\355\352\377\354\357\354\377\356\361\356\377\355\360\355\377" - "\355\360\355\377\353\356\352\377\355\360\355\377\355\360\355\377\357\361" - "\355\377\357\361\355\377\356\361\356\377\356\363\357\377\351\355\352\377" - "\353\357\354\377\354\357\354\377\353\356\352\377\353\356\352\377\353\356" - "\352\377\354\357\354\377\353\357\354\377\346\354\352\377\346\353\351\377" - "\345\353\350\377\351\355\352\377\351\355\352\377\352\355\351\377\353\356" - "\352\377\354\357\354\377\356\361\356\377\355\360\355\377\356\361\356\377" - "\356\363\357\377\355\361\356\377\356\363\357\377\353\357\354\377\352\357" - "\355\377\352\357\355\377\350\354\351\377\353\356\352\377\354\357\354\377" - "\353\357\354\377\353\357\354\377\354\360\355\377\353\357\354\377\353\356" - "\352\377\353\361\356\377\352\357\355\377\355\361\356\377\353\360\356\377" - "\353\360\356\377\354\363\357\377\354\360\355\377\354\363\357\377\354\357" - "\354\377\354\360\355\377\351\357\355\377\353\361\356\377\354\360\355\377" - "\355\361\356\377\354\360\355\377\352\357\355\377\353\357\354\377\356\363" - "\357\377\356\361\356\377\356\361\356\377\355\360\355\377\353\360\356\377" - "\355\360\355\377\356\363\357\377\356\361\356\377\357\363\357\377\356\363" - "\357\377\361\366\363\377\361\366\361\377\366\371\366\377\364\367\363\377" - "\355\361\356\377\354\357\354\377\356\361\356\377\355\357\353\377\353\356" - "\352\377\356\360\354\377\353\357\354\377\354\360\355\377\355\361\356\377" - "\355\360\355\377\355\360\355\377\355\360\355\377\356\363\357\377\355\360" - "\355\377\355\360\355\377\355\361\356\377\355\360\355\377\352\356\353\377" - "\353\357\354\377\355\361\356\377\352\356\353\377\351\356\354\377\353\360" - "\356\377\354\357\354\377\353\360\356\377\352\356\353\377\353\357\354\377" - "\352\356\353\377\353\357\354\377\350\354\351\377\347\353\347\377\357\363" - "\357\377\354\357\354\377\352\357\355\377\350\360\356\377\350\360\356\377" - "\355\360\355\377\357\361\355\377\357\361\355\377\360\363\356\377\357\363" - "\357\377\355\357\353\377\352\355\351\377\353\356\352\377\355\360\355\377" - "\352\356\353\377\352\356\353\377\352\357\355\377\354\357\354\377\355\360" - "\355\377\356\361\356\377\355\361\356\377\355\361\356\377\355\360\355\377" - "\356\361\356\377\355\360\355\377\354\357\354\377\353\356\352\377\355\360" - "\355\377\354\357\354\377\357\361\355\377\353\356\352\377\351\355\352\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\355\360\355\377\352\355" - "\351\377\352\355\351\377\353\357\354\377\352\356\353\377\353\360\356\377" - "\353\360\356\377\350\355\353\377\347\353\350\377\352\355\351\377\353\356" - "\352\377\351\355\352\377\353\356\352\377\354\357\354\377\352\355\351\377" - "\353\356\352\377\353\356\352\377\352\356\353\377\352\356\353\377\352\356" - "\353\377\353\357\354\377\346\353\351\377\341\346\344\377\347\353\347\377" - "\352\355\351\377\353\357\354\377\354\360\355\377\355\361\356\377\355\361" - "\356\377\353\356\352\377\350\354\350\377\353\356\352\377\351\357\355\377" - "\353\357\354\377\352\357\355\377\354\363\357\377\351\357\355\377\354\363" - "\357\377\351\357\355\377\355\361\356\377\353\360\356\377\353\360\356\377" - "\352\357\355\377\353\357\354\377\354\360\355\377\353\357\354\377\354\357" - "\354\377\357\363\357\377\356\363\357\377\355\361\356\377\357\363\357\377" - "\357\361\355\377\355\360\355\377\356\360\354\377\357\363\357\377\356\361" - "\356\377\356\361\356\377\356\363\357\377\361\366\363\377\361\366\361\377" - "\361\366\363\377\367\371\365\377\364\367\363\377\357\363\357\377\355\360" - "\354\377\354\357\353\377\355\360\355\377\355\360\355\377\355\360\355\377" - "\356\361\356\377\356\361\356\377\357\363\357\377\360\363\356\377\356\361" - "\356\377\356\361\356\377\357\363\357\377\356\361\356\377\360\365\360\377" - "\354\357\354\377\354\360\355\377\352\356\353\377\354\360\355\377\353\357" - "\354\377\354\360\355\377\355\361\356\377\357\361\355\377\356\361\356\377" - "\355\360\355\377\356\363\357\377\347\354\351\377\347\353\350\377\350\355" - "\353\377\350\355\353\377\352\357\355\377\354\357\354\377\354\360\355\377" - "\351\357\355\377\352\356\353\377\354\360\355\377\355\360\355\377\356\360" - "\354\377\357\361\355\377\360\365\360\377\356\361\356\377\357\363\357\377" - "\356\360\354\377\355\360\355\377\353\357\354\377\352\356\353\377\354\360" - "\355\377\355\360\355\377\354\357\354\377\354\357\354\377\355\360\355\377" - "\354\360\355\377\356\361\356\377\353\357\354\377\355\360\355\377\355\360" - "\355\377\354\357\354\377\355\360\355\377\355\360\355\377\355\360\355\377" - "\356\361\356\377\355\360\355\377\354\360\355\377\351\355\352\377\352\356" - "\353\377\353\357\354\377\351\355\352\377\352\357\355\377\355\360\355\377" - "\354\360\355\377\353\360\356\377\355\361\356\377\355\364\360\377\356\361" - "\356\377\354\357\354\377\352\356\353\377\353\357\354\377\353\356\352\377" - "\352\355\351\377\353\356\352\377\352\356\353\377\356\360\354\377\354\360" - "\355\377\353\357\354\377\354\360\355\377\350\354\350\377\351\355\352\377" - "\351\356\354\377\344\346\342\377\352\355\351\377\354\357\354\377\354\357" - "\354\377\354\360\355\377\355\361\356\377\356\361\356\377\353\356\352\377" - "\352\355\351\377\351\357\355\377\352\357\355\377\353\360\356\377\354\363" - "\357\377\354\363\357\377\355\361\356\377\355\361\356\377\355\360\355\377" - "\353\357\354\377\351\357\355\377\353\360\356\377\352\357\355\377\353\360" - "\356\377\354\363\357\377\355\361\356\377\353\360\356\377\353\361\356\377" - "\355\361\356\377\357\363\357\377\361\365\360\377\356\360\354\377\357\361" - "\355\377\356\361\356\377\356\361\356\377\354\357\354\377\357\363\357\377" - "\355\361\356\377\360\365\360\377\357\363\357\377\356\361\356\377\360\365" - "\360\377\354\357\354\377\352\356\353\377\354\360\355\377\354\357\354\377" - "\353\356\352\377\353\356\352\377\355\360\355\377\355\361\356\377\360\365" - "\360\377\356\363\357\377\356\361\356\377\357\363\357\377\354\357\354\377" - "\356\361\356\377\360\365\360\377\356\361\356\377\355\364\360\377\351\355" - "\352\377\353\357\354\377\353\357\354\377\353\357\354\377\351\356\354\377" - "\357\363\357\377\357\363\357\377\357\363\357\377\355\360\354\377\347\351" - "\345\377\342\346\344\377\347\353\350\377\346\354\352\377\351\355\352\377" - "\352\355\351\377\352\356\353\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\355\360\355\377\356\361\356\377\360\363\356\377\357\363\357\377" - "\360\363\356\377\360\365\360\377\355\360\355\377\354\357\354\377\352\356" - "\353\377\353\357\354\377\354\360\355\377\355\360\355\377\353\356\352\377" - "\353\360\356\377\354\363\357\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\353\357\354\377\354\360\355\377\356\361\356\377\354\360\355\377" - "\353\356\352\377\353\356\352\377\353\356\352\377\353\356\352\377\353\356" - "\352\377\353\356\352\377\351\356\354\377\354\357\354\377\352\355\351\377" - "\353\357\354\377\353\357\354\377\353\360\356\377\354\363\357\377\354\360" - "\355\377\355\364\360\377\355\361\356\377\355\360\355\377\354\357\354\377" - "\353\356\352\377\354\357\354\377\351\355\352\377\352\355\351\377\353\357" - "\354\377\356\360\354\377\354\360\355\377\353\357\354\377\350\354\350\377" - "\350\354\350\377\351\355\352\377\347\353\350\377\360\363\356\377\356\361" - "\356\377\354\357\354\377\356\361\356\377\357\363\357\377\357\363\357\377" - "\357\363\357\377\356\360\354\377\354\357\354\377\356\361\356\377\354\360" - "\355\377\356\363\357\377\354\360\355\377\354\360\355\377\356\361\356\377" - "\356\360\354\377\353\356\352\377\353\357\354\377\355\361\356\377\355\361" - "\356\377\353\357\354\377\353\357\354\377\355\361\356\377\354\360\355\377" - "\353\360\356\377\354\360\355\377\353\361\356\377\356\363\357\377\357\361" - "\355\377\355\360\355\377\356\361\356\377\354\357\354\377\352\356\353\377" - "\352\356\353\377\354\360\355\377\355\361\356\377\360\365\361\377\356\361" - "\356\377\357\363\357\377\360\365\360\377\356\361\356\377\351\355\352\377" - "\352\356\353\377\352\355\351\377\353\356\352\377\353\356\352\377\353\357" - "\354\377\355\361\356\377\360\365\361\377\357\363\357\377\354\360\355\377" - "\355\364\360\377\354\360\355\377\356\363\357\377\356\363\357\377\360\365" - "\361\377\357\363\357\377\355\361\356\377\353\357\354\377\353\357\354\377" - "\351\356\354\377\353\357\354\377\355\360\355\377\356\361\356\377\361\365" - "\360\377\361\365\360\377\360\363\356\377\355\360\355\377\352\356\353\377" - "\352\356\353\377\351\355\352\377\353\356\352\377\352\356\353\377\353\357" - "\354\377\352\356\353\377\352\356\353\377\353\356\352\377\357\363\357\377" - "\360\363\356\377\357\363\357\377\360\363\356\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\353\360\356\377\354\360\355\377\355\361\356\377" - "\353\357\354\377\355\360\355\377\353\357\354\377\353\357\354\377\353\357" - "\354\377\352\356\353\377\355\360\355\377\355\360\355\377\354\357\354\377" - "\354\357\354\377\356\361\356\377\355\360\354\377\353\356\352\377\353\356" - "\352\377\354\357\354\377\352\356\353\377\352\356\353\377\347\355\353\377" - "\351\355\352\377\353\356\352\377\354\357\354\377\353\356\352\377\351\356" - "\354\377\353\360\356\377\353\360\356\377\352\357\355\377\353\360\356\377" - "\350\356\354\377\352\356\353\377\355\360\355\377\355\360\355\377\353\356" - "\352\377\352\355\351\377\353\356\352\377\352\355\351\377\350\354\350\377" - "\352\355\351\377\353\356\352\377\352\355\351\377\351\355\352\377\354\357" - "\354\377\364\367\363\377\356\361\356\377\357\363\357\377\357\361\355\377" - "\356\360\354\377\360\363\356\377\360\363\356\377\360\363\356\377\356\361" - "\356\377\356\361\356\377\356\363\357\377\354\360\355\377\354\360\355\377" - "\354\360\355\377\354\357\354\377\356\360\354\377\356\360\354\377\354\360" - "\355\377\355\361\356\377\355\364\360\377\355\361\356\377\354\363\357\377" - "\355\361\356\377\354\363\357\377\353\360\356\377\355\364\360\377\355\360" - "\355\377\354\360\355\377\355\360\355\377\353\357\354\377\355\361\356\377" - "\353\357\354\377\354\363\357\377\353\357\354\377\352\357\355\377\354\360" - "\355\377\353\357\354\377\356\363\357\377\361\366\361\377\360\365\360\377" - "\357\363\357\377\361\366\361\377\361\366\363\377\354\357\354\377\333\336" - "\332\377\346\351\346\377\352\356\353\377\352\357\355\377\355\361\356\377" - "\355\361\356\377\355\361\356\377\354\363\357\377\356\363\357\377\354\363" - "\357\377\353\361\356\377\356\363\357\377\356\363\357\377\355\361\356\377" - "\353\357\354\377\353\356\352\377\354\357\354\377\356\361\356\377\356\361" - "\356\377\355\360\355\377\357\363\357\377\363\366\361\377\361\363\356\377" - "\357\361\355\377\355\361\356\377\352\356\353\377\352\355\351\377\355\357" - "\353\377\354\357\354\377\353\357\354\377\352\357\355\377\351\356\354\377" - "\352\357\355\377\353\357\354\377\355\360\355\377\356\363\357\377\354\360" - "\355\377\354\360\355\377\354\360\355\377\353\357\354\377\352\356\353\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\353\357\354\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\352\356\353\377\355\360\355\377" - "\355\360\355\377\355\360\355\377\353\356\352\377\355\361\356\377\357\361" - "\355\377\356\361\356\377\356\361\356\377\356\361\356\377\354\360\355\377" - "\352\356\353\377\351\356\354\377\352\356\353\377\354\357\354\377\353\356" - "\352\377\351\355\352\377\353\357\354\377\353\357\354\377\354\363\357\377" - "\351\356\354\377\352\357\355\377\351\356\354\377\353\357\354\377\353\356" - "\352\377\354\357\354\377\354\357\354\377\354\357\354\377\355\360\355\377" - "\353\356\352\377\353\357\354\377\353\357\354\377\351\356\354\377\350\354" - "\351\377\350\354\351\377\354\357\354\377\356\363\357\377\355\360\355\377" - "\357\363\357\377\356\360\354\377\356\360\354\377\356\360\354\377\356\360" - "\354\377\357\361\355\377\356\361\356\377\357\361\355\377\354\360\355\377" - "\356\363\357\377\355\361\356\377\354\360\355\377\357\363\357\377\356\361" - "\356\377\355\360\355\377\357\363\357\377\357\363\357\377\355\361\356\377" - "\355\360\355\377\355\361\356\377\354\360\355\377\354\360\355\377\356\361" - "\356\377\357\361\355\377\355\361\356\377\355\361\356\377\354\360\355\377" - "\354\360\355\377\354\360\355\377\353\357\354\377\353\360\356\377\352\357" - "\355\377\352\357\355\377\355\364\360\377\357\363\357\377\356\363\357\377" - "\357\363\357\377\360\365\360\377\360\365\360\377\365\370\364\377\371\373" - "\367\377\354\356\351\377\341\343\337\377\346\351\346\377\345\351\346\377" - "\346\354\352\377\353\357\354\377\355\360\355\377\356\361\356\377\354\363" - "\357\377\354\360\355\377\354\360\355\377\354\360\355\377\353\360\356\377" - "\356\361\356\377\354\360\355\377\354\360\355\377\353\357\354\377\354\357" - "\354\377\355\357\353\377\355\360\354\377\360\363\356\377\357\363\357\377" - "\356\361\356\377\354\357\354\377\354\357\354\377\355\361\356\377\357\363" - "\357\377\357\361\355\377\355\357\353\377\353\356\352\377\351\355\352\377" - "\352\357\355\377\352\356\353\377\351\356\354\377\347\355\353\377\347\353" - "\350\377\347\355\353\377\352\356\353\377\354\360\355\377\352\356\353\377" - "\353\357\354\377\353\356\352\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\352\356\353\377\352\356\353\377\353\357\354\377" - "\352\356\353\377\355\360\355\377\355\360\355\377\353\356\352\377\355\361" - "\356\377\355\361\356\377\354\357\354\377\353\356\352\377\356\361\356\377" - "\355\361\356\377\354\363\357\377\354\360\355\377\352\356\353\377\350\354" - "\351\377\351\355\352\377\351\355\352\377\354\357\354\377\354\357\354\377" - "\353\357\354\377\353\357\354\377\352\356\353\377\352\356\353\377\352\356" - "\353\377\353\356\352\377\352\355\351\377\353\356\352\377\354\357\354\377" - "\354\357\354\377\355\360\355\377\355\360\355\377\354\360\355\377\352\356" - "\353\377\350\355\353\377\346\354\353\377\351\355\352\377\351\355\352\377" - "\354\360\355\377\356\361\356\377\357\363\357\377\360\363\356\377\363\365" - "\357\377\357\361\355\377\354\357\354\377\353\357\354\377\353\356\352\377" - "\354\360\355\377\355\361\356\377\356\363\357\377\356\363\357\377\356\363" - "\357\377\356\363\357\377\356\363\357\377\354\360\355\377\356\363\357\377" - "\357\363\357\377\360\365\360\377\356\360\354\377\350\354\350\377\350\354" - "\351\377\354\357\354\377\355\360\355\377\357\361\355\377\353\357\354\377" - "\353\357\354\377\355\360\355\377\356\361\356\377\354\360\355\377\352\356" - "\353\377\353\357\354\377\352\357\355\377\354\363\357\377\356\361\356\377" - "\357\363\357\377\355\361\356\377\356\361\356\377\357\363\357\377\360\365" - "\360\377\361\366\361\377\366\372\367\377\371\377\374\377\363\374\370\377" - "\347\363\360\377\352\357\355\377\351\356\354\377\354\363\357\377\354\360" - "\355\377\352\357\355\377\353\360\356\377\353\357\354\377\354\360\355\377" - "\355\361\356\377\354\360\355\377\357\363\357\377\355\361\356\377\354\357" - "\354\377\355\360\355\377\356\360\354\377\356\361\356\377\354\357\354\377" - "\355\360\355\377\356\361\356\377\356\361\356\377\357\361\355\377\354\357" - "\354\377\354\357\354\377\353\356\352\377\354\357\353\377\353\356\352\377" - "\351\355\352\377\347\355\353\377\352\355\351\377\351\355\352\377\351\356" - "\354\377\350\355\353\377\347\355\353\377\350\355\353\377\351\356\354\377" - "\353\360\356\377\352\356\353\377\352\355\351\377\353\356\352\377\353\356" - "\352\377\355\360\355\377\354\357\354\377\353\356\352\377\357\361\355\377" - "\355\360\355\377\352\356\353\377\353\356\352\377\353\356\352\377\354\357" - "\354\377\353\356\352\377\354\360\355\377\354\360\355\377\354\357\354\377" - "\355\360\355\377\356\361\356\377\354\360\355\377\354\360\355\377\355\361" - "\356\377\356\363\357\377\352\356\353\377\352\356\353\377\353\357\354\377" - "\353\356\352\377\353\356\352\377\354\357\354\377\355\360\355\377\354\360" - "\355\377\352\356\353\377\354\357\354\377\354\357\354\377\353\356\352\377" - "\355\360\355\377\352\355\351\377\354\357\354\377\353\357\354\377\354\357" - "\354\377\354\360\355\377\352\356\353\377\352\356\353\377\347\355\353\377" - "\352\357\355\377\352\357\355\377\356\361\356\377\360\365\360\377\356\361" - "\356\377\356\361\356\377\360\365\360\377\357\363\357\377\354\357\354\377" - "\353\357\354\377\355\360\355\377\354\357\354\377\354\360\355\377\355\361" - "\356\377\356\363\357\377\356\363\357\377\354\360\355\377\353\360\356\377" - "\353\360\356\377\354\360\355\377\361\365\360\377\365\370\364\377\361\365" - "\360\377\357\361\355\377\352\356\353\377\354\357\354\377\353\357\354\377" - "\355\361\356\377\354\360\355\377\353\357\354\377\354\360\355\377\356\361" - "\356\377\355\361\356\377\356\361\356\377\354\360\355\377\354\360\355\377" - "\353\360\356\377\356\361\356\377\356\361\356\377\356\361\356\377\356\361" - "\356\377\357\363\357\377\355\360\355\377\357\363\357\377\356\363\357\377" - "\355\366\363\377\343\360\357\377\346\365\364\377\357\366\365\377\354\363" - "\357\377\354\363\357\377\353\357\354\377\352\357\355\377\354\360\355\377" - "\355\361\356\377\354\360\355\377\355\361\356\377\356\363\357\377\356\361" - "\356\377\357\363\357\377\356\360\354\377\355\357\353\377\355\357\353\377" - "\356\361\356\377\355\360\355\377\356\361\356\377\354\357\354\377\357\363" - "\357\377\353\356\352\377\347\353\347\377\345\351\346\377\344\350\345\377" - "\352\355\351\377\351\355\352\377\351\355\352\377\352\356\353\377\353\357" - "\354\377\353\357\354\377\347\355\353\377\351\355\352\377\351\355\352\377" - "\350\356\354\377\350\356\354\377\356\361\356\377\356\361\356\377\355\357" - "\352\377\353\355\350\377\353\356\352\377\355\357\353\377\360\363\356\377" - "\357\361\355\377\356\360\354\377\356\361\356\377\355\360\355\377\353\357" - "\354\377\354\360\355\377\355\357\353\377\355\360\355\377\356\361\356\377" - "\354\360\355\377\356\361\356\377\354\357\354\377\354\360\355\377\355\361" - "\356\377\355\361\356\377\355\361\356\377\356\361\356\377\356\361\356\377" - "\355\360\355\377\354\357\354\377\353\356\352\377\353\356\352\377\354\357" - "\354\377\355\360\355\377\354\360\355\377\354\360\355\377\355\360\355\377" - "\355\360\355\377\355\357\353\377\352\355\351\377\352\355\351\377\353\356" - "\352\377\353\356\352\377\351\355\352\377\352\356\353\377\354\357\354\377" - "\355\360\355\377\353\360\356\377\351\356\354\377\351\356\354\377\354\360" - "\355\377\357\363\357\377\355\360\355\377\355\360\355\377\357\363\357\377" - "\356\363\357\377\353\357\354\377\353\357\354\377\353\357\354\377\356\361" - "\356\377\354\357\354\377\355\360\355\377\353\357\354\377\357\363\357\377" - "\354\363\357\377\354\360\355\377\353\360\356\377\356\363\357\377\364\367" - "\363\377\366\370\363\377\370\372\366\377\364\367\363\377\356\361\356\377" - "\355\360\355\377\355\361\356\377\355\361\356\377\356\361\356\377\354\357" - "\354\377\354\357\354\377\354\357\354\377\355\360\355\377\353\357\354\377" - "\353\357\354\377\354\357\354\377\355\361\356\377\354\357\354\377\356\361" - "\356\377\354\360\355\377\354\357\354\377\354\357\354\377\356\361\356\377" - "\360\365\360\377\356\363\357\377\353\363\360\377\346\357\356\377\350\361" - "\360\377\353\363\360\377\353\360\356\377\353\360\356\377\353\357\354\377" - "\353\357\354\377\355\360\355\377\355\361\356\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\356\363\357\377\357\363\357\377\357\363\357\377" - "\357\361\355\377\356\360\354\377\357\363\357\377\356\363\357\377\355\361" - "\356\377\357\363\357\377\360\363\356\377\356\360\354\377\337\340\334\377" - "\324\327\323\377\340\343\340\377\353\356\352\377\347\355\353\377\350\356" - "\354\377\351\357\355\377\351\355\352\377\351\355\352\377\351\355\352\377" - "\351\355\352\377\353\357\354\377\354\360\355\377\354\360\355\377\354\363" - "\357\377\357\363\357\377\365\366\360\377\360\363\356\377\355\357\353\377" - "\356\360\354\377\356\360\354\377\355\360\355\377\357\361\355\377\356\360" - "\354\377\356\361\356\377\354\357\354\377\356\360\354\377\356\360\354\377" - "\357\361\355\377\355\360\355\377\357\361\355\377\355\361\356\377\355\360" - "\355\377\354\360\355\377\355\361\356\377\354\360\355\377\354\360\355\377" - "\355\357\353\377\355\360\355\377\355\360\355\377\355\360\355\377\353\356" - "\352\377\353\356\352\377\353\356\352\377\353\357\354\377\353\357\354\377" - "\354\360\355\377\353\356\352\377\357\361\355\377\355\357\353\377\352\355" - "\351\377\352\355\351\377\352\355\351\377\354\357\354\377\352\356\353\377" - "\351\355\352\377\354\357\354\377\355\360\355\377\353\356\352\377\351\356" - "\354\377\350\356\355\377\355\361\356\377\356\363\357\377\353\357\354\377" - "\354\357\354\377\355\360\355\377\355\361\356\377\355\361\356\377\354\360" - "\355\377\356\361\356\377\353\356\352\377\353\356\352\377\355\360\355\377" - "\353\360\356\377\355\361\356\377\354\360\355\377\354\360\355\377\353\360" - "\356\377\356\361\356\377\360\365\360\377\366\371\366\377\372\373\367\377" - "\363\366\361\377\360\365\360\377\360\365\360\377\357\363\357\377\357\363" - "\357\377\357\363\357\377\356\360\354\377\357\361\355\377\356\361\356\377" - "\354\357\354\377\353\357\354\377\354\360\355\377\355\360\355\377\355\360" - "\355\377\354\357\354\377\356\361\356\377\355\360\355\377\354\357\354\377" - "\353\356\352\377\355\360\355\377\353\356\352\377\352\357\355\377\351\360" - "\356\377\351\360\356\377\352\361\357\377\354\363\357\377\355\361\356\377" - "\354\363\357\377\354\363\357\377\350\360\356\377\353\363\360\377\352\357" - "\355\377\353\357\354\377\352\356\353\377\354\360\355\377\360\365\361\377" - "\357\363\357\377\361\365\360\377\360\363\356\377\360\363\356\377\357\363" - "\357\377\360\365\361\377\360\365\360\377\361\366\361\377\371\374\371\377" - "\373\377\373\377\352\355\351\377\337\342\336\377\351\354\350\377\354\357" - "\354\377\351\357\355\377\351\356\354\377\347\355\353\377\351\356\354\377" - "\351\355\352\377\352\356\353\377\351\355\352\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\353\361\356\377\356\361\356\377\360\365\360\377" - "\360\363\356\377\355\357\353\377\356\360\354\377\357\361\355\377\357\361" - "\355\377\360\363\356\377\356\361\356\377\356\361\356\377\353\357\354\377" - "\352\355\351\377\353\356\352\377\353\356\352\377\355\360\355\377\355\360" - "\355\377\355\361\356\377\356\361\356\377\355\360\355\377\356\361\356\377" - "\355\360\355\377\355\360\355\377\353\356\352\377\352\356\353\377\355\360" - "\355\377\355\360\355\377\356\360\354\377\356\360\354\377\355\360\355\377" - "\351\356\354\377\351\356\354\377\353\357\354\377\354\357\354\377\355\357" - "\353\377\354\356\351\377\355\357\353\377\350\354\350\377\353\356\352\377" - "\353\356\352\377\354\357\354\377\352\356\353\377\354\360\355\377\354\360" - "\355\377\355\360\355\377\354\360\355\377\352\356\353\377\356\363\357\377" - "\356\363\357\377\356\363\357\377\355\361\356\377\353\357\354\377\354\360" - "\355\377\360\365\361\377\361\365\360\377\365\367\361\377\360\363\356\377" - "\355\357\353\377\350\354\350\377\351\356\354\377\354\360\355\377\355\364" - "\360\377\355\364\360\377\356\363\357\377\356\361\356\377\361\366\361\377" - "\356\363\357\377\361\366\363\377\356\363\357\377\356\363\357\377\355\361" - "\356\377\356\361\356\377\360\363\356\377\360\363\356\377\357\361\355\377" - "\356\360\354\377\356\361\356\377\356\361\356\377\354\360\355\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\354\357\354\377\354\357\354\377" - "\353\356\352\377\353\356\352\377\353\356\352\377\354\357\354\377\353\356" - "\352\377\351\355\352\377\352\356\353\377\350\355\353\377\352\357\355\377" - "\352\357\355\377\354\360\355\377\355\361\356\377\353\357\354\377\352\357" - "\355\377\350\355\353\377\352\357\355\377\352\357\355\377\352\357\355\377" - "\354\360\355\377\357\363\357\377\360\365\360\377\357\361\355\377\357\361" - "\355\377\357\363\357\377\354\360\355\377\355\361\356\377\360\365\360\377" - "\361\366\363\377\366\372\367\377\377\377\377\377\377\377\376\377\377\377" - "\377\377\365\367\361\377\361\366\361\377\352\356\353\377\351\356\354\377" - "\347\355\353\377\347\355\353\377\350\354\350\377\353\356\352\377\352\356" - "\353\377\352\355\351\377\352\355\351\377\353\357\354\377\353\360\356\377" - "\355\360\355\377\357\363\357\377\355\360\355\377\355\357\353\377\355\357" - "\353\377\354\357\353\377\354\357\353\377\356\360\354\377\356\361\356\377" - "\356\361\356\377\354\357\354\377\356\360\354\377\353\356\352\377\353\356" - "\352\377\352\356\353\377\355\360\355\377\355\360\355\377\355\360\355\377" - "\354\360\355\377\356\361\356\377\357\361\355\377\354\357\354\377\352\356" - "\353\377\351\355\352\377\353\356\352\377\354\357\354\377\355\357\353\377" - "\354\357\354\377\356\360\354\377\354\360\355\377\351\356\354\377\352\357" - "\355\377\356\361\356\377\360\363\356\377\357\361\355\377\361\365\360\377" - "\356\361\356\377\355\360\355\377\355\360\355\377\355\360\355\377\347\355" - "\353\377\351\356\354\377\354\360\355\377\355\360\355\377\356\361\356\377" - "\355\360\355\377\354\363\357\377\355\361\356\377\357\361\355\377\360\363" - "\356\377\356\361\356\377\355\361\356\377\360\365\361\377\355\361\356\377" - "\367\372\367\377\367\371\365\377\363\367\364\377\352\357\356\377\352\357" - "\355\377\352\357\355\377\354\363\357\377\354\363\357\377\356\361\356\377" - "\360\365\360\377\355\361\356\377\354\363\357\377\354\360\355\377\354\360" - "\355\377\356\363\357\377\354\363\357\377\354\360\355\377\360\363\356\377" - "\355\360\355\377\354\357\354\377\356\360\354\377\355\360\355\377\356\360" - "\354\377\355\361\356\377\355\361\356\377\355\361\356\377\356\361\356\377" - "\355\360\354\377\356\357\352\377\344\346\342\377\347\353\347\377\347\353" - "\347\377\352\355\351\377\353\355\350\377\347\351\345\377\352\355\351\377" - "\353\357\354\377\350\355\353\377\351\356\354\377\352\357\355\377\354\360" - "\355\377\353\357\354\377\352\357\355\377\352\357\355\377\352\357\355\377" - "\350\354\351\377\351\355\352\377\353\357\354\377\360\363\356\377\357\361" - "\355\377\357\361\355\377\356\360\354\377\357\361\355\377\355\360\355\377" - "\355\360\355\377\357\363\357\377\361\366\361\377\363\367\364\377\367\373" - "\370\377\365\371\366\377\367\371\365\377\361\363\355\377\360\365\360\377" - "\356\361\356\377\352\356\353\377\353\357\354\377\352\356\353\377\352\355" - "\351\377\354\357\354\377\351\355\352\377\351\355\352\377\352\356\353\377" - "\351\355\352\377\352\356\353\377\356\361\356\377\357\363\357\377\356\361" - "\356\377\351\354\350\377\351\354\350\377\353\356\352\377\354\357\354\377" - "\354\357\354\377\354\357\353\377\354\357\354\377\355\360\355\377\353\356" - "\352\377\353\356\352\377\354\357\354\377\354\360\355\377\354\357\354\377" - "\356\361\356\377\354\360\355\377\355\361\356\377\356\361\356\377\360\363" - "\356\377\357\363\357\377\356\363\357\377\352\356\353\377\352\356\353\377" - "\352\356\353\377\356\361\356\377\355\360\355\377\355\360\355\377\352\356" - "\353\377\350\356\354\377\352\356\353\377\355\361\356\377\357\361\355\377" - "\357\361\355\377\355\357\353\377\355\360\355\377\353\356\352\377\356\360" - "\354\377\354\357\354\377\352\357\355\377\351\356\354\377\354\360\355\377" - "\353\356\352\377\354\357\354\377\355\360\355\377\353\360\356\377\354\357" - "\354\377\355\357\353\377\353\356\352\377\354\357\353\377\355\360\355\377" - "\357\363\357\377\356\361\356\377\360\365\360\377\360\365\360\377\354\363" - "\360\377\354\363\360\377\354\360\355\377\355\361\356\377\353\357\354\377" - "\354\360\355\377\354\360\355\377\356\363\357\377\355\360\355\377\356\363" - "\357\377\357\361\355\377\357\363\357\377\354\360\355\377\353\357\354\377" - "\353\357\354\377\357\363\357\377\356\361\356\377\356\361\356\377\356\361" - "\356\377\355\361\356\377\354\360\355\377\356\361\356\377\356\363\357\377" - "\360\365\360\377\355\360\354\377\361\363\356\377\352\353\346\377\335\336" - "\330\377\336\337\332\377\342\345\341\377\356\357\352\377\355\357\353\377" - "\355\357\353\377\354\357\354\377\352\355\351\377\353\357\354\377\350\355" - "\353\377\351\356\354\377\353\357\354\377\354\357\354\377\356\361\356\377" - "\365\370\364\377\357\361\355\377\350\353\347\377\350\354\351\377\356\360" - "\354\377\356\360\354\377\352\355\351\377\350\354\350\377\356\360\354\377" - "\357\363\357\377\357\363\357\377\356\361\356\377\355\360\355\377\355\364" - "\360\377\354\363\357\377\356\365\361\377\350\354\351\377\347\353\350\377" - "\355\357\353\377\360\365\360\377\354\357\354\377\353\357\354\377\354\357" - "\354\377\354\357\354\377\353\356\352\377\354\357\354\377\352\356\353\377" - "\353\357\354\377\352\356\353\377\355\360\355\377\355\360\355\377\360\365" - "\360\377\357\363\357\377\363\366\361\377\361\363\356\377\356\360\354\377" - "\355\357\353\377\353\356\352\377\353\356\352\377\353\356\352\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\353\357\354\377\353\357\354\377" - "\351\355\352\377\353\357\354\377\353\356\352\377\354\360\355\377\353\357" - "\354\377\354\360\355\377\355\360\355\377\360\363\356\377\356\361\356\377" - "\356\361\356\377\354\360\355\377\353\357\354\377\355\360\355\377\355\360" - "\355\377\355\360\355\377\352\356\353\377\352\355\351\377\351\355\352\377" - "\352\356\353\377\352\355\351\377\353\360\356\377\353\357\354\377\355\361" - "\356\377\355\360\355\377\354\357\354\377\356\361\356\377\352\356\353\377" - "\354\357\354\377\356\361\356\377\355\360\355\377\354\357\353\377\354\357" - "\354\377\355\360\355\377\356\361\356\377\355\357\353\377\355\357\353\377" - "\354\357\353\377\354\357\354\377\355\360\355\377\357\363\357\377\357\363" - "\357\377\360\365\361\377\354\363\360\377\351\360\356\377\355\361\356\377" - "\354\360\355\377\355\361\356\377\357\363\357\377\355\360\355\377\355\360" - "\355\377\356\361\356\377\357\363\357\377\356\361\356\377\357\361\355\377" - "\355\360\355\377\353\357\354\377\354\360\355\377\355\357\353\377\356\361" - "\356\377\355\360\354\377\356\361\355\377\354\357\354\377\355\360\355\377" - "\356\363\357\377\356\363\357\377\360\365\360\377\370\372\366\377\365\366" - "\360\377\357\360\353\377\365\366\360\377\366\367\361\377\366\370\363\377" - "\361\365\360\377\364\367\363\377\360\363\356\377\356\361\356\377\352\355" - "\351\377\353\356\352\377\351\355\352\377\352\356\353\377\353\357\354\377" - "\353\357\354\377\356\361\356\377\374\377\373\377\377\377\374\377\374\376" - "\371\377\364\367\363\377\357\361\355\377\347\351\345\377\352\355\351\377" - "\353\356\352\377\356\361\356\377\356\361\356\377\353\357\354\377\355\360" - "\355\377\355\360\355\377\355\361\356\377\356\363\357\377\356\365\361\377" - "\355\361\356\377\352\356\353\377\353\357\354\377\357\361\355\377\355\360" - "\355\377\353\357\354\377\353\356\352\377\353\356\352\377\354\357\353\377" - "\354\357\354\377\353\357\354\377\353\357\354\377\353\357\354\377\355\360" - "\355\377\356\361\356\377\354\357\354\377\357\363\357\377\360\365\360\377" - "\365\367\361\377\361\365\360\377\360\363\356\377\357\361\355\377\355\360" - "\355\377\352\356\353\377\352\356\353\377\354\360\355\377\353\357\354\377" - "\352\356\353\377\352\356\353\377\353\356\352\377\353\356\352\377\350\354" - "\351\377\354\360\355\377\352\356\353\377\352\357\355\377\352\357\355\377" - "\354\357\354\377\355\360\355\377\354\360\355\377\353\357\354\377\355\361" - "\356\377\354\357\354\377\355\360\355\377\352\355\351\377\351\354\350\377" - "\347\353\347\377\347\353\347\377\353\356\352\377\353\356\352\377\350\355" - "\353\377\352\356\353\377\352\356\353\377\354\357\354\377\354\357\354\377" - "\355\360\355\377\353\357\354\377\354\360\355\377\354\360\355\377\354\357" - "\354\377\355\361\356\377\360\363\356\377\356\361\356\377\356\361\356\377" - "\356\361\356\377\355\357\353\377\355\357\353\377\354\357\354\377\353\356" - "\352\377\356\361\356\377\357\363\357\377\356\361\356\377\351\355\352\377" - "\354\360\355\377\353\360\356\377\355\361\356\377\355\360\355\377\354\357" - "\354\377\355\360\355\377\355\360\355\377\356\361\356\377\357\363\357\377" - "\357\363\357\377\357\361\355\377\354\357\354\377\355\360\355\377\353\356" - "\352\377\356\360\354\377\356\360\354\377\355\357\353\377\355\357\353\377" - "\356\360\354\377\355\360\355\377\356\361\356\377\360\365\360\377\364\367" - "\363\377\372\373\367\377\371\372\365\377\370\371\364\377\376\377\372\377" - "\376\377\372\377\367\371\365\377\361\365\360\377\360\365\360\377\361\366" - "\361\377\356\361\356\377\354\357\354\377\353\356\352\377\352\356\353\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\353\357\354\377\365\370" - "\364\377\374\376\371\377\374\376\371\377\370\372\366\377\356\360\354\377" - "\346\350\343\377\351\353\346\377\356\360\354\377\356\361\356\377\356\363" - "\357\377\356\361\356\377\355\360\355\377\354\357\354\377\355\360\355\377" - "\360\365\361\377\360\365\361\377\355\361\356\377\357\363\357\377\355\361" - "\356\377\357\363\357\377\354\357\354\377\351\355\352\377\351\355\352\377" - "\352\355\351\377\354\357\354\377\352\355\351\377\352\355\351\377\352\355" - "\351\377\353\356\352\377\355\360\355\377\354\357\354\377\354\360\355\377" - "\355\360\354\377\360\365\360\377\361\365\360\377\357\363\357\377\357\361" - "\355\377\356\360\354\377\356\360\354\377\352\356\353\377\352\356\353\377" - "\354\360\355\377\350\356\354\377\351\355\352\377\351\355\352\377\351\355" - "\352\377\347\354\352\377\351\356\354\377\351\356\354\377\353\360\356\377" - "\353\360\356\377\354\360\355\377\353\357\354\377\353\356\352\377\353\357" - "\354\377\353\357\354\377\355\361\356\377\355\357\353\377\361\365\357\377" - "\354\356\351\377\351\353\346\377\347\351\345\377\352\355\351\377\353\356" - "\352\377\355\357\353\377\355\360\355\377\353\356\352\377\352\356\353\377" - "\352\356\353\377\355\360\355\377\354\357\354\377\353\357\354\377\351\356" - "\354\377\354\360\355\377\353\357\354\377\356\363\357\377\357\363\357\377" - "\355\360\355\377\357\363\357\377\355\360\355\377\355\360\355\377\353\356" - "\352\377\355\357\353\377\356\360\354\377\357\363\357\377\356\361\356\377" - "\356\361\356\377\353\357\354\377\353\357\354\377\353\357\354\377\354\360" - "\355\377\354\357\354\377\356\361\356\377\356\361\356\377\355\360\355\377" - "\357\363\357\377\357\363\357\377\355\361\356\377\355\361\356\377\355\361" - "\356\377\355\360\355\377\356\360\354\377\355\360\355\377\356\361\356\377" - "\356\360\354\377\356\360\354\377\357\361\355\377\355\360\355\377\355\361" - "\356\377\360\365\361\377\364\367\363\377\372\373\367\377\370\372\365\377" - "\373\374\367\377\373\374\367\377\367\371\365\377\365\370\364\377\361\366" - "\361\377\356\363\357\377\357\363\357\377\357\363\357\377\355\360\355\377" - "\354\360\355\377\352\356\353\377\353\357\354\377\352\356\353\377\354\360" - "\355\377\355\361\356\377\360\365\361\377\372\376\372\377\366\371\365\377" - "\371\373\367\377\361\365\360\377\353\354\347\377\354\355\350\377\360\361" - "\355\377\355\360\355\377\356\363\357\377\354\357\354\377\354\357\354\377" - "\354\357\354\377\352\355\351\377\355\361\356\377\356\363\357\377\355\361" - "\356\377\355\361\356\377\353\357\354\377\354\357\354\377\352\355\351\377" - "\353\357\354\377\350\354\351\377\353\356\352\377\354\357\354\377\354\357" - "\354\377\353\356\352\377\352\355\351\377\355\360\355\377\353\357\354\377" - "\355\360\355\377\353\356\352\377\355\360\354\377\360\363\356\377\360\363" - "\356\377\355\360\355\377\354\357\354\377\357\361\355\377\356\360\354\377" - "\353\356\352\377\351\355\352\377\353\357\354\377\356\360\354\377\353\356" - "\352\377\353\357\354\377\350\354\351\377\350\355\353\377\347\355\353\377" - "\353\357\354\377\352\356\353\377\352\356\353\377\354\357\354\377\353\356" - "\352\377\353\357\354\377\352\356\353\377\354\360\355\377\355\361\356\377" - "\357\361\355\377\361\365\357\377\357\360\353\377\347\351\345\377\342\345" - "\341\377\352\355\351\377\352\355\351\377\355\357\353\377\354\357\354\377" - "\354\357\354\377\352\356\353\377\352\356\353\377\354\360\355\377\354\357" - "\354\377\352\356\353\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\355\361\356\377\356\363\357\377\357\363\357\377\357\363\357\377\355\360" - "\355\377\356\361\356\377\356\360\354\377\355\357\353\377\356\360\354\377" - "\356\360\354\377\356\360\354\377\355\360\355\377\355\361\356\377\353\357" - "\354\377\354\360\355\377\355\360\355\377\356\361\356\377\357\363\357\377" - "\357\363\357\377\356\361\356\377\357\363\357\377\356\361\356\377\355\361" - "\356\377\354\360\355\377\353\357\354\377\353\356\352\377\353\356\352\377" - "\354\357\354\377\355\360\355\377\355\360\355\377\356\361\356\377\356\361" - "\356\377\356\361\356\377\356\363\357\377\356\363\357\377\357\363\357\377" - "\361\366\361\377\374\376\372\377\376\377\373\377\374\376\372\377\367\372" - "\367\377\361\366\363\377\354\360\355\377\355\360\355\377\354\357\354\377" - "\353\357\354\377\354\360\355\377\353\357\354\377\352\356\353\377\352\355" - "\351\377\351\356\354\377\352\356\353\377\353\360\356\377\355\361\356\377" - "\365\370\364\377\363\366\361\377\366\371\365\377\365\370\364\377\367\371" - "\365\377\361\363\356\377\356\360\354\377\356\361\356\377\354\360\355\377" - "\354\357\353\377\353\356\352\377\353\356\352\377\353\356\352\377\355\361" - "\356\377\353\360\356\377\353\360\356\377\356\363\357\377\354\357\354\377" - "\354\360\355\377\352\356\353\377\350\354\351\377\351\355\352\377\351\355" - "\352\377\352\356\353\377\355\360\355\377\353\356\352\377\354\357\354\377" - "\353\357\354\377\351\355\352\377\353\357\354\377\354\357\354\377\356\360" - "\354\377\355\357\353\377\356\360\354\377\356\361\356\377\353\356\352\377" - "\353\356\352\377\355\357\353\377\355\357\353\377\353\355\350\377\353\355" - "\350\377\355\360\354\377\354\357\353\377\352\355\351\377\352\355\351\377" - "\353\357\354\377\350\354\351\377\352\356\353\377\352\356\353\377\353\357" - "\355\377\353\356\353\377\353\356\352\377\352\356\353\377\353\357\354\377" - "\353\357\354\377\354\360\355\377\356\361\356\377\360\363\356\377\364\367" - "\361\377\353\355\350\377\336\340\334\377\352\355\351\377\353\356\352\377" - "\353\356\352\377\355\360\355\377\354\357\354\377\355\360\355\377\352\356" - "\353\377\353\356\352\377\354\357\354\377\353\356\352\377\353\357\354\377" - "\352\357\355\377\354\360\355\377\355\361\356\377\360\365\361\377\354\360" - "\355\377\356\361\356\377\356\361\356\377\356\361\356\377\357\361\355\377" - "\355\357\353\377\355\357\353\377\355\357\353\377\357\361\355\377\356\361" - "\356\377\354\357\354\377\354\360\355\377\354\357\354\377\355\361\356\377" - "\355\360\355\377\356\361\356\377\357\363\357\377\357\363\357\377\356\361" - "\356\377\357\363\357\377\354\363\357\377\354\363\357\377\352\357\355\377" - "\351\355\352\377\352\355\351\377\351\356\354\377\355\360\355\377\352\356" - "\353\377\354\357\354\377\354\357\354\377\356\361\356\377\353\357\354\377" - "\355\360\355\377\356\361\356\377\356\361\356\377\356\361\356\377\364\367" - "\363\377\371\373\367\377\361\366\361\377\356\363\357\377\355\361\356\377" - "\355\360\355\377\354\357\354\377\351\355\352\377\347\353\350\377\347\355" - "\353\377\353\357\354\377\353\357\354\377\350\355\353\377\352\356\353\377" - "\353\357\354\377\360\365\361\377\355\360\355\377\360\365\360\377\356\361" - "\356\377\364\370\365\377\364\367\363\377\364\367\363\377\356\360\354\377" - "\357\363\357\377\356\361\356\377\355\360\354\377\355\360\355\377\353\356" - "\352\377\353\356\352\377\352\356\353\377\347\357\355\377\352\357\355\377" - "\351\357\355\377\353\357\354\377\352\355\351\377\350\354\351\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\352\356\353\377\352\355\351\377" - "\352\356\353\377\352\356\353\377\351\355\352\377\351\355\352\377\352\356" - "\353\377\354\357\354\377\354\357\354\377\356\361\356\377\355\357\353\377" - "\356\360\354\377\355\357\353\377\353\356\352\377\353\356\352\377\355\357" - "\353\377\353\356\352\377\353\356\352\377\354\357\353\377\352\355\351\377" - "\352\355\351\377\350\354\350\377\351\355\352\377\352\356\353\377\352\356" - "\353\377\352\355\351\377\353\356\352\377\356\360\354\377\355\360\355\377" - "\354\357\354\377\354\357\354\377\355\360\355\377\355\360\355\377\356\361" - "\356\377\357\363\357\377\366\371\366\377\366\371\366\377\357\363\357\377" - "\355\357\353\377\353\356\352\377\353\356\352\377\353\356\352\377\353\356" - "\352\377\355\360\355\377\354\357\354\377\353\357\354\377\352\356\353\377" - "\355\360\355\377\354\360\355\377\355\361\356\377\352\357\355\377\355\361" - "\356\377\355\361\356\377\354\360\355\377\353\357\354\377\356\361\356\377" - "\356\361\356\377\357\361\355\377\353\356\352\377\353\356\352\377\353\356" - "\352\377\356\360\354\377\355\360\355\377\354\360\355\377\355\361\356\377" - "\353\357\354\377\355\361\356\377\353\357\354\377\354\357\354\377\356\363" - "\357\377\357\363\357\377\354\360\355\377\354\360\355\377\355\364\360\377" - "\353\360\356\377\354\363\357\377\354\360\355\377\350\355\353\377\351\356" - "\354\377\351\361\357\377\354\360\355\377\354\357\354\377\353\357\354\377" - "\355\360\355\377\354\357\354\377\354\360\355\377\356\361\356\377\357\363" - "\357\377\360\365\360\377\357\363\357\377\360\365\360\377\360\365\360\377" - "\356\363\357\377\355\361\356\377\354\360\355\377\354\357\354\377\350\354" - "\351\377\347\353\350\377\351\355\352\377\353\357\354\377\352\356\353\377" - "\353\357\354\377\352\356\353\377\351\355\352\377\352\357\355\377\352\356" - "\353\377\361\366\363\377\357\363\357\377\363\367\364\377\356\363\357\377" - "\360\365\361\377\360\365\360\377\357\363\357\377\357\363\357\377\357\363" - "\357\377\355\357\353\377\354\357\354\377\354\357\354\377\352\356\353\377" - "\344\354\352\377\352\357\355\377\347\355\353\377\353\356\352\377\353\357" - "\354\377\350\354\351\377\353\357\354\377\353\357\354\377\352\355\351\377" - "\352\355\351\377\353\356\352\377\352\356\353\377\352\356\353\377\351\355" - "\352\377\352\356\353\377\352\356\353\377\352\356\353\377\354\357\354\377" - "\353\356\352\377\356\360\354\377\354\357\354\377\355\357\353\377\354\357" - "\354\377\353\356\352\377\355\357\353\377\353\356\352\377\353\356\352\377" - "\354\357\353\377\354\357\353\377\354\357\354\377\352\355\351\377\352\355" - "\351\377\353\356\352\377\351\355\352\377\352\356\353\377\353\356\352\377" - "\356\360\354\377\356\360\354\377\356\361\356\377\356\360\354\377\357\361" - "\355\377\355\360\355\377\357\361\355\377\360\363\356\377\361\365\360\377" - "\363\366\361\377\357\363\357\377\354\357\354\377\352\355\351\377\353\357" - "\354\377\353\356\352\377\356\361\356\377\354\357\354\377\353\356\352\377" - "\352\356\353\377\352\356\353\377\354\357\354\377\355\360\355\377\353\357" - "\354\377\353\360\356\377\353\357\354\377\353\357\354\377\352\356\353\377" - "\353\356\352\377\354\357\354\377\355\360\355\377\357\363\357\377\360\365" - "\360\377\360\363\356\377\355\360\355\377\354\360\355\377\355\361\356\377" - "\354\357\354\377\354\360\355\377\354\360\355\377\354\360\355\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\354\357\354\377\355\360\355\377" - "\356\361\356\377\355\364\360\377\354\363\357\377\353\364\361\377\355\364" - "\360\377\351\356\354\377\351\356\354\377\351\356\354\377\354\360\355\377" - "\353\357\354\377\354\360\355\377\353\356\352\377\350\354\350\377\353\356" - "\352\377\352\355\351\377\360\365\360\377\353\356\352\377\357\363\357\377" - "\356\361\356\377\356\363\357\377\356\363\357\377\356\361\356\377\354\360" - "\355\377\353\356\352\377\353\356\352\377\352\355\351\377\352\356\353\377" - "\353\357\354\377\352\356\353\377\352\356\353\377\352\356\353\377\351\355" - "\352\377\350\355\353\377\354\360\356\377\354\360\355\377\360\365\360\377" - "\356\361\356\377\361\366\363\377\356\363\357\377\356\363\357\377\355\361" - "\356\377\354\360\355\377\355\361\356\377\355\360\355\377\355\360\355\377" - "\353\356\352\377\352\356\353\377\346\354\352\377\350\356\355\377\352\357" - "\355\377\352\357\355\377\353\360\356\377\352\356\353\377\352\356\353\377" - "\352\356\353\377\351\355\352\377\352\355\351\377\353\356\352\377\353\357" - "\354\377\352\356\353\377\352\356\353\377\354\360\355\377\347\353\347\377" - "\354\357\354\377\355\360\355\377\353\356\353\377\354\357\353\377\356\360" - "\354\377\356\360\354\377\355\360\355\377\355\357\353\377\353\356\352\377" - "\355\357\353\377\353\356\352\377\356\360\354\377\356\360\354\377\357\361" - "\355\377\356\361\356\377\355\360\355\377\354\357\354\377\353\356\352\377" - "\353\356\352\377\354\357\354\377\354\357\354\377\354\357\354\377\356\360" - "\354\377\357\361\355\377\355\357\353\377\355\357\353\377\356\360\354\377" - "\356\360\354\377\357\361\355\377\357\363\357\377\356\363\357\377\357\363" - "\357\377\356\361\356\377\353\356\352\377\353\356\352\377\356\360\354\377" - "\356\360\354\377\355\360\354\377\355\360\355\377\352\356\353\377\353\357" - "\354\377\353\360\356\377\352\357\355\377\352\357\355\377\352\356\353\377" - "\352\356\353\377\353\357\354\377\355\361\356\377\353\357\354\377\355\360" - "\355\377\357\363\357\377\356\361\356\377\355\360\355\377\354\357\354\377" - "\354\360\355\377\354\360\355\377\355\361\356\377\355\361\356\377\356\363" - "\357\377\354\360\355\377\354\360\355\377\353\357\354\377\355\360\355\377" - "\356\361\356\377\354\357\354\377\356\361\356\377\356\363\357\377\353\361" - "\356\377\356\363\357\377\355\360\355\377\347\355\353\377\351\356\354\377" - "\352\356\353\377\352\356\353\377\353\357\354\377\352\356\353\377\353\357" - "\354\377\354\360\355\377\351\355\352\377\353\356\352\377\352\356\353\377" - "\354\357\354\377\352\356\353\377\353\356\352\377\355\360\355\377\357\363" - "\357\377\355\360\355\377\354\360\355\377\356\361\356\377\356\361\356\377" - "\354\357\354\377\354\360\355\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\352\356\353\377\352\356\353\377\351\356\354\377\353\357\354\377" - "\355\361\356\377\353\356\352\377\352\356\353\377\351\355\352\377\351\355" - "\352\377\347\353\350\377\353\356\352\377\353\357\354\377\353\357\354\377" - "\352\356\353\377\351\355\352\377\350\354\351\377\351\355\352\377\346\354" - "\351\377\351\356\354\377\347\355\353\377\351\356\354\377\351\356\354\377" - "\346\354\352\377\352\356\353\377\351\355\352\377\352\356\353\377\353\356" - "\352\377\353\357\354\377\352\355\351\377\351\355\352\377\355\360\355\377" - "\354\357\354\377\356\361\356\377\357\361\355\377\354\357\353\377\347\353" - "\347\377\330\333\325\377\337\342\336\377\352\355\351\377\355\357\353\377" - "\355\357\353\377\355\357\353\377\352\355\351\377\355\357\353\377\353\356" - "\352\377\355\357\353\377\355\360\355\377\354\357\354\377\355\360\355\377" - "\356\361\356\377\356\361\356\377\355\360\355\377\355\360\355\377\356\361" - "\356\377\356\361\356\377\356\360\354\377\356\361\356\377\360\363\356\377" - "\360\363\356\377\357\361\355\377\360\363\356\377\357\363\357\377\354\360" - "\355\377\354\360\355\377\355\360\355\377\355\360\355\377\355\360\355\377" - "\355\357\353\377\356\360\354\377\356\360\354\377\354\357\354\377\352\356" - "\353\377\351\355\352\377\351\355\352\377\351\356\354\377\352\357\355\377" - "\352\357\355\377\354\360\355\377\354\360\355\377\352\355\351\377\354\357" - "\354\377\354\357\354\377\354\357\354\377\356\361\356\377\354\360\355\377" - "\356\363\357\377\354\360\355\377\355\361\356\377\353\357\354\377\355\360" - "\355\377\355\361\356\377\357\363\357\377\357\363\357\377\357\363\357\377" - "\355\361\356\377\355\360\355\377\355\360\355\377\354\357\354\377\354\357" - "\354\377\354\360\355\377\354\360\355\377\355\361\356\377\357\363\357\377" - "\361\366\363\377\355\361\356\377\356\361\356\377\354\360\355\377\354\357" - "\354\377\351\355\352\377\347\355\353\377\353\357\354\377\353\357\354\377" - "\352\355\351\377\353\356\352\377\351\355\352\377\351\355\352\377\350\354" - "\351\377\350\354\350\377\352\356\353\377\351\355\352\377\351\355\352\377" - "\355\360\355\377\354\360\355\377\353\357\354\377\351\356\354\377\355\361" - "\356\377\353\357\354\377\355\361\356\377\354\360\355\377\353\357\354\377" - "\354\363\357\377\354\360\355\377\355\361\356\377\353\357\354\377\355\361" - "\356\377\354\360\355\377\354\360\355\377\353\357\354\377\353\356\352\377" - "\353\357\354\377\354\360\355\377\353\357\354\377\352\356\353\377\350\354" - "\351\377\347\355\353\377\351\357\355\377\351\356\354\377\346\354\351\377" - "\351\356\354\377\347\355\353\377\347\355\353\377\346\354\352\377\350\354" - "\351\377\352\356\353\377\353\356\352\377\352\356\353\377\352\355\351\377" - "\347\355\353\377\354\357\354\377\355\360\355\377\355\360\355\377\364\366" - "\360\377\367\370\363\377\371\372\365\377\356\360\354\377\346\351\346\377" - "\355\357\353\377\355\360\355\377\355\360\355\377\354\357\354\377\353\356" - "\352\377\353\356\352\377\353\356\352\377\352\355\351\377\356\360\354\377" - "\355\360\355\377\355\360\355\377\354\357\354\377\356\360\354\377\354\357" - "\354\377\355\360\355\377\356\361\356\377\353\357\354\377\355\360\355\377" - "\356\361\356\377\357\361\355\377\357\361\355\377\357\361\355\377\356\361" - "\356\377\356\363\357\377\355\361\356\377\353\357\354\377\354\360\355\377" - "\353\356\352\377\353\356\352\377\353\356\352\377\353\356\352\377\354\357" - "\354\377\354\360\355\377\354\360\355\377\356\361\356\377\353\360\356\377" - "\352\357\355\377\354\363\357\377\354\360\355\377\354\360\355\377\353\357" - "\354\377\357\363\357\377\356\361\356\377\355\357\353\377\355\360\355\377" - "\353\357\354\377\355\361\356\377\355\361\356\377\354\360\355\377\354\360" - "\355\377\354\363\357\377\354\363\357\377\354\360\355\377\356\361\356\377" - "\360\363\356\377\356\361\356\377\356\363\357\377\357\363\357\377\356\361" - "\356\377\355\360\355\377\357\363\357\377\354\360\355\377\355\361\356\377" - "\357\363\357\377\356\361\356\377\360\363\356\377\356\361\356\377\354\360" - "\355\377\354\360\355\377\352\356\353\377\351\355\352\377\352\356\353\377" - "\353\357\354\377\352\356\353\377\354\357\354\377\353\356\352\377\351\355" - "\352\377\351\356\354\377\351\356\354\377\354\357\354\377\353\356\352\377" - "\352\355\351\377\351\355\352\377\351\355\352\377\354\360\355\377\354\357" - "\354\377\354\363\357\377\354\360\355\377\354\360\355\377\353\357\354\377" - "\354\360\355\377\352\356\353\377\353\360\356\377\354\357\354\377\353\360" - "\356\377\353\357\354\377\352\357\355\377\353\357\354\377\355\360\355\377" - "\354\357\354\377\353\356\352\377\353\356\352\377\352\356\353\377\353\357" - "\354\377\352\356\353\377\352\356\353\377\351\357\355\377\352\356\353\377" - "\347\355\353\377\352\356\353\377\351\355\352\377\352\356\353\377\351\355" - "\352\377\346\354\352\377\345\353\350\377\347\355\353\377\353\356\352\377" - "\352\356\353\377\351\355\352\377\351\355\352\377\351\355\352\377\354\360" - "\355\377\356\361\356\377\370\373\370\377\376\377\373\377\377\377\377\377" - "\377\377\375\377\361\363\355\377\361\365\357\377\355\357\353\377\353\355" - "\350\377\351\354\350\377\352\355\351\377\353\356\352\377\352\355\351\377" - "\351\354\350\377\350\353\347\377\352\355\351\377\353\356\352\377\353\356" - "\352\377\354\357\354\377\355\360\355\377\356\361\356\377\354\360\355\377" - "\354\357\354\377\353\357\354\377\355\360\355\377\355\360\355\377\354\357" - "\354\377\356\360\354\377\354\360\355\377\355\361\356\377\354\360\355\377" - "\354\360\355\377\354\360\355\377\353\356\352\377\354\357\354\377\353\356" - "\352\377\353\356\352\377\352\356\353\377\354\360\355\377\353\357\354\377" - "\356\361\356\377\351\355\352\377\351\355\352\377\352\356\353\377\352\356" - "\353\377\354\360\355\377\352\356\353\377\360\363\356\377\360\365\360\377" - "\357\363\357\377\356\360\354\377\355\357\353\377\354\360\355\377\356\361" - "\356\377\354\360\355\377\354\360\355\377\353\361\356\377\352\357\355\377" - "\354\360\355\377\355\361\356\377\356\361\356\377\355\360\355\377\355\361" - "\356\377\357\363\357\377\360\365\360\377\355\360\355\377\356\363\357\377" - "\355\360\355\377\355\361\356\377\354\360\355\377\355\361\356\377\357\363" - "\357\377\354\360\355\377\355\361\356\377\353\357\354\377\350\354\350\377" - "\344\350\344\377\351\355\352\377\352\356\353\377\353\357\354\377\353\356" - "\352\377\354\360\355\377\353\357\354\377\352\356\353\377\353\357\354\377" - "\353\356\352\377\352\356\353\377\352\355\351\377\350\354\351\377\352\356" - "\353\377\352\356\353\377\353\357\354\377\354\360\355\377\356\361\356\377" - "\355\360\355\377\356\361\356\377\356\361\356\377\356\361\356\377\356\361" - "\356\377\354\357\354\377\354\360\355\377\353\357\354\377\352\356\353\377" - "\352\356\353\377\355\360\355\377\354\357\354\377\352\356\353\377\352\356" - "\353\377\352\356\353\377\354\357\354\377\352\356\353\377\353\356\352\377" - "\353\356\352\377\351\355\352\377\352\356\353\377\347\355\353\377\352\356" - "\353\377\351\355\352\377\350\354\351\377\345\353\350\377\347\353\350\377" - "\347\353\350\377\350\354\351\377\351\355\352\377\351\356\354\377\347\355" - "\353\377\347\355\353\377\347\355\353\377\355\361\356\377\360\365\361\377" - "\366\371\366\377\371\373\367\377\373\374\370\377\367\371\364\377\364\366" - "\360\377\361\363\355\377\356\360\354\377\353\356\352\377\352\355\351\377" - "\353\356\352\377\353\356\352\377\352\355\351\377\353\356\352\377\355\357" - "\353\377\355\357\353\377\352\355\351\377\351\355\352\377\352\355\351\377" - "\353\356\352\377\355\360\355\377\354\357\354\377\354\357\354\377\353\357" - "\354\377\354\357\354\377\353\357\354\377\354\357\354\377\353\356\352\377" - "\354\357\354\377\353\357\354\377\353\356\352\377\354\357\354\377\355\360" - "\355\377\354\357\354\377\355\360\355\377\355\360\355\377\353\357\354\377" - "\353\357\354\377\353\357\354\377\354\356\351\377\352\356\353\377\351\355" - "\352\377\352\356\353\377\353\357\354\377\354\360\355\377\355\361\356\377" - "\356\361\356\377\357\363\357\377\360\365\360\377\356\361\356\377\354\360" - "\355\377\353\357\354\377\354\357\354\377\354\360\355\377\353\360\356\377" - "\353\357\354\377\353\360\356\377\353\357\354\377\352\357\355\377\353\357" - "\354\377\353\356\352\377\356\360\354\377\356\360\354\377\360\363\356\377" - "\356\361\356\377\356\363\357\377\356\361\356\377\354\357\354\377\355\360" - "\355\377\355\360\355\377\355\360\355\377\355\361\356\377\355\361\356\377" - "\355\361\356\377\355\357\353\377\353\356\352\377\352\355\351\377\351\355" - "\352\377\352\355\351\377\354\357\354\377\354\357\354\377\353\356\352\377" - "\353\357\354\377\352\356\353\377\352\356\353\377\354\357\354\377\354\357" - "\354\377\354\357\354\377\352\355\351\377\352\356\353\377\353\357\354\377" - "\352\356\353\377\353\357\354\377\354\357\354\377\356\361\356\377\356\361" - "\356\377\355\361\356\377\354\360\355\377\355\361\356\377\356\361\356\377" - "\353\357\354\377\352\356\353\377\355\361\356\377\355\360\355\377\354\360" - "\355\377\355\360\355\377\355\360\355\377\354\360\355\377\354\357\354\377" - "\347\353\350\377\350\354\350\377\350\354\350\377\353\356\352\377\350\354" - "\350\377\351\355\352\377\352\355\351\377\353\356\352\377\350\354\351\377" - "\347\353\350\377\350\354\351\377\350\354\351\377\351\355\352\377\350\354" - "\351\377\347\356\355\377\351\356\354\377\347\356\355\377\353\357\354\377" - "\350\354\351\377\357\363\357\377\364\367\363\377\365\370\364\377\360\361" - "\355\377\356\357\352\377\356\360\354\377\363\365\357\377\360\363\356\377" - "\360\363\356\377\360\363\356\377\355\360\355\377\354\357\354\377\352\355" - "\351\377\355\357\353\377\353\356\352\377\355\360\355\377\355\357\353\377" - "\352\356\353\377\352\355\351\377\351\355\352\377\352\355\351\377\354\357" - "\354\377\355\360\355\377\355\360\355\377\355\360\355\377\353\360\356\377" - "\354\360\355\377\354\357\354\377\353\357\354\377\353\357\354\377\354\357" - "\354\377\355\361\356\377\356\361\356\377\353\357\354\377\353\356\352\377" - "\354\357\354\377\351\356\354\377\354\360\355\377\356\363\357\377\360\363" - "\356\377\356\361\356\377\354\360\355\377\355\361\356\377\353\357\354\377" - "\353\357\354\377\354\360\355\377\356\361\356\377\357\363\357\377\356\363" - "\357\377\354\360\355\377\353\356\352\377\353\356\352\377\353\357\354\377" - "\354\360\355\377\353\357\354\377\353\357\354\377\351\360\356\377\354\363" - "\357\377\352\357\355\377\353\357\354\377\355\357\353\377\355\360\355\377" - "\356\360\354\377\355\360\355\377\355\360\355\377\355\361\356\377\357\361" - "\355\377\357\361\355\377\357\361\355\377\354\357\354\377\355\357\353\377" - "\356\361\356\377\356\361\356\377\357\363\357\377\355\357\353\377\347\351" - "\345\377\353\356\352\377\352\355\351\377\355\360\355\377\354\357\354\377" - "\354\357\354\377\353\356\352\377\352\355\351\377\351\355\352\377\352\356" - "\353\377\351\355\352\377\354\360\355\377\354\357\354\377\352\355\351\377" - "\351\355\352\377\353\357\354\377\353\357\354\377\351\355\352\377\355\360" - "\355\377\356\361\356\377\355\360\355\377\354\360\355\377\354\360\355\377" - "\354\360\355\377\355\361\356\377\355\360\355\377\355\360\355\377\354\360" - "\355\377\354\360\355\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\356\360\354\377\352\355\351\377\346\351\346\377\353\356\352\377\354\357" - "\354\377\353\356\352\377\350\354\350\377\351\355\352\377\352\355\351\377" - "\354\357\354\377\353\356\352\377\352\356\353\377\351\355\352\377\352\355" - "\351\377\353\356\352\377\353\356\352\377\347\355\353\377\347\355\353\377" - "\347\355\353\377\352\356\353\377\355\360\355\377\356\361\356\377\356\361" - "\356\377\361\365\360\377\361\363\356\377\356\360\354\377\360\361\355\377" - "\355\357\353\377\357\361\355\377\356\360\354\377\356\360\354\377\356\360" - "\354\377\355\360\355\377\355\357\353\377\355\360\355\377\356\360\354\377" - "\355\360\355\377\354\357\354\377\350\354\350\377\351\355\352\377\350\354" - "\350\377\353\356\352\377\353\356\352\377\355\360\355\377\355\360\355\377" - "\354\357\354\377\355\361\356\377\355\360\355\377\355\360\355\377\357\361" - "\355\377\354\357\354\377\355\357\353\377\355\360\355\377\354\357\354\377" - "\356\361\356\377\354\357\354\377\354\357\354\377\353\357\354\377\353\357" - "\354\377\356\361\356\377\356\361\356\377\356\361\356\377\355\361\356\377" - "\355\361\356\377\355\361\356\377\355\361\356\377\354\360\355\377\355\361" - "\356\377\355\361\356\377\357\363\357\377\357\363\357\377\357\363\357\377" - "\356\363\357\377\356\361\356\377\354\360\355\377\355\361\356\377\354\360" - "\355\377\351\356\354\377\353\357\354\377\355\364\360\377\360\365\361\377" - "\361\365\360\377\360\363\356\377\360\365\360\377\357\363\357\377\355\360" - "\355\377\355\360\355\377\356\363\357\377\357\361\355\377\356\360\354\377" - "\354\357\354\377\356\360\354\377\357\363\357\377\357\363\357\377\356\361" - "\356\377\371\373\367\377\353\355\350\377\351\354\350\377\352\355\351\377" - "\354\357\354\377\355\357\353\377\356\360\354\377\355\360\355\377\352\355" - "\351\377\354\357\354\377\353\357\354\377\354\357\354\377\353\357\354\377" - "\350\354\351\377\350\354\350\377\352\355\351\377\351\355\352\377\353\357" - "\354\377\353\357\354\377\354\357\354\377\354\357\354\377\355\360\355\377" - "\353\357\354\377\356\363\357\377\355\361\356\377\356\361\356\377\356\361" - "\356\377\353\356\352\377\353\356\352\377\352\355\351\377\352\356\353\377" - "\355\361\356\377\360\365\361\377\364\367\363\377\361\363\356\377\352\353" - "\346\377\334\336\330\377\342\345\341\377\347\353\350\377\354\357\354\377" - "\351\355\352\377\354\357\354\377\354\357\354\377\353\356\352\377\353\356" - "\352\377\351\355\352\377\352\356\353\377\354\357\354\377\353\356\352\377" - "\351\355\352\377\351\355\352\377\352\356\353\377\353\357\354\377\354\360" - "\355\377\353\356\352\377\356\360\354\377\360\363\356\377\356\360\354\377" - "\355\357\353\377\356\360\354\377\355\357\353\377\354\357\354\377\354\357" - "\354\377\353\356\352\377\354\357\354\377\353\356\352\377\353\356\352\377" - "\353\356\352\377\355\360\355\377\355\360\355\377\354\357\354\377\355\360" - "\355\377\353\357\354\377\353\357\354\377\353\357\354\377\353\357\354\377" - "\355\360\355\377\355\360\355\377\356\361\356\377\355\361\356\377\356\360" - "\354\377\356\361\356\377\357\361\355\377\354\360\355\377\356\361\356\377" - "\355\360\355\377\353\356\352\377\353\356\352\377\353\357\354\377\354\357" - "\354\377\353\357\354\377\354\357\354\377\353\357\354\377\353\357\354\377" - "\355\360\355\377\355\360\355\377\354\357\354\377\353\357\354\377\354\360" - "\355\377\355\361\356\377\354\360\355\377\355\361\356\377\355\360\355\377" - "\355\361\356\377\356\361\356\377\356\361\356\377\355\360\355\377\354\357" - "\354\377\355\364\360\377\353\360\356\377\354\363\357\377\353\357\354\377" - "\354\360\355\377\356\363\357\377\360\365\361\377\357\363\357\377\360\365" - "\361\377\356\363\357\377\357\363\357\377\355\361\356\377\355\361\356\377" - "\356\363\357\377\354\357\354\377\357\363\357\377\357\361\355\377\357\363" - "\357\377\357\363\357\377\357\363\357\377\361\365\360\377\360\361\355\377" - "\353\356\352\377\353\356\352\377\355\360\355\377\353\356\352\377\355\360" - "\355\377\353\356\352\377\352\355\351\377\353\356\352\377\354\357\354\377" - "\354\357\354\377\354\360\355\377\352\356\353\377\351\354\350\377\352\355" - "\351\377\347\355\353\377\352\356\353\377\352\357\355\377\352\356\353\377" - "\354\360\355\377\355\360\355\377\355\360\355\377\354\363\357\377\357\363" - "\357\377\357\363\357\377\355\357\353\377\351\354\350\377\351\355\352\377" - "\346\351\346\377\351\355\352\377\355\360\355\377\361\366\361\377\363\366" - "\361\377\374\376\371\377\364\365\357\377\353\355\350\377\347\351\345\377" - "\352\355\351\377\352\356\353\377\351\355\352\377\350\354\350\377\352\355" - "\351\377\353\356\352\377\350\354\350\377\352\355\351\377\351\355\352\377" - "\353\356\352\377\353\356\352\377\354\357\354\377\354\357\354\377\354\357" - "\354\377\355\360\355\377\352\356\353\377\353\357\354\377\354\357\354\377" - "\354\357\354\377\353\356\352\377\354\357\354\377\356\361\356\377\353\357" - "\354\377\356\360\354\377\357\361\355\377\354\357\354\377\353\356\352\377" - "\350\354\350\377\352\355\351\377\352\355\351\377\354\357\354\377\355\360" - "\355\377\356\360\354\377\356\360\354\377\352\355\351\377\352\357\355\377" - "\352\356\353\377\352\356\353\377\354\360\355\377\355\360\355\377\355\357" - "\353\377\354\360\355\377\355\357\353\377\356\361\356\377\355\360\355\377" - "\355\361\356\377\356\360\354\377\355\360\355\377\354\360\355\377\353\357" - "\354\377\354\360\355\377\356\361\356\377\356\361\356\377\356\361\356\377" - "\357\361\355\377\355\360\355\377\354\357\354\377\355\360\355\377\355\360" - "\355\377\353\357\354\377\353\357\354\377\353\357\354\377\353\357\354\377" - "\353\357\354\377\356\361\356\377\353\357\354\377\355\361\356\377\355\361" - "\356\377\354\360\355\377\354\360\355\377\354\363\357\377\355\364\360\377" - "\356\363\357\377\356\363\357\377\354\363\357\377\353\360\356\377\355\364" - "\360\377\355\361\356\377\356\363\357\377\355\361\356\377\355\361\356\377" - "\354\360\355\377\354\360\355\377\355\364\360\377\355\361\356\377\355\361" - "\356\377\355\361\356\377\355\361\356\377\357\363\357\377\360\365\360\377" - "\360\365\360\377\361\366\361\377\356\363\357\377\353\356\352\377\354\357" - "\354\377\353\356\352\377\354\357\354\377\353\356\352\377\355\360\355\377" - "\352\355\351\377\352\355\351\377\347\355\353\377\354\360\355\377\355\357" - "\353\377\353\356\352\377\354\357\354\377\347\355\353\377\351\355\352\377" - "\347\355\353\377\352\356\353\377\352\356\353\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\356\361\356\377\354\357\354\377\353\356\352\377" - "\351\354\350\377\352\355\351\377\353\355\350\377\353\356\352\377\356\360" - "\354\377\357\361\355\377\364\367\363\377\367\372\367\377\377\377\374\377" - "\373\374\370\377\364\367\363\377\354\357\354\377\350\354\350\377\351\356" - "\354\377\352\356\353\377\354\356\351\377\352\355\351\377\350\354\350\377" - "\352\355\351\377\353\357\354\377\353\357\354\377\351\357\355\377\353\356" - "\352\377\354\357\354\377\354\357\354\377\355\360\355\377\352\356\353\377" - "\352\355\351\377\352\355\351\377\353\356\352\377\355\357\353\377\354\357" - "\354\377\356\360\354\377\354\360\355\377\357\361\355\377\357\361\355\377" - "\355\360\355\377\353\356\352\377\353\356\352\377\352\355\351\377\352\355" - "\351\377\353\356\352\377\356\360\354\377\353\356\352\377\354\357\353\377" - "\352\356\353\377\352\356\353\377\351\356\354\377\353\356\352\377\353\357" - "\354\377\355\360\355\377\354\360\355\377\356\361\356\377\356\360\354\377" - "\354\357\354\377\353\357\354\377\355\361\356\377\355\361\356\377\354\357" - "\354\377\352\356\353\377\354\360\355\377\353\357\354\377\354\360\355\377" - "\354\360\355\377\356\363\357\377\357\361\355\377\357\361\355\377\355\357" - "\353\377\355\360\355\377\355\360\355\377\354\357\354\377\353\357\354\377" - "\354\357\354\377\354\360\355\377\355\361\356\377\355\361\356\377\354\360" - "\355\377\355\361\356\377\353\357\354\377\355\361\356\377\354\360\355\377" - "\355\361\356\377\355\361\356\377\355\361\356\377\354\360\355\377\352\357" - "\355\377\353\360\356\377\353\360\356\377\354\363\357\377\360\365\361\377" - "\360\365\360\377\356\363\357\377\356\363\357\377\350\360\356\377\353\360" - "\356\377\353\360\356\377\355\360\355\377\355\361\356\377\355\361\356\377" - "\354\360\355\377\356\361\356\377\357\363\357\377\356\361\356\377\355\361" - "\356\377\355\360\355\377\355\360\355\377\355\360\355\377\356\361\356\377" - "\356\360\354\377\352\355\351\377\352\355\351\377\351\355\352\377\347\357" - "\355\377\351\357\355\377\352\356\353\377\353\357\354\377\353\357\354\377" - "\353\361\356\377\353\357\354\377\353\357\354\377\352\356\353\377\352\356" - "\353\377\351\355\352\377\352\356\353\377\354\360\355\377\354\357\354\377" - "\354\357\354\377\357\361\355\377\360\363\356\377\363\366\361\377\357\361" - "\355\377\355\360\355\377\356\360\354\377\360\365\360\377\357\363\357\377" - "\361\366\361\377\361\366\361\377\363\366\361\377\360\365\360\377\356\360" - "\354\377\354\357\354\377\355\360\355\377\350\354\350\377\352\355\351\377" - "\353\356\352\377\350\354\350\377\352\355\351\377\352\356\353\377\351\355" - "\352\377\352\356\353\377\352\355\351\377\353\356\352\377\353\357\354\377" - "\355\360\355\377\360\363\356\377\361\365\360\377\357\361\355\377\353\356" - "\352\377\354\357\354\377\354\357\354\377\354\357\354\377\356\361\356\377" - "\356\360\354\377\356\361\356\377\356\360\354\377\356\360\354\377\353\356" - "\352\377\352\355\351\377\350\354\350\377\352\355\351\377\353\357\354\377" - "\352\355\351\377\352\355\351\377\352\355\351\377\354\357\354\377\354\357" - "\354\377\352\355\351\377\352\355\351\377\352\355\351\377\352\355\351\377" - "\357\361\355\377\357\361\355\377\354\357\354\377\352\356\353\377\354\360" - "\355\377\357\361\355\377\357\361\355\377\353\356\352\377\353\357\354\377" - "\353\357\354\377\355\361\356\377\354\360\355\377\354\357\354\377\355\357" - "\353\377\356\361\356\377\355\360\355\377\354\357\354\377\354\360\355\377" - "\354\360\355\377\355\361\356\377\355\360\355\377\354\360\355\377\355\360" - "\355\377\355\360\355\377\355\361\356\377\353\357\354\377\353\357\354\377" - "\354\360\355\377\355\361\356\377\355\361\356\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\354\360\355\377\353\360\356\377\354\363\357\377" - "\356\363\357\377\355\361\356\377\357\363\357\377\357\363\357\377\357\363" - "\357\377\353\360\356\377\351\361\357\377\351\361\357\377\355\364\360\377" - "\356\363\357\377\354\363\357\377\353\360\356\377\355\364\360\377\356\363" - "\357\377\354\360\355\377\355\361\356\377\354\360\355\377\356\361\356\377" - "\356\361\356\377\360\365\360\377\360\363\356\377\355\357\353\377\350\353" - "\347\377\353\356\352\377\351\356\354\377\347\355\353\377\351\355\352\377" - "\352\356\353\377\351\355\352\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\353\357\354\377\354\360\355\377\353\357\354\377\351\360\356\377" - "\354\360\355\377\356\361\356\377\354\357\354\377\356\361\356\377\357\363" - "\357\377\360\365\360\377\356\361\356\377\356\361\356\377\354\357\354\377" - "\354\357\354\377\354\363\357\377\355\361\356\377\360\365\360\377\357\363" - "\357\377\357\363\357\377\356\360\354\377\352\355\351\377\353\356\352\377" - "\347\353\347\377\352\355\351\377\352\356\353\377\351\355\352\377\351\355" - "\352\377\351\355\352\377\351\355\352\377\352\356\353\377\351\357\355\377" - "\353\357\354\377\354\360\355\377\355\360\355\377\357\361\355\377\360\363" - "\356\377\363\365\357\377\361\365\360\377\354\357\354\377\354\357\354\377" - "\354\357\354\377\354\357\354\377\355\360\355\377\355\360\355\377\353\356" - "\352\377\353\356\352\377\350\354\350\377\350\354\350\377\351\355\352\377" - "\353\356\352\377\354\357\354\377\353\356\352\377\352\355\351\377\352\355" - "\351\377\352\355\351\377\353\356\352\377\353\356\352\377\354\357\354\377" - "\352\356\353\377\353\356\352\377\355\360\354\377\360\363\356\377\355\360" - "\355\377\354\357\354\377\353\357\354\377\355\361\356\377\355\361\356\377" - "\352\356\353\377\353\357\354\377\352\356\353\377\353\357\354\377\354\363" - "\357\377\353\357\354\377\355\360\355\377\354\357\354\377\354\357\354\377" - "\353\357\354\377\352\356\353\377\352\356\353\377\354\360\355\377\354\357" - "\354\377\354\360\355\377\354\357\354\377\355\361\356\377\354\360\355\377" - "\354\360\355\377\354\360\355\377\354\360\355\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\353\356\352\377\353\357\354\377\355\361\356\377" - "\356\363\357\377\355\364\360\377\354\363\357\377\354\363\357\377\357\363" - "\357\377\355\360\355\377\355\360\355\377\355\361\356\377\355\364\360\377" - "\356\363\357\377\351\361\357\377\354\360\355\377\354\363\357\377\353\360" - "\356\377\354\360\355\377\356\363\357\377\355\361\356\377\356\363\357\377" - "\354\360\355\377\355\360\355\377\357\363\357\377\357\363\357\377\357\361" - "\355\377\356\360\354\377\352\354\347\377\350\353\347\377\347\353\347\377" - "\351\355\352\377\351\355\352\377\352\356\353\377\347\355\353\377\354\360" - "\355\377\354\360\355\377\354\360\355\377\352\356\353\377\354\360\355\377" - "\352\356\353\377\351\356\354\377\355\361\356\377\357\363\357\377\356\361" - "\356\377\356\363\357\377\355\361\356\377\360\365\361\377\354\360\355\377" - "\355\360\355\377\354\357\354\377\353\357\354\377\352\356\353\377\353\357" - "\354\377\357\361\355\377\357\363\357\377\357\363\357\377\357\361\355\377" - "\356\360\354\377\340\342\335\377\341\343\337\377\353\356\352\377\352\355" - "\351\377\353\357\354\377\351\355\352\377\353\357\354\377\353\357\354\377" - "\352\356\353\377\353\360\356\377\351\357\355\377\353\357\354\377\356\360" - "\354\377\357\361\355\377\363\366\361\377\357\363\357\377\357\363\357\377" - "\354\357\354\377\355\360\355\377\355\360\355\377\354\357\354\377\354\357" - "\354\377\354\357\353\377\353\356\352\377\351\354\350\377\347\353\347\377" - "\352\355\351\377\351\355\352\377\351\355\352\377\354\360\355\377\352\356" - "\353\377\353\356\352\377\353\356\352\377\353\356\352\377\355\360\355\377" - "\356\360\354\377\354\357\354\377\353\356\352\377\354\357\354\377\355\360" - "\355\377\355\360\355\377\356\361\356\377\356\361\356\377\353\357\354\377" - "\353\357\354\377\353\357\354\377\353\356\352\377\352\356\353\377\354\360" - "\355\377\353\357\354\377\352\357\355\377\355\357\353\377\357\361\355\377" - "\357\361\355\377\354\357\354\377\353\356\352\377\351\355\352\377\353\356" - "\352\377\354\357\354\377\353\356\352\377\355\360\355\377\355\360\355\377" - "\355\360\355\377\356\361\356\377\355\361\356\377\353\357\354\377\353\357" - "\354\377\353\361\356\377\353\357\354\377\353\357\354\377\350\354\351\377" - "\352\356\353\377\351\355\352\377\352\356\353\377\353\357\354\377\355\361" - "\356\377\356\363\357\377\355\361\356\377\355\360\355\377\354\360\355\377" - "\356\361\356\377\356\363\357\377\356\363\357\377\355\364\360\377\355\361" - "\356\377\355\364\360\377\353\357\354\377\353\357\354\377\354\360\355\377" - "\355\361\356\377\354\360\355\377\353\357\354\377\355\360\355\377\356\361" - "\356\377\356\361\356\377\360\363\356\377\363\366\361\377\361\365\360\377" - "\356\360\354\377\356\360\354\377\355\361\356\377\352\356\353\377\351\355" - "\352\377\353\357\354\377\350\356\354\377\353\361\356\377\353\357\354\377" - "\351\355\352\377\352\356\353\377\352\356\353\377\352\356\353\377\354\360" - "\355\377\355\360\355\377\353\356\352\377\353\357\354\377\353\357\354\377" - "\353\357\354\377\356\360\354\377\354\357\354\377\354\357\354\377\355\361" - "\356\377\354\357\354\377\353\357\354\377\354\357\354\377\356\361\356\377" - "\360\365\360\377\364\367\363\377\361\365\360\377\356\360\354\377\342\345" - "\341\377\353\356\352\377\352\355\351\377\353\357\354\377\353\357\354\377" - "\354\360\355\377\353\357\354\377\353\357\354\377\352\356\353\377\352\356" - "\353\377\354\360\355\377\356\360\354\377\353\356\352\377\353\356\352\377" - "\356\361\356\377\357\363\357\377\355\360\355\377\356\361\356\377\354\357" - "\354\377\354\357\354\377\354\357\354\377\356\361\356\377\357\363\357\377" - "\356\361\356\377\361\365\360\377\355\360\355\377\354\360\355\377\354\360" - "\355\377\356\361\356\377\355\361\356\377\355\360\355\377\355\360\355\377" - "\356\361\356\377\355\360\355\377\356\361\356\377\356\361\356\377\354\360" - "\355\377\356\361\356\377\354\357\354\377\355\360\355\377\356\361\356\377" - "\354\357\354\377\353\356\352\377\355\360\355\377\352\356\353\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\354\357\354\377\356\361\356\377" - "\357\361\355\377\357\361\355\377\357\363\357\377\360\363\356\377\355\361" - "\356\377\356\361\356\377\354\357\354\377\355\360\355\377\355\360\355\377" - "\355\361\356\377\355\360\355\377\355\360\355\377\355\360\355\377\355\361" - "\356\377\355\361\356\377\354\360\355\377\356\363\357\377\354\363\357\377" - "\356\363\357\377\350\354\351\377\344\350\345\377\345\351\346\377\352\356" - "\353\377\353\357\354\377\353\357\354\377\355\361\356\377\356\363\357\377" - "\357\363\357\377\355\361\356\377\355\361\356\377\355\361\356\377\356\363" - "\357\377\356\363\357\377\356\363\357\377\357\363\357\377\356\361\356\377" - "\353\357\354\377\354\360\355\377\356\361\356\377\353\357\354\377\354\360" - "\355\377\356\361\356\377\353\357\354\377\356\361\356\377\357\363\357\377" - "\360\365\360\377\357\363\357\377\364\367\363\377\360\363\356\377\357\363" - "\357\377\352\357\355\377\347\355\353\377\351\356\354\377\351\357\355\377" - "\351\361\357\377\352\357\355\377\353\360\356\377\354\360\355\377\354\360" - "\355\377\352\356\353\377\353\356\352\377\353\356\352\377\353\356\352\377" - "\352\356\353\377\353\356\352\377\353\360\356\377\354\357\354\377\355\360" - "\355\377\353\356\352\377\355\361\356\377\356\363\357\377\352\357\355\377" - "\354\357\354\377\354\360\355\377\360\365\361\377\367\372\367\377\372\376" - "\372\377\376\377\375\377\367\371\365\377\360\363\356\377\354\357\354\377" - "\352\356\353\377\352\356\353\377\350\356\354\377\353\357\354\377\354\360" - "\355\377\352\356\353\377\354\360\355\377\355\361\356\377\353\356\352\377" - "\352\355\351\377\354\357\354\377\353\356\352\377\354\363\357\377\355\361" - "\356\377\361\365\360\377\360\363\356\377\356\361\356\377\357\363\357\377" - "\356\361\356\377\356\361\356\377\357\363\357\377\356\361\356\377\355\361" - "\356\377\355\361\356\377\354\360\355\377\355\361\356\377\355\361\356\377" - "\356\361\356\377\354\360\355\377\353\356\352\377\353\357\354\377\355\360" - "\355\377\354\357\354\377\356\361\356\377\356\361\356\377\357\363\357\377" - "\360\363\356\377\357\361\355\377\357\361\355\377\355\360\355\377\356\360" - "\354\377\356\360\354\377\352\356\353\377\352\356\353\377\352\356\353\377" - "\353\357\354\377\355\360\355\377\356\360\354\377\356\361\356\377\355\360" - "\355\377\355\361\356\377\354\360\355\377\355\361\356\377\352\356\353\377" - "\352\356\353\377\353\356\352\377\355\361\356\377\356\360\354\377\355\360" - "\355\377\354\357\354\377\355\361\356\377\354\360\355\377\354\360\355\377" - "\354\360\355\377\361\366\363\377\364\370\365\377\364\370\365\377\347\353" - "\350\377\351\355\352\377\353\356\352\377\354\357\354\377\356\361\356\377" - "\354\360\355\377\355\361\356\377\355\361\356\377\356\363\357\377\357\363" - "\357\377\356\363\357\377\356\363\357\377\356\363\357\377\356\363\357\377" - "\355\360\355\377\356\361\356\377\356\361\356\377\355\360\355\377\355\357" - "\353\377\353\356\352\377\355\360\355\377\356\361\356\377\354\360\355\377" - "\355\360\355\377\356\361\356\377\356\361\356\377\356\361\356\377\357\363" - "\357\377\356\361\356\377\355\361\356\377\351\356\354\377\350\354\351\377" - "\347\355\353\377\350\356\354\377\351\361\357\377\354\360\355\377\353\360" - "\356\377\355\361\356\377\355\361\356\377\355\360\355\377\355\357\353\377" - "\355\357\353\377\353\356\352\377\352\355\351\377\352\355\351\377\352\356" - "\353\377\354\357\353\377\354\357\354\377\354\357\353\377\355\360\355\377" - "\353\357\354\377\353\360\356\377\352\357\355\377\351\361\357\377\355\361" - "\356\377\360\365\361\377\364\370\365\377\361\366\363\377\356\361\356\377" - "\354\357\354\377\353\356\352\377\352\356\353\377\353\356\352\377\352\355" - "\351\377\350\354\351\377\354\357\354\377\353\356\352\377\353\356\352\377" - "\355\360\355\377\355\360\355\377\354\357\354\377\355\360\355\377\355\361" - "\356\377\354\363\357\377\354\360\355\377\360\363\356\377\360\363\356\377" - "\357\361\355\377\356\361\356\377\355\360\355\377\355\360\355\377\355\360" - "\355\377\354\357\354\377\354\357\354\377\355\361\356\377\353\361\356\377" - "\355\364\360\377\355\361\356\377\356\361\356\377\354\357\354\377\356\361" - "\356\377\354\360\355\377\355\360\355\377\354\357\354\377\356\361\356\377" - "\353\357\354\377\356\363\357\377\357\361\355\377\357\361\355\377\353\356" - "\352\377\350\354\350\377\355\357\353\377\355\360\355\377\352\356\353\377" - "\351\355\352\377\353\357\354\377\353\357\354\377\354\357\354\377\354\357" - "\353\377\354\357\354\377\353\356\352\377\353\356\352\377\355\360\355\377" - "\354\360\355\377\351\356\354\377\351\356\354\377\351\356\354\377\353\363" - "\360\377\356\361\356\377\356\361\356\377\355\360\355\377\354\357\354\377" - "\354\360\355\377\354\360\355\377\354\360\355\377\361\366\363\377\365\371" - "\366\377\373\377\373\377\366\371\366\377\352\355\351\377\352\356\353\377" - "\354\357\354\377\354\357\354\377\355\361\356\377\353\357\354\377\354\363" - "\357\377\356\363\357\377\354\360\355\377\355\361\356\377\356\361\356\377" - "\355\361\356\377\355\361\356\377\354\360\355\377\356\363\357\377\356\361" - "\356\377\354\357\354\377\355\360\355\377\354\357\354\377\353\356\352\377" - "\353\356\352\377\354\357\354\377\354\357\354\377\354\357\354\377\354\357" - "\354\377\355\360\355\377\356\361\356\377\355\360\355\377\353\357\354\377" - "\353\357\354\377\351\355\352\377\352\356\353\377\352\356\353\377\351\361" - "\357\377\351\356\354\377\353\360\356\377\353\360\356\377\353\361\356\377" - "\354\360\355\377\353\356\352\377\356\360\354\377\355\360\355\377\352\356" - "\353\377\352\356\353\377\354\357\354\377\354\357\353\377\356\360\354\377" - "\356\361\356\377\354\357\354\377\355\360\355\377\353\357\354\377\352\357" - "\355\377\346\355\354\377\346\354\352\377\354\363\357\377\356\365\361\377" - "\363\367\364\377\356\361\356\377\355\360\355\377\354\357\354\377\353\360" - "\356\377\353\357\354\377\351\355\352\377\352\355\351\377\352\355\351\377" - "\352\355\351\377\352\355\351\377\355\360\355\377\356\363\357\377\353\357" - "\354\377\353\356\352\377\354\360\355\377\353\360\356\377\356\361\356\377" - "\356\360\354\377\360\363\356\377\357\361\355\377\355\360\355\377\353\356" - "\352\377\355\360\355\377\353\357\354\377\353\360\356\377\353\357\354\377" - "\355\361\356\377\356\363\357\377\355\364\360\377\352\357\355\377\355\360" - "\355\377\357\361\355\377\354\357\354\377\355\360\355\377\353\356\352\377" - "\355\360\355\377\357\363\357\377\357\363\357\377\357\363\357\377\356\360" - "\354\377\356\360\354\377\350\353\347\377\347\351\345\377\352\355\351\377" - "\356\360\354\377\353\356\352\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\355\360\355\377\353\356\352\377\353\356\352\377\352\356\353\377" - "\352\356\353\377\352\356\353\377\352\356\353\377\351\356\354\377\351\356" - "\354\377\352\357\355\377\351\356\354\377\355\360\355\377\355\360\355\377" - "\354\357\353\377\354\357\354\377\353\357\354\377\355\361\356\377\355\361" - "\356\377\360\365\361\377\360\367\365\377\363\370\366\377\356\363\357\377" - "\355\360\355\377\351\355\352\377\353\357\354\377\353\357\354\377\352\356" - "\353\377\353\357\354\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\354\360\355\377\356\361\356\377\356\363\357\377\356\363\357\377\354\360" - "\355\377\356\361\356\377\355\360\355\377\356\361\356\377\353\356\352\377" - "\353\357\354\377\353\356\352\377\353\357\354\377\352\356\353\377\353\357" - "\354\377\353\356\352\377\352\356\353\377\353\356\352\377\356\361\356\377" - "\355\361\356\377\355\361\356\377\356\363\357\377\353\357\354\377\352\356" - "\353\377\352\356\353\377\354\363\357\377\353\360\356\377\352\357\355\377" - "\353\360\356\377\352\357\355\377\353\357\354\377\353\357\354\377\355\360" - "\355\377\356\361\356\377\354\357\354\377\355\360\355\377\354\357\354\377" - "\354\357\354\377\353\356\352\377\354\357\354\377\354\360\355\377\356\361" - "\356\377\354\360\355\377\347\356\355\377\346\355\354\377\353\357\354\377" - "\354\360\355\377\356\361\356\377\357\363\357\377\356\361\356\377\354\360" - "\355\377\354\357\354\377\354\360\355\377\355\360\355\377\354\357\354\377" - "\354\357\353\377\352\355\351\377\352\355\351\377\350\354\350\377\354\357" - "\354\377\353\356\352\377\353\357\354\377\353\357\354\377\354\360\355\377" - "\353\361\356\377\355\360\355\377\357\361\355\377\360\363\356\377\356\360" - "\354\377\355\360\355\377\351\355\352\377\351\356\354\377\354\360\355\377" - "\352\357\355\377\353\357\354\377\355\361\357\377\354\363\357\377\354\363" - "\357\377\354\360\355\377\354\357\354\377\356\361\356\377\355\357\353\377" - "\353\356\352\377\353\356\352\377\356\361\356\377\357\361\355\377\357\363" - "\357\377\360\363\356\377\363\366\361\377\357\361\355\377\333\336\332\377" - "\344\346\342\377\351\354\350\377\355\361\356\377\356\361\356\377\355\360" - "\355\377\353\356\352\377\355\360\355\377\356\361\356\377\357\361\355\377" - "\356\360\354\377\354\357\354\377\353\357\354\377\353\357\354\377\354\360" - "\355\377\353\357\354\377\352\357\355\377\350\355\353\377\353\360\356\377" - "\351\356\354\377\354\360\355\377\354\357\354\377\356\360\354\377\355\360" - "\355\377\353\357\354\377\353\357\354\377\360\365\361\377\357\366\364\377" - "\363\370\366\377\356\363\357\377\354\357\354\377\351\355\352\377\354\360" - "\355\377\353\356\352\377\352\356\353\377\353\357\354\377\354\360\355\377" - "\353\357\354\377\354\360\355\377\356\361\356\377\355\361\356\377\356\363" - "\357\377\357\363\357\377\357\363\357\377\356\361\356\377\356\361\356\377" - "\357\361\355\377\356\361\356\377\354\357\354\377\354\357\354\377\352\356" - "\353\377\352\356\353\377\351\355\352\377\353\356\352\377\347\355\353\377" - "\353\356\352\377\356\361\356\377\353\357\354\377\355\364\360\377\355\361" - "\356\377\355\360\355\377\352\356\353\377\354\360\355\377\352\357\355\377" - "\351\356\354\377\351\360\356\377\353\360\356\377\353\360\356\377\352\357" - "\355\377\353\357\354\377\356\361\356\377\356\361\356\377\356\361\356\377" - "\355\360\355\377\353\356\352\377\356\360\354\377\355\360\355\377\355\360" - "\355\377\355\360\355\377\355\360\355\377\354\360\355\377\351\356\354\377" - "\352\356\353\377\352\356\353\377\354\360\355\377\354\360\355\377\356\363" - "\357\377\353\357\354\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\355\361\356\377\355\361\356\377\356\361\356\377\350\354\350\377\351\353" - "\346\377\347\351\345\377\350\354\350\377\353\356\352\377\353\356\352\377" - "\353\357\354\377\355\361\356\377\354\363\357\377\357\363\357\377\355\360" - "\355\377\357\363\357\377\357\363\357\377\354\357\354\377\352\356\353\377" - "\353\357\354\377\353\360\356\377\353\360\356\377\352\356\353\377\353\357" - "\354\377\352\357\355\377\355\361\356\377\360\365\361\377\355\360\355\377" - "\353\356\352\377\351\354\350\377\354\357\354\377\354\357\353\377\355\360" - "\355\377\357\361\355\377\356\361\356\377\356\361\356\377\356\361\356\377" - "\357\363\357\377\346\350\343\377\343\346\343\377\354\357\354\377\355\360" - "\355\377\356\360\354\377\355\357\353\377\356\360\354\377\355\360\355\377" - "\355\360\355\377\357\361\355\377\356\360\354\377\353\357\354\377\354\360" - "\355\377\353\357\354\377\353\357\354\377\353\357\354\377\351\355\352\377" - "\351\356\354\377\351\356\354\377\352\357\355\377\355\360\355\377\356\360" - "\354\377\356\360\354\377\355\357\353\377\352\356\353\377\356\363\357\377" - "\354\360\355\377\356\363\357\377\361\366\363\377\355\361\356\377\356\361" - "\356\377\353\356\352\377\355\360\355\377\352\356\353\377\353\357\354\377" - "\354\360\355\377\355\361\356\377\353\357\354\377\353\357\354\377\357\363" - "\357\377\355\360\355\377\357\363\357\377\356\361\356\377\357\363\357\377" - "\355\360\355\377\355\360\355\377\357\363\357\377\355\360\355\377\353\356" - "\352\377\352\355\351\377\354\360\355\377\353\357\354\377\352\356\353\377" - "\353\356\352\377\352\356\353\377\353\356\352\377\354\360\355\377\353\357" - "\354\377\355\361\356\377\356\363\357\377\357\361\355\377\356\360\354\377" - "\352\356\353\377\352\357\355\377\351\356\354\377\351\356\354\377\351\356" - "\354\377\352\357\355\377\353\360\356\377\355\361\356\377\356\361\356\377" - "\356\361\356\377\357\363\357\377\354\360\356\377\353\357\354\377\353\357" - "\354\377\354\357\354\377\353\356\352\377\354\357\354\377\354\360\355\377" - "\353\357\354\377\352\356\353\377\352\355\351\377\350\356\353\377\354\357" - "\354\377\355\361\356\377\353\357\354\377\353\357\354\377\353\357\354\377" - "\353\357\354\377\353\357\354\377\355\361\356\377\355\361\356\377\355\361" - "\356\377\355\361\356\377\353\356\352\377\350\354\350\377\344\350\344\377" - "\346\351\346\377\352\355\351\377\354\357\354\377\355\360\355\377\356\363" - "\357\377\357\363\357\377\357\363\357\377\357\363\357\377\357\363\357\377" - "\355\360\355\377\352\356\353\377\354\360\355\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\355\361\356\377\354\360\355\377\355\361\356\377" - "\356\363\357\377\357\363\357\377\347\353\347\377\344\350\344\377\350\354" - "\350\377\351\354\350\377\353\356\352\377\356\360\354\377\357\363\357\377" - "\360\365\361\377\364\370\365\377\363\367\364\377\365\371\366\377\355\360" - "\355\377\355\360\355\377\355\360\355\377\354\357\353\377\356\360\354\377" - "\353\356\352\377\354\357\354\377\353\356\352\377\353\356\352\377\352\355" - "\351\377\352\356\353\377\354\357\354\377\353\357\354\377\352\356\353\377" - "\347\355\353\377\353\357\354\377\353\360\356\377\347\355\353\377\353\356" - "\352\377\357\361\355\377\357\361\355\377\360\361\355\377\353\356\352\377" - "\353\356\352\377\355\360\355\377\356\361\356\377\355\361\356\377\360\365" - "\361\377\355\361\356\377\355\360\355\377\355\360\354\377\352\355\351\377" - "\347\353\347\377\353\356\352\377\355\361\356\377\354\360\355\377\355\361" - "\356\377\355\361\356\377\355\360\355\377\355\361\356\377\353\357\354\377" - "\352\357\355\377\354\360\355\377\356\363\357\377\356\361\356\377\356\360" - "\354\377\357\361\355\377\354\357\354\377\354\357\354\377\354\360\355\377" - "\356\361\356\377\355\361\356\377\356\361\356\377\355\361\356\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\351\355\352\377\355\360\355\377" - "\355\357\353\377\356\360\354\377\354\360\355\377\354\360\355\377\346\355" - "\354\377\350\355\353\377\351\356\354\377\351\356\354\377\352\357\355\377" - "\351\357\355\377\355\360\355\377\355\360\355\377\353\357\354\377\352\356" - "\353\377\351\356\354\377\353\356\352\377\353\356\352\377\353\356\352\377" - "\354\357\354\377\353\357\354\377\354\360\355\377\354\360\355\377\355\361" - "\356\377\354\360\355\377\353\357\354\377\352\357\355\377\353\357\354\377" - "\354\360\355\377\355\361\356\377\354\357\354\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\355\361\356\377\355\361\356\377\356\361\356\377" - "\354\357\354\377\347\353\347\377\347\353\347\377\353\356\352\377\354\357" - "\354\377\356\361\356\377\356\361\356\377\354\360\355\377\355\361\356\377" - "\360\365\361\377\361\366\361\377\354\360\355\377\355\361\356\377\354\360" - "\355\377\354\360\355\377\353\357\354\377\354\360\355\377\355\360\355\377" - "\357\363\357\377\360\365\361\377\355\361\356\377\355\361\356\377\337\343" - "\340\377\342\345\341\377\340\342\335\377\350\353\347\377\355\357\353\377" - "\356\361\356\377\356\361\356\377\356\363\357\377\364\370\365\377\371\374" - "\371\377\373\376\372\377\366\371\366\377\354\357\354\377\354\357\354\377" - "\354\357\354\377\355\360\355\377\352\355\351\377\353\356\352\377\354\357" - "\354\377\354\357\354\377\353\356\352\377\354\360\355\377\352\355\351\377" - "\353\360\356\377\352\357\355\377\353\360\356\377\353\357\354\377\353\360" - "\356\377\352\355\351\377\354\357\354\377\356\360\354\377\357\361\355\377" - "\356\361\356\377\355\361\356\377\355\360\355\377\354\360\355\377\356\361" - "\356\377\356\361\356\377\354\360\355\377\354\360\355\377\355\360\355\377" - "\356\360\354\377\356\360\354\377\357\363\357\377\353\357\354\377\354\360" - "\355\377\353\357\354\377\354\360\355\377\356\363\357\377\355\361\356\377" - "\353\360\356\377\353\360\356\377\352\357\355\377\352\357\355\377\356\361" - "\356\377\355\361\356\377\355\360\355\377\356\360\354\377\356\361\356\377" - "\355\360\355\377\354\360\355\377\355\360\355\377\355\360\355\377\355\360" - "\355\377\354\357\354\377\353\356\352\377\354\357\354\377\352\356\353\377" - "\350\356\354\377\352\356\353\377\354\360\355\377\354\357\354\377\355\360" - "\355\377\354\360\355\377\354\360\355\377\354\360\355\377\351\356\354\377" - "\347\355\353\377\351\356\354\377\351\357\355\377\355\360\355\377\354\357" - "\353\377\354\360\355\377\355\361\357\377\354\363\357\377\354\360\355\377" - "\354\360\355\377\352\356\353\377\352\356\353\377\353\357\354\377\353\357" - "\354\377\354\357\354\377\353\357\354\377\353\357\354\377\351\356\354\377" - "\351\356\354\377\354\360\355\377\355\360\355\377\355\360\355\377\353\356" - "\352\377\352\356\353\377\354\357\354\377\352\356\353\377\352\357\355\377" - "\356\363\360\377\365\370\364\377\366\371\366\377\366\371\366\377\360\365" - "\361\377\356\361\356\377\356\361\356\377\356\361\356\377\354\357\354\377" - "\355\360\355\377\355\360\355\377\354\357\354\377\353\357\354\377\351\355" - "\352\377\354\360\355\377\354\357\354\377\354\360\355\377\354\357\354\377" - "\355\360\355\377\354\357\354\377\356\361\356\377\364\370\365\377\372\376" - "\373\377\377\377\377\377\367\371\365\377\360\363\356\377\344\346\342\377" - "\354\356\351\377\356\360\354\377\356\361\356\377\357\363\357\377\355\361" - "\356\377\360\365\360\377\364\367\363\377\356\360\354\377\342\345\341\377" - "\335\337\333\377\347\353\347\377\351\355\352\377\354\360\355\377\352\356" - "\353\377\354\357\354\377\354\357\354\377\350\354\350\377\345\350\344\377" - "\347\351\345\377\352\355\351\377\352\355\351\377\351\355\352\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\357\361\355\377\354\357\354\377" - "\356\361\356\377\354\357\354\377\355\360\354\377\354\360\355\377\353\357" - "\354\377\352\357\355\377\354\357\354\377\356\361\356\377\353\357\354\377" - "\353\357\354\377\356\363\357\377\357\363\357\377\361\366\361\377\356\363" - "\357\377\354\360\355\377\354\360\355\377\353\357\354\377\355\361\356\377" - "\355\361\356\377\355\361\356\377\353\360\356\377\352\357\355\377\352\361" - "\357\377\351\360\356\377\352\357\355\377\354\360\355\377\355\360\355\377" - "\353\356\352\377\353\357\354\377\353\357\354\377\353\357\354\377\354\360" - "\355\377\355\360\355\377\354\360\355\377\354\357\354\377\352\355\351\377" - "\352\356\353\377\353\357\354\377\353\357\354\377\352\356\353\377\351\356" - "\354\377\351\355\352\377\353\360\356\377\353\357\354\377\352\356\353\377" - "\351\355\352\377\353\357\354\377\353\357\354\377\352\357\355\377\351\356" - "\354\377\355\360\355\377\353\356\352\377\352\356\353\377\352\357\355\377" - "\353\357\354\377\352\356\353\377\355\360\355\377\353\356\352\377\354\360" - "\355\377\353\357\354\377\353\357\354\377\353\356\352\377\354\360\355\377" - "\352\356\353\377\352\356\353\377\352\357\355\377\352\356\353\377\356\361" - "\356\377\357\363\357\377\355\361\356\377\355\361\356\377\353\357\354\377" - "\352\357\355\377\354\360\355\377\353\357\354\377\356\361\356\377\356\361" - "\356\377\355\361\356\377\353\357\354\377\354\360\355\377\351\357\355\377" - "\354\360\355\377\356\361\356\377\355\357\353\377\347\353\347\377\351\355" - "\352\377\347\353\350\377\350\354\351\377\347\354\352\377\354\357\354\377" - "\356\361\356\377\355\361\356\377\357\363\357\377\360\365\360\377\357\363" - "\357\377\364\370\365\377\372\374\370\377\375\377\374\377\366\370\363\377" - "\337\340\333\377\340\342\335\377\350\353\347\377\355\360\355\377\356\361" - "\356\377\354\360\355\377\361\366\363\377\360\365\360\377\364\367\363\377" - "\366\367\361\377\360\363\356\377\346\351\346\377\347\353\347\377\351\355" - "\352\377\352\356\353\377\351\355\352\377\355\360\355\377\352\355\351\377" - "\336\340\335\377\342\345\341\377\345\350\344\377\347\353\347\377\352\355" - "\351\377\353\356\352\377\352\356\353\377\352\356\353\377\353\356\352\377" - "\356\361\356\377\356\361\356\377\355\360\355\377\354\357\354\377\354\360" - "\355\377\354\360\355\377\353\357\354\377\353\357\354\377\357\363\357\377" - "\357\363\357\377\355\360\355\377\353\356\352\377\355\360\355\377\355\361" - "\356\377\356\363\357\377\356\363\357\377\356\363\357\377\355\361\356\377" - "\356\361\356\377\354\360\355\377\354\360\355\377\355\361\356\377\353\361" - "\356\377\354\363\357\377\352\357\355\377\352\357\355\377\352\356\354\377" - "\355\360\355\377\355\360\355\377\354\360\355\377\355\361\356\377\352\357" - "\355\377\351\356\354\377\351\356\354\377\352\356\353\377\353\357\354\377" - "\352\356\353\377\354\360\355\377\354\360\355\377\352\356\353\377\354\357" - "\353\377\353\357\354\377\353\357\354\377\352\356\353\377\353\357\354\377" - "\354\357\354\377\352\356\353\377\352\355\351\377\353\356\352\377\354\360" - "\355\377\352\356\353\377\353\357\354\377\354\357\354\377\356\361\356\377" - "\355\361\356\377\353\357\354\377\355\360\355\377\355\360\355\377\353\356" - "\352\377\354\357\354\377\354\357\354\377\354\357\354\377\352\356\353\377" - "\353\360\356\377\351\357\355\377\352\357\355\377\351\356\354\377\347\355" - "\353\377\353\357\354\377\357\363\357\377\356\361\356\377\353\361\356\377" - "\353\360\356\377\351\356\354\377\351\356\354\377\353\360\356\377\353\360" - "\356\377\351\356\354\377\353\357\354\377\352\357\355\377\354\360\355\377" - "\353\360\356\377\353\357\354\377\354\357\354\377\354\357\354\377\353\355" - "\350\377\351\353\346\377\351\355\352\377\345\351\346\377\343\346\343\377" - "\347\351\345\377\351\354\350\377\353\356\352\377\355\360\355\377\360\363" - "\356\377\357\363\357\377\361\366\361\377\363\367\364\377\373\377\373\377" - "\377\377\377\377\377\377\377\377\374\376\371\377\361\365\360\377\355\360" - "\355\377\354\357\354\377\354\357\354\377\351\355\352\377\354\360\355\377" - "\360\365\360\377\363\367\364\377\371\374\371\377\371\373\367\377\365\370" - "\364\377\356\361\356\377\350\354\350\377\347\353\350\377\342\346\343\377" - "\363\366\361\377\360\363\356\377\344\345\340\377\333\334\326\377\342\343" - "\336\377\345\350\344\377\350\354\351\377\351\355\352\377\353\357\354\377" - "\353\357\354\377\354\357\354\377\356\361\356\377\355\360\355\377\356\361" - "\356\377\354\360\355\377\355\361\356\377\355\360\355\377\354\360\355\377" - "\356\363\357\377\360\365\361\377\361\366\363\377\363\367\364\377\360\365" - "\360\377\356\361\356\377\354\360\355\377\354\360\355\377\356\363\357\377" - "\355\361\356\377\356\361\356\377\356\361\356\377\354\363\357\377\354\360" - "\355\377\355\361\356\377\353\361\356\377\353\363\360\377\353\363\360\377" - "\356\363\357\377\355\360\355\377\355\357\353\377\357\361\355\377\354\360" - "\355\377\354\363\357\377\347\356\355\377\350\355\353\377\351\356\354\377" - "\351\356\354\377\353\357\354\377\354\360\355\377\352\356\353\377\353\357" - "\354\377\355\360\355\377\355\360\354\377\354\360\355\377\354\360\355\377" - "\351\357\355\377\352\356\353\377\354\357\354\377\353\356\352\377\352\356" - "\353\377\353\356\352\377\353\357\354\377\353\356\352\377\354\357\354\377" - "\353\357\354\377\355\360\355\377\354\360\355\377\354\360\355\377\354\360" - "\355\377\353\357\354\377\353\357\354\377\354\357\354\377\355\360\355\377" - "\351\355\352\377\351\356\354\377\352\357\355\377\350\356\354\377\352\357" - "\355\377\347\355\353\377\350\356\354\377\353\357\354\377\353\357\354\377" - "\355\360\355\377\350\354\351\377\350\355\353\377\350\355\353\377\351\356" - "\354\377\351\356\354\377\352\357\355\377\350\355\353\377\347\355\353\377" - "\353\357\354\377\352\356\353\377\352\356\353\377\352\356\353\377\356\361" - "\356\377\356\361\356\377\355\360\354\377\356\361\356\377\361\366\363\377" - "\361\366\363\377\355\360\355\377\353\356\352\377\353\356\352\377\354\357" - "\354\377\355\360\355\377\355\360\355\377\355\360\355\377\351\355\352\377" - "\357\363\357\377\361\366\361\377\372\377\375\377\370\376\374\377\374\377" - "\374\377\364\367\363\377\360\365\360\377\355\360\355\377\353\356\352\377" - "\352\356\353\377\352\356\353\377\356\361\356\377\360\365\361\377\363\367" - "\364\377\366\371\366\377\357\363\357\377\361\365\360\377\361\365\360\377" - "\360\365\361\377\361\366\361\377\374\376\371\377\375\377\373\377\376\377" - "\373\377\374\377\372\377\364\366\360\377\356\361\356\377\346\354\352\377" - "\351\356\354\377\352\356\353\377\353\357\354\377\353\357\354\377\353\357" - "\354\377\352\357\355\377\354\360\355\377\355\360\355\377\355\360\355\377" - "\354\357\354\377\355\361\356\377\355\361\356\377\356\363\357\377\360\365" - "\361\377\360\365\361\377\360\365\360\377\356\361\356\377\355\360\355\377" - "\354\360\355\377\354\360\355\377\354\360\355\377\354\363\357\377\352\357" - "\355\377\352\357\355\377\352\356\353\377\353\357\354\377\353\357\354\377" - "\351\361\357\377\355\364\360\377\363\367\363\377\361\365\360\377\361\365" - "\360\377\357\363\357\377\356\363\357\377\354\363\357\377\353\357\354\377" - "\345\354\352\377\351\356\354\377\352\356\353\377\352\356\353\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\352\356\353\377\355\360\355\377" - "\354\360\355\377\355\360\355\377\354\360\355\377\354\360\355\377\354\357" - "\354\377\355\360\355\377\354\357\354\377\354\357\354\377\352\356\353\377" - "\354\360\355\377\354\360\355\377\355\360\355\377\353\360\356\377\354\360" - "\355\377\354\360\355\377\352\356\353\377\353\357\354\377\352\356\353\377" - "\355\357\353\377\352\357\355\377\353\357\354\377\352\357\355\377\350\355" - "\353\377\350\355\353\377\353\357\354\377\351\355\352\377\351\355\352\377" - "\351\357\355\377\355\360\355\377\355\361\356\377\354\360\355\377\351\356" - "\354\377\352\357\355\377\352\357\355\377\352\357\355\377\351\356\354\377" - "\351\356\354\377\354\357\354\377\352\356\353\377\352\356\353\377\350\356" - "\354\377\354\357\354\377\356\361\356\377\357\363\357\377\355\361\356\377" - "\356\361\356\377\355\361\356\377\357\363\357\377\360\365\360\377\355\360" - "\355\377\355\360\355\377\356\361\356\377\355\360\355\377\355\360\355\377" - "\354\360\355\377\353\357\354\377\350\354\351\377\347\355\353\377\351\356" - "\354\377\353\360\356\377\360\365\361\377\360\363\356\377\363\366\361\377" - "\363\367\363\377\360\365\360\377\353\357\354\377\352\356\353\377\355\360" - "\355\377\354\360\355\377\355\361\356\377\351\355\352\377\353\356\352\377" - "\356\360\354\377\357\363\357\377\360\365\361\377\365\370\364\377\364\367" - "\363\377\367\372\366\377\371\374\370\377\370\373\367\377\370\373\367\377" - "\353\357\354\377\350\355\353\377\350\355\353\377\350\355\353\377\345\354" - "\353\377\347\355\354\377\354\360\355\377\353\357\354\377\355\360\355\377" - "\354\360\355\377\356\361\356\377\355\360\355\377\354\360\355\377\357\363" - "\357\377\354\360\355\377\353\357\354\377\355\360\355\377\354\357\354\377" - "\353\357\354\377\354\357\354\377\353\357\354\377\353\357\354\377\355\361" - "\356\377\353\360\356\377\350\356\354\377\351\356\354\377\353\357\354\377" - "\354\363\357\377\355\364\360\377\355\364\360\377\355\360\355\377\357\363" - "\357\377\360\365\360\377\360\365\360\377\360\365\360\377\356\363\357\377" - "\354\363\357\377\355\364\360\377\361\366\363\377\354\360\355\377\353\361" - "\356\377\352\356\353\377\353\357\354\377\352\356\353\377\352\356\353\377" - "\353\356\352\377\354\357\354\377\356\361\356\377\355\360\355\377\356\361" - "\356\377\355\357\353\377\353\356\352\377\356\361\356\377\355\360\355\377" - "\355\360\355\377\354\360\355\377\351\356\354\377\347\355\353\377\352\356" - "\353\377\353\357\354\377\354\360\355\377\354\360\355\377\352\356\353\377" - "\352\355\351\377\352\356\353\377\351\355\352\377\353\357\354\377\352\356" - "\353\377\351\356\354\377\350\355\353\377\351\356\354\377\347\355\353\377" - "\351\356\354\377\347\355\353\377\350\355\353\377\347\356\355\377\351\361" - "\357\377\353\357\354\377\354\360\355\377\353\356\352\377\352\356\353\377" - "\352\356\353\377\354\360\355\377\354\360\355\377\353\356\352\377\353\357" - "\354\377\353\356\352\377\352\355\351\377\354\357\354\377\355\360\355\377" - "\356\363\357\377\355\361\356\377\356\361\356\377\355\360\355\377\355\361" - "\356\377\353\360\356\377\355\360\355\377\355\360\355\377\354\360\355\377" - "\355\360\355\377\353\357\354\377\354\357\354\377\352\357\355\377\352\356" - "\353\377\353\357\354\377\354\360\355\377\350\354\351\377\350\354\351\377" - "\355\360\354\377\354\357\353\377\354\360\355\377\355\361\356\377\354\357" - "\354\377\353\357\354\377\352\355\351\377\354\360\355\377\354\357\354\377" - "\352\357\355\377\355\360\355\377\352\356\353\377\346\351\346\377\355\360" - "\355\377\355\360\355\377\360\365\360\377\366\371\366\377\357\363\357\377" - "\364\370\365\377\361\366\361\377\354\360\355\377\352\357\355\377\352\356" - "\353\377\351\356\354\377\345\351\346\377\344\350\344\377\355\361\356\377" - "\354\360\355\377\354\357\354\377\356\360\354\377\356\361\356\377\353\356" - "\352\377\353\357\354\377\354\357\354\377\356\361\356\377\357\361\355\377" - "\363\366\361\377\357\363\357\377\355\361\356\377\355\360\355\377\354\360" - "\355\377\355\361\356\377\355\361\356\377\355\364\360\377\356\363\357\377" - "\356\363\357\377\356\363\357\377\355\360\355\377\355\360\355\377\356\363" - "\357\377\355\361\356\377\360\365\360\377\355\360\355\377\354\357\354\377" - "\357\363\357\377\355\360\355\377\355\361\356\377\355\364\360\377\355\361" - "\356\377\354\363\357\377\353\361\356\377\355\361\356\377\356\361\356\377" - "\357\363\357\377\355\361\356\377\353\357\354\377\354\357\354\377\353\356" - "\352\377\356\361\356\377\355\360\355\377\356\361\356\377\356\361\356\377" - "\354\357\354\377\354\357\354\377\355\360\355\377\353\357\354\377\352\357" - "\355\377\353\360\356\377\354\360\355\377\353\357\354\377\354\360\355\377" - "\356\363\357\377\354\360\355\377\352\354\347\377\347\351\345\377\353\355" - "\350\377\353\356\352\377\353\357\354\377\352\356\353\377\351\356\354\377" - "\347\355\353\377\352\357\355\377\351\356\354\377\351\356\354\377\351\356" - "\354\377\350\355\353\377\351\356\354\377\353\360\356\377\352\357\355\377" - "\351\355\352\377\352\356\353\377\352\356\353\377\353\357\354\377\353\356" - "\352\377\355\357\353\377\361\366\363\377\357\363\357\377\354\357\354\377" - "\354\357\354\377\354\357\354\377\354\357\354\377\353\356\352\377\354\357" - "\354\377\355\360\355\377\353\357\354\377\351\356\354\377\353\356\352\377" - "\353\356\352\377\353\356\352\377\355\360\355\377\354\363\357\377\354\360" - "\355\377\354\363\357\377\354\360\355\377\352\356\353\377\354\357\354\377" - "\350\355\353\377\353\357\354\377\353\356\352\377\354\357\354\377\352\356" - "\353\377\352\356\353\377\353\357\354\377\350\354\351\377\350\354\350\377" - "\351\355\352\377\351\355\352\377\353\360\356\377\351\355\352\377\352\356" - "\353\377\353\356\352\377\350\354\350\377\347\353\350\377\360\365\360\377" - "\355\360\355\377\355\360\355\377\355\360\355\377\356\361\356\377\356\361" - "\356\377\354\357\354\377\354\357\354\377\363\366\361\377\355\357\353\377" - "\344\346\342\377\354\360\355\377\355\361\356\377\354\360\355\377\357\361" - "\355\377\356\360\354\377\354\357\354\377\352\357\355\377\354\357\354\377" - "\360\363\356\377\357\363\357\377\357\363\357\377\355\361\356\377\353\357" - "\354\377\352\356\353\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\356\363\357\377\356\363\357\377\356\363\357\377\357\363\357\377\355\360" - "\355\377\354\360\355\377\352\357\355\377\354\360\355\377\360\365\361\377" - "\355\361\356\377\355\360\355\377\355\360\355\377\355\360\355\377\355\361" - "\356\377\352\357\355\377\352\357\355\377\353\360\356\377\351\357\355\377" - "\354\360\355\377\356\361\356\377\356\361\356\377\355\360\355\377\354\357" - "\354\377\350\356\354\377\354\363\357\377\353\357\354\377\354\360\355\377" - "\352\356\353\377\354\360\355\377\356\361\356\377\354\360\355\377\354\357" - "\354\377\353\357\354\377\355\361\356\377\354\363\357\377\355\361\356\377" - "\356\361\356\377\355\361\356\377\356\363\357\377\356\361\356\377\353\356" - "\352\377\350\353\347\377\354\356\351\377\352\356\353\377\353\357\354\377" - "\347\355\353\377\347\355\353\377\347\355\353\377\352\357\355\377\352\357" - "\355\377\351\355\352\377\353\357\354\377\352\356\353\377\353\360\356\377" - "\353\360\356\377\353\360\356\377\353\357\354\377\353\357\354\377\352\356" - "\353\377\352\356\353\377\354\357\354\377\356\360\354\377\361\365\360\377" - "\357\363\357\377\356\361\356\377\354\357\354\377\354\357\354\377\354\357" - "\354\377\353\356\352\377\353\356\352\377\353\356\352\377\353\356\352\377" - "\353\356\352\377\353\356\352\377\354\357\354\377\353\356\352\377\353\356" - "\352\377\355\360\355\377\357\361\355\377\354\360\355\377\356\361\356\377" - "\354\357\354\377\351\356\354\377\350\355\353\377\351\355\352\377\354\357" - "\354\377\353\356\352\377\352\356\353\377\352\356\353\377\351\355\352\377" - "\350\354\351\377\350\354\351\377\347\353\350\377\353\356\352\377\352\356" - "\353\377\353\357\354\377\350\354\351\377\347\353\347\377\352\355\351\377" - "\353\356\352\377\352\355\351\377\350\354\350\377\353\356\353\377\353\357" - "\354\377\355\361\356\377\356\361\356\377\356\361\356\377\356\361\356\377" - "\360\363\356\377\367\371\365\377\356\360\354\377\355\361\356\377\355\361" - "\356\377\357\361\355\377\357\361\355\377\353\356\352\377\353\356\352\377" - "\353\357\354\377\353\357\354\377\356\361\356\377\356\361\356\377\354\360" - "\355\377\354\360\355\377\353\357\354\377\353\357\354\377\353\357\354\377" - "\354\360\355\377\354\360\355\377\355\361\356\377\356\361\356\377\357\363" - "\357\377\357\363\357\377\353\356\352\377\352\356\353\377\354\363\357\377" - "\352\357\355\377\360\365\361\377\355\364\360\377\354\360\355\377\355\360" - "\355\377\355\360\355\377\353\356\352\377\350\354\351\377\351\355\352\377" - "\352\357\355\377\353\357\354\377\354\363\357\377\354\360\355\377\357\363" - "\357\377\357\363\357\377\354\360\355\377\352\357\355\377\351\356\354\377" - "\352\357\355\377\354\360\355\377\354\360\355\377\353\357\354\377\355\360" - "\355\377\354\360\355\377\356\361\356\377\354\363\357\377\355\361\356\377" - "\355\361\356\377\355\360\355\377\356\361\356\377\355\361\356\377\360\365" - "\361\377\364\367\363\377\365\370\364\377\361\363\356\377\356\361\356\377" - "\354\357\354\377\352\356\353\377\347\355\353\377\351\356\354\377\346\354" - "\352\377\351\356\354\377\351\356\354\377\352\356\353\377\352\356\353\377" - "\353\356\352\377\353\357\354\377\352\356\353\377\351\356\354\377\352\357" - "\355\377\352\356\353\377\352\356\353\377\352\356\353\377\352\356\353\377" - "\354\357\354\377\355\360\354\377\355\361\356\377\355\361\356\377\354\360" - "\355\377\356\361\356\377\355\360\355\377\353\357\354\377\353\357\354\377" - "\354\360\355\377\354\360\355\377\354\360\355\377\354\357\354\377\355\360" - "\355\377\353\356\352\377\352\356\353\377\356\360\354\377\355\360\355\377" - "\354\357\354\377\355\361\356\377\355\360\355\377\350\355\353\377\350\355" - "\353\377\354\357\354\377\353\356\352\377\353\356\352\377\351\355\352\377" - "\353\357\354\377\352\356\353\377\347\354\352\377\346\353\351\377\347\353" - "\350\377\352\356\353\377\354\360\355\377\352\356\353\377\345\351\346\377" - "\343\346\343\377\351\354\350\377\352\355\351\377\350\353\347\377\345\351" - "\346\377\336\342\337\377\345\351\346\377\353\357\354\377\355\361\356\377" - "\354\360\355\377\355\361\356\377\364\366\360\377\375\377\372\377\373\374" - "\370\377\353\357\354\377\355\361\356\377\360\365\360\377\360\363\356\377" - "\356\360\354\377\354\357\354\377\355\360\355\377\353\356\352\377\353\357" - "\354\377\354\360\355\377\352\357\355\377\353\360\356\377\355\361\356\377" - "\355\361\356\377\355\361\356\377\355\361\356\377\353\357\354\377\353\357" - "\354\377\355\360\355\377\356\361\356\377\355\360\355\377\355\361\356\377" - "\353\357\354\377\347\356\355\377\346\355\353\377\347\356\355\377\353\360" - "\356\377\353\361\356\377\357\363\357\377\354\357\354\377\350\354\350\377" - "\344\350\345\377\351\356\354\377\351\356\354\377\351\356\354\377\347\355" - "\353\377\352\356\353\377\353\357\354\377\354\357\354\377\353\357\354\377" - "\351\357\355\377\352\357\355\377\351\356\354\377\353\357\354\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\355\361\356\377\355\361\356\377\354\360\355\377\355\361\356\377\356\361" - "\356\377\356\361\356\377\360\365\361\377\360\365\361\377\360\365\360\377" - "\357\363\357\377\355\361\356\377\353\357\354\377\352\357\355\377\352\357" - "\355\377\352\357\355\377\350\360\356\377\351\360\356\377\347\355\354\377" - "\350\355\353\377\352\356\353\377\352\356\353\377\353\361\356\377\353\361" - "\356\377\355\361\356\377\355\361\356\377\353\357\354\377\353\357\354\377" - "\355\361\356\377\353\357\354\377\354\357\354\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\352\356\353\377\353\357\354\377" - "\352\356\353\377\352\356\353\377\354\357\354\377\352\356\353\377\353\357" - "\354\377\352\356\353\377\354\357\354\377\354\357\354\377\355\360\355\377" - "\353\356\352\377\354\357\353\377\352\355\351\377\354\357\354\377\355\360" - "\355\377\352\357\355\377\353\357\354\377\354\357\354\377\354\357\354\377" - "\354\357\354\377\351\355\352\377\351\355\352\377\352\356\353\377\350\355" - "\353\377\350\355\353\377\353\357\354\377\352\356\353\377\356\363\357\377" - "\361\366\363\377\356\360\354\377\351\354\350\377\352\355\351\377\356\360" - "\354\377\354\357\354\377\347\356\355\377\346\355\354\377\354\363\357\377" - "\354\360\355\377\355\361\356\377\355\361\356\377\355\361\356\377\356\361" - "\356\377\361\365\360\377\364\366\360\377\353\357\354\377\356\361\356\377" - "\360\363\356\377\365\370\364\377\365\370\364\377\364\367\363\377\361\366" - "\361\377\354\357\354\377\355\360\355\377\351\356\354\377\352\357\355\377" - "\353\357\354\377\354\360\355\377\353\357\354\377\354\360\355\377\355\361" - "\356\377\357\363\357\377\356\361\356\377\355\360\355\377\355\361\356\377" - "\354\357\354\377\354\360\355\377\351\355\352\377\350\356\355\377\345\354" - "\352\377\351\356\354\377\353\360\356\377\352\356\353\377\356\361\356\377" - "\355\360\355\377\353\356\352\377\347\355\353\377\350\356\354\377\351\357" - "\355\377\351\355\352\377\351\355\352\377\351\357\355\377\351\355\352\377" - "\353\357\354\377\352\356\353\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\353\356\352\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\354\360\355\377\355\361\356\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\356\361\356\377\355\361\356\377\355\364\360\377" - "\355\364\360\377\357\363\357\377\355\360\355\377\354\360\355\377\353\357" - "\354\377\352\356\353\377\354\360\355\377\354\360\355\377\351\356\354\377" - "\346\355\354\377\345\354\352\377\346\354\352\377\352\356\353\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\355\361\356\377\355\361\356\377" - "\355\361\356\377\355\361\356\377\355\361\356\377\355\361\356\377\354\363" - "\357\377\353\357\354\377\347\355\353\377\350\355\353\377\352\356\353\377" - "\353\361\356\377\354\360\355\377\352\356\353\377\351\356\354\377\354\357" - "\354\377\353\357\354\377\352\356\353\377\353\357\354\377\352\356\353\377" - "\355\360\355\377\353\356\352\377\353\356\352\377\347\353\347\377\352\355" - "\351\377\353\356\352\377\353\356\352\377\351\355\352\377\352\356\353\377" - "\352\355\351\377\354\357\353\377\354\357\354\377\352\355\351\377\351\355" - "\352\377\350\354\351\377\351\356\354\377\347\355\353\377\352\356\353\377" - "\353\357\354\377\355\361\356\377\360\365\361\377\366\371\366\377\361\365" - "\360\377\355\357\353\377\352\356\353\377\355\361\356\377\352\360\356\377" - "\354\357\354\377\355\361\356\377\355\360\355\377\353\357\354\377\354\360" - "\355\377\361\366\363\377\360\365\360\377\357\363\357\377\360\365\360\377" - "\355\361\356\377\357\361\355\377\360\363\356\377\361\365\360\377\360\365" - "\360\377\361\366\363\377\355\361\356\377\354\357\354\377\352\355\351\377" - "\350\354\350\377\351\356\354\377\351\356\354\377\354\363\357\377\354\360" - "\355\377\355\361\356\377\356\361\356\377\353\357\354\377\356\363\357\377" - "\355\361\356\377\353\357\354\377\353\361\356\377\353\357\354\377\344\351" - "\347\377\347\355\354\377\347\354\352\377\352\357\355\377\353\357\354\377" - "\355\360\355\377\354\360\355\377\355\360\355\377\353\356\352\377\352\356" - "\353\377\351\357\355\377\347\355\353\377\352\356\353\377\353\357\354\377" - "\350\356\354\377\351\355\352\377\352\355\351\377\352\355\351\377\351\355" - "\352\377\353\356\352\377\355\360\355\377\356\360\354\377\353\356\352\377" - "\353\357\354\377\353\357\354\377\352\356\353\377\354\360\355\377\355\360" - "\355\377\354\360\355\377\356\360\354\377\352\356\353\377\347\356\355\377" - "\346\354\352\377\353\360\356\377\354\363\357\377\356\361\356\377\355\360" - "\355\377\347\355\353\377\352\361\357\377\354\360\355\377\354\360\355\377" - "\353\357\354\377\351\356\354\377\351\356\354\377\352\357\355\377\352\357" - "\355\377\353\357\354\377\352\356\353\377\353\357\354\377\353\357\354\377" - "\355\361\356\377\354\357\354\377\354\360\355\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\354\360\355\377\353\357\354\377\351\356\354\377" - "\351\356\354\377\352\356\353\377\353\357\354\377\353\360\356\377\351\356" - "\354\377\352\356\353\377\356\361\356\377\356\360\354\377\356\361\356\377" - "\356\361\356\377\356\361\356\377\361\365\360\377\360\363\356\377\353\356" - "\352\377\353\356\352\377\355\360\355\377\354\360\355\377\353\361\356\377" - "\352\357\355\377\352\356\353\377\353\357\354\377\355\360\355\377\354\357" - "\354\377\353\356\352\377\350\354\350\377\350\354\351\377\351\355\352\377" - "\352\356\353\377\351\355\352\377\355\361\356\377\354\360\355\377\355\364" - "\360\377\355\361\356\377\361\366\361\377\357\363\357\377\354\357\354\377" - "\355\361\356\377\355\361\356\377\354\357\354\377\356\361\356\377\356\361" - "\356\377\353\357\354\377\354\360\355\377\356\361\356\377\354\357\354\377" - "\355\360\355\377\355\360\355\377\355\361\356\377\357\363\357\377\357\363" - "\357\377\357\363\357\377\356\361\356\377\355\364\360\377\357\363\357\377" - "\353\356\352\377\343\346\343\377\347\353\347\377\347\353\350\377\351\355" - "\352\377\352\357\355\377\353\357\354\377\355\360\355\377\356\363\357\377" - "\355\361\356\377\355\361\356\377\355\361\356\377\355\361\356\377\354\363" - "\357\377\347\355\353\377\345\353\350\377\345\353\350\377\347\355\353\377" - "\353\356\352\377\353\356\352\377\353\357\354\377\355\361\356\377\356\361" - "\356\377\354\357\354\377\351\355\352\377\347\355\353\377\351\356\354\377" - "\352\357\355\377\352\356\353\377\352\356\353\377\350\354\351\377\350\354" - "\351\377\352\355\351\377\352\355\351\377\353\356\352\377\354\357\353\377" - "\354\356\351\377\353\356\352\377\353\356\352\377\353\356\352\377\352\356" - "\353\377\356\361\356\377\356\361\356\377\356\361\356\377\355\360\355\377" - "\352\356\353\377\347\356\355\377\351\356\354\377\351\356\354\377\353\360" - "\356\377\355\360\355\377\352\356\353\377\352\356\353\377\351\355\352\377" - "\353\356\352\377\354\357\354\377\355\360\355\377\353\357\354\377\352\356" - "\353\377\347\355\353\377\351\355\352\377\351\356\354\377\352\356\353\377" - "\353\357\354\377\353\356\352\377\354\360\355\377\354\357\354\377\354\360" - "\355\377\353\357\354\377\354\360\355\377\356\361\356\377\354\357\354\377" - "\353\357\354\377\351\355\352\377\352\356\353\377\354\360\355\377\353\357" - "\354\377\351\356\354\377\352\357\355\377\352\357\355\377\357\361\355\377" - "\357\361\355\377\356\361\356\377\354\357\354\377\356\361\356\377\363\366" - "\361\377\364\367\363\377\360\363\356\377\353\356\352\377\354\360\355\377" - "\355\360\355\377\354\360\355\377\353\357\354\377\353\357\354\377\352\356" - "\353\377\351\356\354\377\353\357\354\377\355\360\355\377\354\357\354\377" - "\352\355\351\377\350\354\351\377\350\355\353\377\350\354\351\377\352\356" - "\353\377\353\357\354\377\352\356\353\377\354\360\355\377\355\361\356\377" - "\355\360\355\377\354\357\354\377\354\357\354\377\355\361\356\377\357\363" - "\357\377\356\361\356\377\357\363\357\377\356\361\356\377\354\357\354\377" - "\354\357\354\377\355\360\355\377\354\357\354\377\355\361\356\377\354\360" - "\355\377\354\360\355\377\356\361\356\377\357\363\357\377\357\366\364\377" - "\355\364\360\377\355\361\356\377\357\363\357\377\344\350\344\377\343\346" - "\343\377\352\355\351\377\350\354\351\377\351\355\352\377\355\361\356\377" - "\354\357\354\377\355\361\356\377\353\357\354\377\354\360\355\377\356\363" - "\357\377\353\360\356\377\355\364\360\377\356\365\361\377\350\355\353\377" - "\350\355\353\377\351\356\354\377\351\355\352\377\353\357\354\377\352\356" - "\353\377\354\357\354\377\356\361\356\377\355\360\355\377\355\360\355\377" - "\352\357\355\377\347\355\353\377\351\356\354\377\351\355\352\377\352\356" - "\353\377\353\357\354\377\351\355\352\377\353\356\352\377\354\357\353\377" - "\356\360\354\377\355\357\353\377\353\356\352\377\353\356\352\377\353\356" - "\352\377\353\356\352\377\354\357\354\377\355\360\355\377\356\361\356\377" - "\356\361\356\377\356\361\356\377\353\357\354\377\353\357\354\377\350\356" - "\354\377\350\360\356\377\350\360\356\377\354\357\354\377\352\356\353\377" - "\352\356\353\377\351\356\354\377\353\357\354\377\355\360\355\377\355\360" - "\355\377\354\357\354\377\354\357\354\377\353\357\354\377\351\356\354\377" - "\351\356\354\377\347\355\353\377\352\356\353\377\354\357\354\377\353\357" - "\354\377\353\357\354\377\351\357\355\377\353\357\354\377\351\357\355\377" - "\353\356\352\377\354\360\355\377\353\357\354\377\353\360\356\377\353\360" - "\356\377\354\360\355\377\352\356\353\377\354\360\355\377\352\357\355\377" - "\353\357\354\377\355\360\355\377\355\357\353\377\354\357\354\377\353\356" - "\352\377\355\360\355\377\364\367\363\377\361\365\360\377\370\372\366\377" - "\357\361\355\377\354\360\355\377\355\361\356\377\354\357\354\377\354\357" - "\354\377\352\356\353\377\351\356\354\377\352\357\355\377\353\360\356\377" - "\355\360\355\377\353\357\354\377\353\356\352\377\351\355\352\377\350\355" - "\353\377\347\354\352\377\352\356\353\377\354\360\355\377\352\356\353\377" - "\354\357\354\377\360\363\356\377\356\361\356\377\353\357\354\377\354\357" - "\354\377\353\360\356\377\354\360\355\377\357\361\355\377\357\363\357\377" - "\355\360\355\377\352\356\353\377\354\360\355\377\353\357\354\377\355\360" - "\355\377\355\360\355\377\353\357\354\377\355\361\356\377\355\360\355\377" - "\360\365\360\377\356\363\357\377\360\365\361\377\365\371\366\377\364\370" - "\365\377\355\361\357\377\354\360\355\377\354\357\354\377\353\356\352\377" - "\354\357\354\377\355\360\355\377\354\360\355\377\353\360\356\377\355\361" - "\356\377\355\361\356\377\353\357\354\377\350\360\356\377\354\360\355\377" - "\357\366\364\377\353\360\356\377\350\355\353\377\351\355\352\377\354\360" - "\355\377\353\357\354\377\354\357\354\377\356\361\356\377\356\361\356\377" - "\356\361\356\377\356\361\356\377\352\356\353\377\352\357\355\377\351\355" - "\352\377\351\355\352\377\350\356\354\377\354\357\354\377\352\356\353\377" - "\355\360\355\377\356\360\354\377\355\360\355\377\356\360\354\377\356\360" - "\354\377\353\356\352\377\354\357\354\377\355\360\355\377\356\361\356\377" - "\355\360\355\377\356\361\356\377\356\361\356\377\355\361\356\377\355\361" - "\356\377\353\357\354\377\355\360\355\377\354\360\355\377\352\356\353\377" - "\353\356\352\377\353\357\354\377\353\357\354\377\352\356\353\377\353\356" - "\352\377\355\360\355\377\353\356\352\377\354\357\354\377\353\356\352\377" - "\353\357\354\377\352\357\355\377\351\356\354\377\351\356\354\377\353\357" - "\354\377\352\356\353\377\352\356\353\377\352\356\353\377\353\357\354\377" - "\353\357\354\377\354\363\357\377\353\361\356\377\352\356\353\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\353\357\354\377\355\361\356\377" - "\353\357\354\377\355\361\356\377\356\361\356\377\356\361\356\377\355\357" - "\353\377\354\357\354\377\354\357\354\377\353\356\352\377\356\361\356\377" - "\361\366\361\377\363\366\361\377\356\363\360\377\355\361\356\377\355\360" - "\355\377\355\357\353\377\353\356\352\377\353\357\354\377\354\360\355\377" - "\353\357\354\377\355\361\356\377\354\360\355\377\355\360\355\377\354\357" - "\354\377\354\357\354\377\350\355\353\377\353\360\356\377\354\360\355\377" - "\355\361\356\377\355\361\356\377\355\361\356\377\356\361\356\377\354\357" - "\354\377\354\363\357\377\354\360\355\377\352\357\355\377\354\360\355\377" - "\357\361\355\377\360\363\356\377\357\363\357\377\354\360\355\377\356\361" - "\356\377\354\360\355\377\353\356\352\377\354\357\354\377\356\363\357\377" - "\357\363\357\377\360\363\356\377\357\363\357\377\360\365\361\377\360\365" - "\360\377\361\366\361\377\363\367\363\377\361\366\363\377\357\363\357\377" - "\361\366\361\377\355\360\355\377\355\360\355\377\355\360\355\377\354\360" - "\355\377\353\357\354\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\355\364\360\377\355\361\356\377\353\360\356\377\351\360\356\377\350\356" - "\355\377\353\357\354\377\355\360\355\377\352\356\353\377\352\356\353\377" - "\356\361\356\377\355\360\355\377\355\360\355\377\353\356\352\377\352\356" - "\353\377\347\355\353\377\354\360\355\377\352\356\353\377\352\356\353\377" - "\354\357\354\377\353\356\352\377\351\354\350\377\354\357\354\377\355\357" - "\353\377\356\361\356\377\356\361\356\377\356\361\356\377\354\357\354\377" - "\355\360\355\377\354\357\354\377\356\361\356\377\355\360\355\377\356\361" - "\356\377\355\361\356\377\353\360\356\377\353\357\354\377\354\360\355\377" - "\352\356\353\377\353\360\356\377\354\360\355\377\353\357\354\377\355\360" - "\355\377\355\361\356\377\354\357\354\377\353\357\354\377\355\360\355\377" - "\353\357\354\377\352\356\353\377\354\360\355\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\354\360\355\377\353\357\354\377\353\357\354\377" - "\354\360\355\377\355\361\356\377\354\360\355\377\354\360\355\377\353\357" - "\354\377\353\357\354\377\352\356\353\377\352\356\353\377\354\360\355\377" - "\353\357\354\377\355\361\356\377\355\361\356\377\357\363\357\377\360\365" - "\360\377\361\366\361\377\357\363\357\377\354\357\354\377\350\354\350\377" - "\352\356\353\377\353\357\354\377\355\361\356\377\354\360\355\377\355\360" - "\355\377\353\357\354\377\357\363\357\377\357\361\355\377\357\361\355\377" - "\355\361\356\377\354\360\355\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\353\357\354\377\354\357\354\377\351\355\352\377" - "\352\356\353\377\354\360\355\377\351\357\355\377\354\360\355\377\355\361" - "\356\377\354\357\354\377\350\356\354\377\353\357\354\377\354\360\355\377" - "\355\357\353\377\356\363\357\377\356\360\354\377\360\363\356\377\357\363" - "\357\377\356\363\357\377\357\363\357\377\361\366\361\377\357\361\355\377" - "\342\345\341\377\357\363\357\377\357\361\355\377\357\361\355\377\360\365" - "\360\377\356\363\357\377\360\365\360\377\360\365\360\377\356\363\357\377" - "\357\365\363\377\356\363\357\377\357\363\357\377\356\361\356\377\356\361" - "\356\377\355\360\355\377\355\360\355\377\354\360\355\377\355\360\355\377" - "\353\357\354\377\353\357\354\377\353\361\356\377\353\357\354\377\353\357" - "\354\377\350\356\355\377\347\355\354\377\352\356\353\377\354\357\354\377" - "\354\357\354\377\353\356\352\377\355\360\355\377\355\360\355\377\355\360" - "\355\377\353\356\352\377\352\355\351\377\350\354\351\377\352\356\353\377" - "\354\360\355\377\352\356\353\377\354\357\354\377\353\356\352\377\353\356" - "\352\377\353\357\354\377\352\356\353\377\353\357\354\377\355\361\356\377" - "\356\361\356\377\354\360\355\377\355\361\356\377\355\361\356\377\353\357" - "\354\377\354\357\354\377\353\357\354\377\354\360\355\377\353\357\354\377" - "\354\360\355\377\352\357\355\377\352\356\353\377\353\357\354\377\352\356" - "\353\377\351\356\354\377\352\356\353\377\354\360\355\377\354\357\353\377" - "\355\360\355\377\354\357\354\377\354\360\355\377\352\356\353\377\352\356" - "\353\377\355\360\355\377\355\360\355\377\355\360\355\377\354\360\355\377" - "\352\356\353\377\354\360\355\377\354\360\355\377\354\357\354\377\355\361" - "\356\377\354\360\355\377\355\361\356\377\353\360\356\377\353\357\354\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\354\360\355\377\355\360" - "\355\377\357\363\357\377\363\366\361\377\363\367\363\377\365\370\364\377" - "\370\373\370\377\357\363\357\377\354\360\355\377\353\357\354\377\354\363" - "\357\377\354\363\357\377\354\360\355\377\354\360\355\377\357\363\357\377" - "\357\363\357\377\357\361\355\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\353\357\354\377\355\361\356\377\354\357\354\377\355\360\355\377" - "\353\357\354\377\351\356\354\377\352\357\355\377\354\363\357\377\355\360" - "\355\377\354\360\355\377\355\361\356\377\353\357\354\377\351\357\355\377" - "\352\356\353\377\355\361\356\377\356\361\356\377\355\360\355\377\354\360" - "\355\377\360\365\360\377\360\365\360\377\357\363\357\377\361\366\361\377" - "\370\372\366\377\355\357\353\377\352\354\347\377\360\365\361\377\355\360" - "\355\377\356\361\356\377\357\363\357\377\355\361\356\377\354\357\354\377" - "\351\355\352\377\350\354\350\377\353\357\354\377\355\361\356\377\355\360" - "\355\377\356\360\354\377\355\357\353\377\356\361\356\377\360\363\356\377" - "\356\361\356\377\355\361\356\377\356\361\356\377\356\363\357\377\355\361" - "\356\377\353\357\354\377\352\356\353\377\346\354\352\377\352\356\353\377" - "\354\360\355\377\354\360\355\377\351\355\352\377\354\360\355\377\354\360" - "\355\377\357\363\357\377\357\363\357\377\357\363\357\377\355\360\355\377" - "\354\357\354\377\353\357\354\377\352\356\353\377\354\357\354\377\353\356" - "\352\377\355\360\355\377\354\357\354\377\355\360\355\377\354\357\354\377" - "\353\357\354\377\353\357\354\377\356\361\356\377\356\361\356\377\355\361" - "\356\377\353\357\354\377\356\361\356\377\353\356\352\377\354\360\355\377" - "\355\361\356\377\355\360\355\377\352\356\353\377\351\355\352\377\352\356" - "\353\377\352\356\353\377\353\357\354\377\352\356\353\377\352\356\353\377" - "\354\360\355\377\356\360\354\377\355\360\355\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\354\360\355\377\354\357\354\377\355\360\355\377" - "\356\361\356\377\355\361\356\377\351\356\354\377\355\361\356\377\355\361" - "\356\377\354\360\355\377\352\356\353\377\353\357\354\377\354\360\355\377" - "\354\360\355\377\353\357\354\377\353\357\354\377\351\355\352\377\356\361" - "\356\377\357\363\357\377\357\363\357\377\356\361\356\377\355\360\355\377" - "\356\363\357\377\361\366\363\377\364\370\365\377\361\366\363\377\356\363" - "\357\377\354\363\357\377\352\357\355\377\353\360\356\377\353\360\356\377" - "\355\361\356\377\354\360\355\377\355\361\356\377\356\361\356\377\356\361" - "\356\377\356\361\356\377\354\360\355\377\354\360\355\377\356\361\356\377" - "\355\360\355\377\354\357\354\377\353\357\354\377\353\357\354\377\352\356" - "\353\377\353\357\354\377\353\357\354\377\354\360\355\377\352\357\355\377" - "\354\360\355\377\352\357\355\377\353\357\354\377\353\357\354\377\353\356" - "\352\377\353\356\352\377\353\357\354\377\355\361\356\377\357\363\357\377" - "\361\366\361\377\364\367\363\377\370\373\370\377\377\377\374\377\373\374" - "\370\377\353\363\360\377\354\363\357\377\360\365\361\377\356\363\357\377" - "\356\363\357\377\356\361\356\377\353\356\352\377\353\356\352\377\353\357" - "\354\377\354\360\355\377\356\363\357\377\356\363\360\377\354\360\355\377" - "\354\360\355\377\356\361\356\377\357\363\357\377\356\361\356\377\357\363" - "\357\377\355\361\356\377\360\365\361\377\353\357\354\377\354\357\354\377" - "\343\346\343\377\351\355\352\377\352\355\351\377\352\360\355\377\352\356" - "\353\377\352\356\353\377\354\363\357\377\354\360\355\377\356\363\357\377" - "\357\363\357\377\356\361\356\377\356\361\356\377\352\356\353\377\353\357" - "\354\377\351\355\352\377\353\356\352\377\354\357\354\377\355\360\355\377" - "\354\360\355\377\355\361\356\377\354\357\354\377\353\357\354\377\356\361" - "\356\377\355\361\356\377\354\360\355\377\355\361\356\377\355\361\356\377" - "\355\361\356\377\355\361\356\377\354\360\355\377\355\361\356\377\355\361" - "\356\377\354\360\355\377\354\360\355\377\352\356\353\377\354\360\355\377" - "\354\360\355\377\354\357\354\377\354\357\353\377\354\357\353\377\353\356" - "\352\377\353\356\352\377\355\360\355\377\353\357\354\377\354\360\355\377" - "\353\357\354\377\355\360\355\377\355\360\355\377\354\360\355\377\350\360" - "\356\377\354\360\355\377\354\360\355\377\352\357\355\377\355\361\356\377" - "\354\357\354\377\355\357\353\377\353\357\354\377\354\360\355\377\354\357" - "\354\377\356\361\356\377\357\361\355\377\357\361\355\377\357\363\357\377" - "\356\361\356\377\357\363\357\377\356\363\357\377\355\361\356\377\355\361" - "\356\377\356\363\357\377\354\363\357\377\351\356\354\377\347\356\355\377" - "\351\356\354\377\352\357\355\377\354\360\355\377\353\357\354\377\355\361" - "\356\377\355\360\355\377\355\357\353\377\356\360\354\377\354\360\355\377" - "\354\360\355\377\354\357\354\377\355\360\355\377\355\360\355\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\355\361\356\377\354\360\355\377" - "\353\357\354\377\352\356\353\377\352\356\353\377\354\360\355\377\352\356" - "\353\377\353\356\352\377\351\355\352\377\352\355\351\377\352\355\351\377" - "\356\361\356\377\355\360\355\377\360\363\356\377\365\370\364\377\364\367" - "\363\377\364\366\360\377\361\365\357\377\351\361\357\377\353\363\360\377" - "\360\365\361\377\356\363\357\377\361\366\361\377\357\363\357\377\356\361" - "\356\377\355\360\355\377\353\357\354\377\357\363\357\377\353\357\354\377" - "\352\356\353\377\351\357\355\377\356\363\357\377\355\360\355\377\356\361" - "\356\377\355\360\355\377\356\361\356\377\356\363\357\377\360\365\361\377" - "\364\370\365\377\356\363\357\377\353\356\352\377\352\355\351\377\352\356" - "\352\377\355\361\356\377\350\356\353\377\352\356\353\377\352\357\355\377" - "\353\357\354\377\354\360\355\377\356\361\356\377\360\365\360\377\361\366" - "\361\377\357\363\357\377\354\360\355\377\346\351\346\377\344\346\342\377" - "\354\357\354\377\355\360\355\377\353\357\354\377\354\360\355\377\354\357" - "\354\377\354\357\354\377\355\360\355\377\354\360\355\377\355\361\356\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\352\356\353\377\352\357" - "\355\377\353\360\356\377\352\356\353\377\354\360\355\377\352\356\353\377" - "\354\360\355\377\352\356\353\377\353\356\352\377\355\360\355\377\355\360" - "\355\377\355\360\355\377\353\356\352\377\352\355\351\377\352\356\353\377" - "\352\356\353\377\354\357\354\377\354\360\355\377\355\360\355\377\354\360" - "\355\377\354\357\354\377\351\357\355\377\355\360\355\377\353\357\354\377" - "\353\357\354\377\353\356\352\377\353\356\352\377\357\361\355\377\353\357" - "\354\377\354\357\354\377\355\360\355\377\354\357\354\377\356\360\354\377" - "\356\360\354\377\356\361\356\377\354\360\355\377\356\361\356\377\355\361" - "\356\377\355\361\356\377\355\364\360\377\353\360\356\377\355\364\360\377" - "\354\364\361\377\350\360\356\377\352\357\355\377\354\360\355\377\355\361" - "\356\377\352\357\355\377\355\361\356\377\355\361\356\377\356\360\354\377" - "\356\360\354\377\353\357\354\377\353\357\354\377\353\357\354\377\355\360" - "\355\377\353\356\352\377\354\360\355\377\352\356\353\377\353\357\354\377" - "\355\361\356\377\355\361\356\377\354\360\355\377\354\360\355\377\352\356" - "\353\377\351\357\355\377\353\360\356\377\356\363\360\377\357\363\357\377" - "\356\361\356\377\354\360\355\377\355\360\355\377\356\363\357\377\360\363" - "\356\377\363\366\361\377\367\373\370\377\374\377\375\377\370\374\371\377" - "\350\360\356\377\354\360\355\377\355\361\356\377\355\361\356\377\357\363" - "\357\377\357\363\357\377\357\363\357\377\357\363\357\377\354\363\357\377" - "\353\360\356\377\356\363\357\377\355\361\356\377\354\363\357\377\355\361" - "\356\377\356\361\356\377\355\360\355\377\355\360\355\377\356\361\356\377" - "\356\363\357\377\356\365\361\377\355\364\360\377\360\365\361\377\360\365" - "\361\377\360\365\360\377\357\361\355\377\356\361\356\377\353\357\354\377" - "\354\360\355\377\355\361\356\377\353\357\354\377\356\363\357\377\355\360" - "\355\377\356\361\356\377\360\365\361\377\364\370\365\377\365\371\366\377" - "\361\370\366\377\355\361\356\377\357\361\355\377\357\363\357\377\355\360" - "\355\377\353\357\354\377\355\361\356\377\354\357\354\377\354\357\354\377" - "\352\356\353\377\353\360\356\377\351\356\354\377\354\363\357\377\354\360" - "\355\377\353\357\354\377\354\363\357\377\352\357\355\377\353\357\354\377" - "\354\363\360\377\354\357\354\377\355\360\355\377\353\356\352\377\352\356" - "\353\377\353\356\352\377\357\361\355\377\360\365\360\377\361\365\360\377" - "\353\356\352\377\355\357\353\377\354\357\354\377\354\360\355\377\354\360" - "\355\377\353\357\354\377\355\360\355\377\355\360\355\377\355\364\360\377" - "\356\361\356\377\356\361\356\377\354\357\354\377\353\356\352\377\354\357" - "\354\377\355\360\355\377\352\355\351\377\353\356\352\377\351\354\350\377" - "\350\354\350\377\353\356\352\377\356\361\356\377\354\357\354\377\354\360" - "\355\377\355\360\355\377\355\361\356\377\354\357\354\377\353\357\354\377" - "\355\360\355\377\354\357\354\377\353\357\354\377\355\360\355\377\354\360" - "\355\377\355\361\356\377\352\356\353\377\354\360\355\377\354\360\355\377" - "\355\360\355\377\355\357\353\377\357\361\355\377\356\361\356\377\354\360" - "\355\377\353\357\354\377\355\361\356\377\355\360\355\377\354\360\355\377" - "\352\356\353\377\353\360\356\377\355\361\356\377\356\361\356\377\356\363" - "\357\377\357\363\357\377\355\361\356\377\347\355\353\377\352\356\353\377" - "\353\357\354\377\354\360\355\377\355\360\355\377\353\357\354\377\353\360" - "\356\377\355\361\356\377\357\361\355\377\353\356\352\377\355\360\355\377" - "\360\365\360\377\353\356\352\377\355\361\356\377\356\363\357\377\355\360" - "\355\377\355\360\355\377\354\360\355\377\355\360\355\377\356\363\357\377" - "\355\360\355\377\357\363\357\377\356\363\357\377\355\361\356\377\356\363" - "\357\377\356\363\357\377\360\365\361\377\356\363\357\377\360\365\360\377" - "\357\363\357\377\355\360\355\377\355\361\356\377\352\361\357\377\351\360" - "\356\377\355\364\360\377\356\365\361\377\364\367\363\377\364\367\363\377" - "\356\361\356\377\354\360\355\377\353\357\354\377\354\360\355\377\354\357" - "\354\377\355\361\356\377\354\360\355\377\355\361\356\377\360\365\361\377" - "\360\365\361\377\361\366\361\377\356\365\361\377\356\363\357\377\360\363" - "\356\377\357\361\355\377\354\357\354\377\353\357\354\377\353\357\354\377" - "\355\360\355\377\354\357\354\377\354\360\355\377\352\357\355\377\351\356" - "\354\377\352\357\355\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\354\360\355\377\353\357\354\377\355\361\356\377\354\357\354\377\356\361" - "\356\377\355\357\353\377\352\355\351\377\353\356\352\377\354\357\354\377" - "\357\363\357\377\360\363\356\377\357\361\355\377\356\360\354\377\354\360" - "\355\377\354\357\354\377\352\357\355\377\354\360\355\377\355\360\355\377" - "\354\357\354\377\353\357\354\377\356\361\356\377\357\363\357\377\360\365" - "\360\377\356\361\356\377\355\360\355\377\353\357\354\377\354\360\355\377" - "\354\357\354\377\350\354\350\377\346\351\346\377\352\355\351\377\354\357" - "\354\377\352\356\353\377\355\361\356\377\354\357\354\377\353\360\356\377" - "\351\357\355\377\353\360\356\377\353\360\356\377\353\360\356\377\353\360" - "\356\377\355\360\355\377\354\360\355\377\352\356\353\377\355\361\356\377" - "\354\360\355\377\355\361\356\377\355\361\356\377\357\361\355\377\356\361" - "\356\377\355\360\355\377\355\361\356\377\355\360\355\377\356\361\356\377" - "\352\355\351\377\352\356\353\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\356\363\357\377\356\361\356\377\357\363\357\377\356\363\357\377" - "\355\361\356\377\353\357\354\377\355\361\356\377\352\357\355\377\354\357" - "\354\377\354\360\355\377\354\363\357\377\353\360\356\377\356\365\361\377" - "\357\363\357\377\361\366\361\377\356\363\360\377\355\364\360\377\355\361" - "\356\377\356\361\356\377\357\361\355\377\357\363\357\377\354\360\355\377" - "\355\361\356\377\356\363\357\377\355\361\356\377\356\361\356\377\355\361" - "\356\377\356\363\357\377\355\361\356\377\357\363\357\377\356\363\357\377" - "\360\365\361\377\360\365\360\377\360\365\360\377\357\363\357\377\354\360" - "\355\377\353\360\356\377\354\363\357\377\354\360\355\377\356\363\357\377" - "\360\365\361\377\356\361\356\377\357\363\357\377\355\360\355\377\355\361" - "\356\377\356\361\356\377\355\360\355\377\353\357\354\377\354\360\355\377" - "\355\364\360\377\355\361\356\377\355\364\360\377\355\360\355\377\355\361" - "\356\377\354\360\355\377\355\361\356\377\355\361\356\377\355\360\355\377" - "\353\357\354\377\352\356\353\377\353\357\354\377\352\356\353\377\352\357" - "\355\377\351\356\354\377\353\360\356\377\353\357\354\377\353\357\354\377" - "\353\357\354\377\354\360\355\377\356\363\357\377\357\363\357\377\356\361" - "\356\377\354\357\354\377\363\366\361\377\360\363\356\377\354\357\353\377" - "\355\360\354\377\355\360\354\377\355\360\354\377\360\363\356\377\361\363" - "\356\377\353\356\352\377\354\357\354\377\353\356\352\377\353\360\356\377" - "\354\357\354\377\354\357\354\377\355\360\355\377\353\357\354\377\356\363" - "\357\377\356\363\357\377\356\363\357\377\354\360\355\377\356\361\356\377" - "\354\360\355\377\356\363\357\377\354\363\357\377\347\353\347\377\351\356" - "\354\377\351\355\352\377\351\356\354\377\351\357\355\377\351\356\354\377" - "\353\357\354\377\354\363\357\377\353\357\354\377\352\357\355\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\353\356\352\377\355\360\355\377" - "\354\357\354\377\352\356\353\377\353\357\354\377\353\356\352\377\352\356" - "\353\377\354\360\355\377\354\360\355\377\355\360\355\377\356\361\356\377" - "\356\361\356\377\356\361\356\377\353\356\352\377\352\356\353\377\354\357" - "\354\377\355\361\356\377\355\361\356\377\355\361\356\377\355\360\355\377" - "\360\363\356\377\356\360\354\377\354\357\354\377\353\357\354\377\353\357" - "\354\377\354\363\357\377\357\363\357\377\355\361\356\377\355\361\356\377" - "\356\363\357\377\355\361\356\377\357\363\357\377\357\363\357\377\356\361" - "\356\377\347\354\352\377\354\360\355\377\355\361\356\377\354\357\354\377" - "\353\356\352\377\355\360\355\377\353\357\354\377\355\361\356\377\353\357" - "\354\377\356\360\354\377\352\355\351\377\352\357\355\377\347\355\353\377" - "\354\360\355\377\356\363\357\377\356\363\357\377\356\363\357\377\356\363" - "\357\377\356\363\357\377\354\363\357\377\354\363\357\377\360\365\361\377" - "\360\365\360\377\355\360\355\377\356\361\356\377\356\361\356\377\355\360" - "\355\377\357\361\355\377\355\360\355\377\355\360\355\377\355\360\355\377" - "\356\363\357\377\355\361\356\377\355\361\356\377\355\361\356\377\354\360" - "\355\377\354\360\355\377\354\360\355\377\354\360\355\377\355\361\356\377" - "\355\361\356\377\356\361\356\377\353\357\354\377\353\357\354\377\352\356" - "\353\377\351\356\354\377\352\356\353\377\351\355\352\377\352\356\353\377" - "\354\360\355\377\353\357\354\377\353\357\354\377\355\361\356\377\357\363" - "\357\377\357\363\357\377\360\363\356\377\356\361\356\377\361\366\361\377" - "\364\367\363\377\360\363\356\377\347\351\345\377\351\354\350\377\355\357" - "\353\377\351\353\346\377\353\354\347\377\352\355\351\377\355\357\353\377" - "\356\361\356\377\356\361\356\377\354\357\353\377\355\360\355\377\356\361" - "\356\377\354\360\355\377\356\361\356\377\354\360\355\377\356\363\357\377" - "\360\365\361\377\356\363\357\377\360\365\361\377\363\367\364\377\372\377" - "\376\377\363\370\366\377\360\366\364\377\353\360\356\377\353\360\356\377" - "\352\357\355\377\353\360\356\377\354\360\355\377\355\364\360\377\351\357" - "\355\377\352\357\355\377\352\356\353\377\353\357\354\377\353\357\354\377" - "\353\356\352\377\354\357\354\377\352\356\353\377\352\357\355\377\351\356" - "\354\377\353\357\354\377\352\356\353\377\353\357\354\377\355\361\356\377" - "\356\361\356\377\355\360\355\377\355\360\355\377\354\360\355\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\353\357\354\377\353\357\354\377" - "\352\356\353\377\355\361\356\377\357\361\355\377\355\361\356\377\355\360" - "\355\377\354\360\355\377\355\361\356\377\353\357\354\377\353\357\354\377" - "\355\361\356\377\354\360\355\377\356\363\357\377\355\361\356\377\356\363" - "\357\377\353\357\354\377\354\360\355\377\353\360\356\377\351\360\356\377" - "\356\363\357\377\354\357\354\377\354\357\353\377\354\357\353\377\354\357" - "\353\377\356\361\356\377\354\360\355\377\356\361\356\377\355\361\356\377" - "\352\357\355\377\351\356\354\377\353\357\354\377\354\360\355\377\356\363" - "\357\377\356\363\357\377\355\361\356\377\355\364\360\377\354\363\357\377" - "\355\364\360\377\355\361\356\377\357\363\357\377\356\360\354\377\355\360" - "\355\377\356\363\357\377\355\360\355\377\356\361\356\377\355\360\355\377" - "\356\363\357\377\354\360\355\377\355\360\355\377\353\357\354\377\354\360" - "\355\377\355\361\356\377\354\360\355\377\353\357\354\377\354\363\357\377" - "\353\360\356\377\353\357\354\377\356\361\356\377\357\363\357\377\353\357" - "\354\377\355\361\356\377\352\356\353\377\353\360\356\377\352\356\353\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\352\356\353\377\354\360" - "\355\377\353\357\354\377\361\366\363\377\360\365\360\377\360\363\356\377" - "\357\363\357\377\360\365\360\377\360\365\360\377\361\365\360\377\350\353" - "\347\377\347\351\345\377\342\343\337\377\327\333\326\377\336\340\335\377" - "\353\355\350\377\355\357\353\377\356\360\354\377\356\360\354\377\357\361" - "\355\377\357\361\355\377\357\361\355\377\354\360\355\377\355\361\356\377" - "\356\363\357\377\360\365\360\377\361\366\363\377\360\365\361\377\356\363" - "\357\377\361\366\363\377\365\371\366\377\357\366\364\377\353\363\360\377" - "\354\360\355\377\354\363\357\377\353\357\354\377\353\360\356\377\355\361" - "\356\377\355\361\356\377\354\363\357\377\352\357\355\377\351\357\355\377" - "\354\360\355\377\353\357\354\377\354\357\354\377\351\355\352\377\352\356" - "\353\377\351\361\357\377\350\356\354\377\351\356\354\377\352\356\353\377" - "\352\356\353\377\354\357\354\377\354\357\354\377\356\361\356\377\355\360" - "\355\377\355\360\355\377\353\357\354\377\353\357\354\377\354\360\355\377" - "\353\357\354\377\352\356\353\377\351\357\355\377\351\357\355\377\355\361" - "\356\377\356\361\356\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\354\360\355\377\355\361\356\377\354\360\355\377\355\361\356\377\356\363" - "\357\377\356\363\357\377\357\363\357\377\355\361\356\377\355\361\356\377" - "\353\360\356\377\354\364\361\377\356\363\357\377\360\363\356\377\356\360" - "\354\377\354\357\353\377\354\357\354\377\355\361\356\377\355\361\356\377" - "\355\360\355\377\352\356\353\377\353\357\354\377\353\357\354\377\355\361" - "\356\377\355\360\355\377\355\361\356\377\356\363\357\377\356\365\361\377" - "\354\363\357\377\355\361\356\377\354\363\357\377\356\363\357\377\356\361" - "\356\377\357\361\355\377\355\360\355\377\354\360\355\377\354\360\355\377" - "\356\363\357\377\356\361\356\377\355\361\356\377\355\361\356\377\355\361" - "\356\377\354\360\355\377\354\363\357\377\354\360\355\377\354\363\357\377" - "\353\357\354\377\353\360\356\377\353\357\354\377\353\357\354\377\356\361" - "\356\377\356\361\356\377\353\361\356\377\353\361\356\377\354\360\355\377" - "\352\356\353\377\353\360\356\377\353\360\356\377\353\357\354\377\353\357" - "\354\377\351\356\354\377\351\356\354\377\354\363\360\377\354\363\357\377" - "\356\363\357\377\356\363\357\377\355\361\356\377\356\361\356\377\360\365" - "\360\377\363\366\361\377\363\365\357\377\373\374\370\377\355\356\351\377" - "\335\337\333\377\324\325\320\377\346\350\343\377\355\357\353\377\355\357" - "\353\377\355\360\355\377\356\361\356\377\357\361\355\377\356\361\356\377" - "\355\360\355\377\353\357\354\377\356\361\356\377\360\365\360\377\360\365" - "\360\377\360\365\360\377\355\360\355\377\354\360\355\377\357\363\357\377" - "\356\363\357\377\353\357\354\377\353\357\354\377\355\360\355\377\352\356" - "\353\377\353\357\354\377\355\361\356\377\355\361\356\377\353\361\356\377" - "\353\360\356\377\354\363\357\377\353\357\354\377\352\356\353\377\353\356" - "\352\377\347\355\353\377\350\355\353\377\353\361\356\377\351\356\354\377" - "\352\356\353\377\352\356\353\377\353\356\352\377\355\360\355\377\354\357" - "\354\377\356\361\356\377\355\361\356\377\354\357\354\377\353\357\354\377" - "\355\361\356\377\353\357\354\377\354\360\355\377\354\357\354\377\352\356" - "\353\377\353\357\354\377\352\356\353\377\352\356\353\377\353\357\354\377" - "\355\361\356\377\354\357\354\377\355\361\356\377\354\360\355\377\356\361" - "\356\377\355\361\356\377\355\361\356\377\355\361\356\377\353\357\354\377" - "\353\357\354\377\355\361\356\377\353\360\356\377\355\364\360\377\360\365" - "\361\377\357\363\357\377\356\361\356\377\356\361\356\377\354\357\354\377" - "\356\361\356\377\354\357\354\377\355\360\355\377\355\361\357\377\352\357" - "\355\377\351\357\355\377\355\361\356\377\357\363\357\377\357\363\357\377" - "\356\361\356\377\356\361\356\377\355\361\356\377\354\360\355\377\353\361" - "\356\377\356\363\357\377\360\365\361\377\360\363\356\377\356\361\356\377" - "\354\360\355\377\353\357\354\377\353\357\354\377\355\360\355\377\357\363" - "\357\377\354\360\355\377\353\357\354\377\351\356\354\377\351\356\354\377" - "\351\356\354\377\353\363\360\377\353\360\356\377\352\357\355\377\353\361" - "\356\377\354\357\354\377\354\357\354\377\355\360\355\377\353\357\354\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\351\356\354\377\353\357" - "\354\377\352\356\353\377\351\357\355\377\353\360\356\377\351\356\354\377" - "\352\357\355\377\353\360\356\377\354\360\355\377\353\361\356\377\354\363" - "\357\377\355\364\360\377\357\366\364\377\364\370\365\377\372\376\373\377" - "\377\377\377\377\377\377\377\377\377\377\377\377\370\373\367\377\361\365" - "\360\377\357\361\355\377\356\361\356\377\354\357\354\377\354\357\354\377" - "\355\357\353\377\357\361\355\377\356\360\354\377\356\361\356\377\356\360" - "\354\377\357\363\357\377\361\366\361\377\355\364\360\377\356\363\357\377" - "\355\361\356\377\354\357\354\377\355\360\355\377\355\360\355\377\353\357" - "\354\377\355\360\355\377\353\356\352\377\354\357\354\377\353\357\354\377" - "\355\361\356\377\354\360\355\377\355\361\356\377\353\361\356\377\355\361" - "\356\377\352\356\353\377\352\356\353\377\352\356\353\377\352\357\355\377" - "\353\360\356\377\354\360\355\377\352\356\353\377\351\355\352\377\352\355" - "\351\377\352\357\355\377\352\356\353\377\355\361\356\377\353\357\354\377" - "\355\361\356\377\355\360\355\377\353\357\354\377\353\357\354\377\355\360" - "\355\377\355\360\355\377\355\360\355\377\354\360\355\377\352\357\355\377" - "\351\361\357\377\351\357\355\377\353\356\352\377\353\356\352\377\354\360" - "\355\377\355\360\355\377\356\361\356\377\356\361\356\377\353\357\354\377" - "\351\356\354\377\356\361\356\377\354\357\354\377\354\360\355\377\353\357" - "\354\377\353\360\356\377\356\363\357\377\354\360\355\377\354\360\355\377" - "\353\357\354\377\352\356\353\377\354\357\354\377\354\357\354\377\355\360" - "\355\377\353\357\354\377\355\361\356\377\353\357\354\377\353\357\354\377" - "\353\357\354\377\354\360\355\377\357\363\357\377\357\363\357\377\356\363" - "\357\377\356\363\357\377\356\363\357\377\355\361\356\377\356\361\356\377" - "\353\357\354\377\355\361\356\377\356\363\357\377\354\360\355\377\354\363" - "\357\377\355\361\356\377\355\360\355\377\355\361\356\377\353\360\356\377" - "\352\357\355\377\351\356\354\377\353\360\356\377\353\360\356\377\353\360" - "\356\377\354\360\355\377\355\361\356\377\356\361\356\377\354\360\355\377" - "\355\360\355\377\353\357\354\377\354\360\355\377\354\360\355\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\353\357\354\377\352\356\353\377" - "\353\361\356\377\353\360\356\377\353\360\356\377\354\360\355\377\356\361" - "\356\377\355\364\360\377\353\357\354\377\354\360\356\377\363\367\364\377" - "\360\365\361\377\372\376\373\377\375\377\376\377\376\377\376\377\376\377" - "\372\377\366\370\363\377\360\365\360\377\355\361\356\377\353\357\354\377" - "\351\356\354\377\352\356\353\377\354\360\355\377\355\360\355\377\352\355" - "\351\377\354\357\354\377\355\360\355\377\354\363\357\377\351\356\354\377" - "\353\363\360\377\357\363\357\377\356\361\356\377\353\356\352\377\347\353" - "\347\377\352\355\351\377\352\356\353\377\355\360\355\377\353\356\352\377" - "\353\357\354\377\353\357\354\377\352\356\353\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\353\357\354\377\354\357\354\377\355\360\355\377" - "\354\360\355\377\353\356\352\377\356\361\356\377\357\363\357\377\355\357" - "\353\377\355\360\355\377\355\360\355\377\353\356\352\377\353\356\352\377" - "\355\361\356\377\355\361\356\377\355\361\356\377\353\356\352\377\353\356" - "\352\377\354\360\355\377\355\360\355\377\355\360\355\377\353\356\352\377" - "\352\356\353\377\352\356\353\377\347\355\353\377\353\357\354\377\354\357" - "\354\377\353\356\352\377\352\355\351\377\353\356\352\377\353\356\352\377" - "\353\357\354\377\352\356\353\377\351\356\354\377\354\360\355\377\355\357" - "\353\377\353\356\352\377\352\356\353\377\354\360\355\377\356\363\357\377" - "\354\357\354\377\356\361\356\377\354\360\355\377\353\357\354\377\353\357" - "\354\377\355\360\355\377\356\361\356\377\354\360\355\377\353\361\356\377" - "\354\360\355\377\354\363\357\377\355\361\356\377\353\357\354\377\355\360" - "\355\377\356\363\357\377\356\363\357\377\355\361\356\377\356\363\357\377" - "\356\363\357\377\356\363\357\377\353\357\354\377\353\357\354\377\353\357" - "\354\377\354\360\355\377\354\360\355\377\353\357\354\377\353\356\352\377" - "\352\356\353\377\353\357\354\377\353\360\356\377\352\357\355\377\351\356" - "\354\377\354\360\355\377\353\360\356\377\353\357\354\377\356\361\356\377" - "\356\361\356\377\353\357\354\377\352\356\353\377\355\361\356\377\352\356" - "\353\377\352\356\353\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\352\356\353\377\354\360\355\377\353\357\354\377\353\361\356\377\352\357" - "\355\377\353\357\354\377\356\361\356\377\357\363\357\377\354\360\355\377" - "\353\357\355\377\353\357\354\377\354\360\355\377\361\366\363\377\361\366" - "\363\377\364\370\364\377\372\376\372\377\361\366\361\377\360\365\360\377" - "\355\361\356\377\354\360\355\377\352\357\355\377\347\355\353\377\351\355" - "\352\377\350\354\351\377\350\354\350\377\353\357\354\377\352\356\353\377" - "\351\356\354\377\352\361\357\377\351\360\356\377\355\361\356\377\353\357" - "\354\377\355\360\355\377\354\357\354\377\352\355\351\377\354\357\354\377" - "\353\357\354\377\351\355\352\377\351\356\354\377\352\356\353\377\354\360" - "\355\377\353\357\354\377\354\360\355\377\353\356\352\377\354\357\354\377" - "\353\356\352\377\352\356\353\377\353\357\354\377\354\360\355\377\353\357" - "\354\377\355\361\356\377\355\360\355\377\355\357\353\377\356\360\354\377" - "\354\357\354\377\355\360\355\377\353\357\354\377\355\360\355\377\355\361" - "\356\377\353\356\352\377\352\355\351\377\354\357\354\377\353\356\352\377" - "\357\363\357\377\355\360\355\377\353\360\356\377\351\356\354\377\347\356" - "\355\377\352\356\353\377\353\357\354\377\353\356\352\377\352\356\353\377" - "\353\356\352\377\353\356\352\377\353\356\352\377\351\355\352\377\347\355" - "\353\377\354\360\355\377\355\360\355\377\354\360\355\377\351\355\352\377" - "\354\360\355\377\353\357\354\377\356\361\356\377\356\363\357\377\353\357" - "\354\377\350\356\354\377\354\360\355\377\354\357\354\377\355\361\356\377" - "\354\363\357\377\354\360\355\377\355\361\356\377\353\357\354\377\353\360" - "\356\377\355\361\356\377\355\361\356\377\354\360\355\377\355\361\356\377" - "\356\363\357\377\357\363\357\377\356\360\354\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\353\357\354\377\355\361\356\377" - "\355\361\356\377\356\361\356\377\355\361\356\377\352\356\353\377\350\356" - "\354\377\352\357\355\377\354\360\355\377\353\357\354\377\353\357\354\377" - "\353\356\352\377\355\360\355\377\360\363\356\377\352\356\353\377\352\356" - "\353\377\353\357\354\377\354\360\355\377\354\360\355\377\354\363\357\377" - "\355\361\356\377\354\360\355\377\354\360\355\377\353\357\354\377\355\361" - "\356\377\353\360\356\377\353\360\356\377\356\363\357\377\355\360\355\377" - "\355\360\355\377\355\360\355\377\354\360\355\377\353\357\354\377\352\356" - "\353\377\355\360\355\377\355\360\355\377\345\351\346\377\354\357\354\377" - "\353\356\352\377\354\357\354\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\352\356\353\377\352\355\351\377\350\354\350\377\351\355\352\377" - "\351\355\352\377\352\356\353\377\352\356\353\377\347\355\353\377\353\361" - "\356\377\354\360\355\377\352\356\353\377\352\356\353\377\353\356\352\377" - "\352\356\353\377\353\356\352\377\352\356\353\377\350\354\351\377\347\354" - "\352\377\352\355\351\377\353\357\354\377\355\360\355\377\353\356\352\377" - "\353\356\352\377\352\355\351\377\355\360\355\377\355\360\355\377\354\357" - "\354\377\352\357\355\377\352\356\353\377\352\356\353\377\355\360\355\377" - "\353\356\352\377\353\356\352\377\355\357\353\377\354\357\354\377\355\361" - "\356\377\354\360\355\377\360\365\361\377\355\360\354\377\350\354\350\377" - "\350\354\350\377\353\356\352\377\354\357\354\377\353\356\352\377\352\356" - "\353\377\351\356\354\377\347\355\353\377\351\356\354\377\353\357\354\377" - "\354\360\355\377\354\357\354\377\353\356\352\377\353\356\352\377\355\360" - "\355\377\352\356\353\377\352\356\353\377\352\356\353\377\354\360\355\377" - "\354\360\355\377\347\356\355\377\354\363\357\377\353\357\354\377\355\360" - "\355\377\355\360\355\377\353\357\354\377\353\357\354\377\352\356\353\377" - "\352\357\355\377\354\360\355\377\355\361\356\377\354\360\355\377\354\363" - "\357\377\352\357\355\377\352\357\355\377\353\357\354\377\355\361\356\377" - "\355\361\356\377\355\361\356\377\357\363\357\377\356\360\354\377\355\360" - "\355\377\355\361\356\377\355\361\356\377\356\363\357\377\355\360\355\377" - "\355\360\355\377\354\357\354\377\354\360\355\377\353\357\354\377\353\357" - "\354\377\352\357\355\377\350\355\353\377\351\356\354\377\353\357\354\377" - "\353\356\352\377\352\356\353\377\354\357\354\377\357\361\355\377\357\361" - "\355\377\353\356\352\377\353\356\352\377\354\360\355\377\354\360\355\377" - "\352\356\353\377\353\360\356\377\355\361\356\377\355\361\356\377\353\357" - "\354\377\355\361\356\377\353\357\354\377\354\363\357\377\355\361\356\377" - "\354\360\355\377\355\360\355\377\354\360\355\377\355\361\356\377\354\360" - "\355\377\354\360\355\377\355\360\355\377\356\360\354\377\355\360\354\377" - "\353\356\352\377\354\357\354\377\351\355\352\377\352\356\353\377\351\355" - "\352\377\352\356\353\377\352\356\353\377\354\357\354\377\350\354\350\377" - "\350\354\350\377\350\354\351\377\351\355\352\377\352\356\353\377\354\357" - "\354\377\353\357\354\377\353\361\356\377\354\363\357\377\355\361\356\377" - "\351\356\354\377\352\356\353\377\352\355\351\377\352\356\353\377\350\354" - "\351\377\346\354\351\377\344\351\347\377\352\356\353\377\354\357\354\377" - "\354\357\354\377\352\355\351\377\352\355\351\377\352\355\351\377\353\357" - "\354\377\354\357\354\377\354\360\355\377\351\356\354\377\351\356\354\377" - "\352\356\353\377\353\356\352\377\354\357\354\377\356\360\354\377\355\357" - "\353\377\355\360\355\377\354\360\355\377\355\361\356\377\356\361\356\377" - "\354\357\353\377\347\351\345\377\344\350\344\377\352\355\351\377\353\356" - "\352\377\353\356\352\377\353\357\354\377\353\357\354\377\346\355\354\377" - "\353\360\356\377\351\361\357\377\353\360\356\377\354\360\355\377\354\357" - "\354\377\354\357\354\377\354\360\355\377\354\360\355\377\353\361\356\377" - "\354\360\355\377\351\356\354\377\346\357\356\377\347\355\354\377\353\360" - "\356\377\353\357\354\377\352\357\355\377\351\355\352\377\352\355\351\377" - "\352\355\351\377\353\356\352\377\352\355\351\377\353\357\354\377\354\360" - "\355\377\353\357\354\377\353\360\356\377\352\357\355\377\353\360\356\377" - "\355\361\356\377\355\361\356\377\354\360\355\377\355\361\356\377\360\363" - "\356\377\356\360\354\377\354\357\354\377\354\357\354\377\355\361\356\377" - "\352\357\355\377\354\360\355\377\355\360\355\377\355\361\356\377\354\360" - "\355\377\354\357\354\377\351\355\352\377\347\354\352\377\347\354\352\377" - "\347\355\353\377\354\360\355\377\353\357\354\377\353\356\352\377\354\357" - "\354\377\357\361\355\377\357\361\355\377\355\357\353\377\354\357\354\377" - "\354\360\355\377\354\357\354\377\353\357\354\377\353\357\354\377\354\360" - "\355\377\356\361\356\377\355\361\356\377\355\361\356\377\354\360\355\377" - "\352\357\355\377\353\357\354\377\355\361\356\377\357\363\357\377\355\361" - "\356\377\354\360\355\377\354\360\355\377\356\363\357\377\357\363\357\377" - "\360\363\356\377\360\363\356\377\356\360\354\377\355\364\360\377\355\361" - "\356\377\351\356\354\377\351\356\354\377\347\355\353\377\352\356\353\377" - "\354\357\354\377\352\355\351\377\351\355\352\377\350\354\351\377\351\355" - "\352\377\352\355\351\377\354\357\354\377\354\357\354\377\351\355\352\377" - "\351\356\354\377\353\360\356\377\352\357\355\377\351\355\352\377\352\356" - "\353\377\351\355\352\377\351\355\352\377\350\354\351\377\347\355\353\377" - "\353\357\354\377\354\360\355\377\354\357\354\377\350\354\350\377\350\354" - "\350\377\352\356\353\377\351\356\354\377\350\355\353\377\353\360\356\377" - "\353\360\356\377\351\356\354\377\353\356\352\377\352\355\351\377\353\357" - "\354\377\354\357\354\377\355\360\355\377\355\360\355\377\354\363\357\377" - "\356\363\357\377\357\363\357\377\360\363\356\377\353\356\352\377\347\353" - "\347\377\352\355\351\377\350\354\350\377\352\356\353\377\353\357\354\377" - "\353\357\354\377\353\357\354\377\351\356\354\377\352\357\355\377\353\360" - "\356\377\351\356\354\377\351\355\352\377\351\355\352\377\353\357\354\377" - "\353\357\354\377\354\360\355\377\353\361\356\377\347\355\353\377\350\355" - "\353\377\350\355\353\377\352\357\355\377\351\356\354\377\351\356\354\377" - "\351\356\354\377\353\356\352\377\353\356\352\377\354\357\354\377\353\357" - "\354\377\355\361\356\377\355\361\356\377\353\360\356\377\354\363\357\377" - "\354\360\355\377\353\361\356\377\351\357\355\377\354\360\355\377\353\357" - "\354\377\354\360\355\377\356\361\356\377\356\361\356\377\356\361\356\377" - "\354\360\355\377\353\357\354\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\354\360\355\377\354\360\355\377\351\355\352\377\350\354\350\377" - "\350\355\353\377\350\355\353\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\355\361\356\377\356\361\356\377\360\363\356\377" - "\357\361\355\377\354\357\354\377\352\356\353\377\354\357\354\377\355\361" - "\356\377\355\360\355\377\356\361\356\377\355\360\355\377\353\357\354\377" - "\355\361\356\377\354\360\355\377\353\360\356\377\353\360\356\377\352\357" - "\355\377\355\360\355\377\356\361\356\377\353\357\354\377\354\363\357\377" - "\356\360\354\377\357\363\357\377\360\365\360\377\360\365\360\377\357\363" - "\357\377\357\363\357\377\354\357\354\377\354\357\354\377\352\356\353\377" - "\352\356\353\377\353\357\354\377\353\357\354\377\351\355\352\377\353\357" - "\354\377\350\354\351\377\351\355\352\377\352\355\351\377\354\357\354\377" - "\347\354\352\377\350\355\353\377\347\355\353\377\352\357\355\377\351\356" - "\354\377\351\356\354\377\347\357\355\377\350\354\351\377\351\356\354\377" - "\350\354\351\377\351\357\355\377\351\357\355\377\355\364\360\377\356\363" - "\357\377\361\366\361\377\354\357\354\377\352\357\355\377\353\360\357\377" - "\352\357\355\377\351\356\354\377\355\361\356\377\355\361\356\377\356\361" - "\356\377\354\357\354\377\355\360\355\377\353\356\352\377\353\356\352\377" - "\352\355\351\377\353\360\356\377\354\363\357\377\356\363\357\377\366\371" - "\366\377\363\367\363\377\356\361\356\377\354\357\354\377\351\355\352\377" - "\351\355\352\377\351\355\352\377\353\357\354\377\351\355\352\377\351\356" - "\354\377\351\355\352\377\351\355\352\377\351\357\355\377\352\356\353\377" - "\350\356\354\377\351\357\355\377\353\361\356\377\353\361\356\377\354\363" - "\357\377\353\357\354\377\353\360\356\377\353\360\356\377\353\357\354\377" - "\353\360\356\377\354\363\357\377\355\361\356\377\357\363\357\377\357\363" - "\357\377\356\361\356\377\353\357\354\377\355\361\356\377\355\361\356\377" - "\353\360\356\377\353\360\356\377\355\364\360\377\355\364\360\377\354\360" - "\355\377\354\360\355\377\353\357\354\377\353\357\354\377\354\360\355\377" - "\354\357\354\377\355\360\355\377\356\361\356\377\356\363\357\377\355\361" - "\356\377\355\361\356\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\354\357\354\377\350\354\351\377\351\355\352\377\351\355\352\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\360\365\360\377\357\363\357\377\360\363\356\377\355\361\356\377\353\357" - "\354\377\352\356\353\377\353\357\354\377\356\361\356\377\355\361\356\377" - "\353\357\354\377\353\357\354\377\355\361\356\377\354\360\355\377\353\357" - "\354\377\352\357\355\377\354\360\355\377\356\361\356\377\355\360\355\377" - "\356\360\354\377\355\360\355\377\355\360\355\377\356\363\357\377\360\365" - "\361\377\356\363\357\377\357\363\357\377\360\363\356\377\360\363\356\377" - "\356\361\356\377\355\360\355\377\352\356\353\377\350\356\354\377\352\356" - "\353\377\352\357\355\377\352\356\353\377\355\357\353\377\354\357\354\377" - "\350\354\350\377\350\355\353\377\351\356\354\377\347\355\354\377\351\355" - "\352\377\351\355\352\377\345\355\353\377\346\355\353\377\350\354\351\377" - "\350\354\351\377\347\353\350\377\351\355\352\377\355\361\356\377\353\361" - "\356\377\353\361\356\377\355\364\360\377\356\361\356\377\354\360\355\377" - "\355\360\355\377\353\357\354\377\351\356\354\377\352\356\353\377\354\360" - "\355\377\354\360\355\377\356\361\356\377\356\361\356\377\356\361\356\377" - "\353\356\352\377\353\356\352\377\352\355\351\377\351\355\352\377\352\357" - "\355\377\353\360\356\377\355\361\356\377\354\360\355\377\352\356\353\377" - "\352\355\351\377\353\356\352\377\351\355\352\377\350\354\351\377\351\355" - "\352\377\352\356\353\377\353\357\354\377\351\355\352\377\354\357\353\377" - "\352\356\353\377\352\355\351\377\350\356\354\377\351\355\352\377\352\356" - "\353\377\352\356\353\377\354\360\355\377\351\356\354\377\351\356\354\377" - "\353\360\356\377\353\360\356\377\351\356\354\377\353\357\354\377\356\361" - "\356\377\356\361\356\377\356\361\356\377\355\360\355\377\353\357\354\377" - "\355\361\356\377\355\361\356\377\353\360\356\377\353\363\360\377\354\364" - "\361\377\353\363\360\377\354\363\357\377\354\360\355\377\354\360\355\377" - "\355\360\355\377\357\363\357\377\356\363\357\377\357\363\357\377\356\361" - "\356\377\355\360\355\377\354\360\355\377\353\357\354\377\353\357\354\377" - "\355\361\356\377\354\360\355\377\352\356\353\377\352\356\353\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\352\355\351\377\352\356\353\377" - "\352\356\353\377\352\356\353\377\356\361\356\377\355\360\355\377\355\360" - "\355\377\354\360\355\377\353\357\354\377\355\361\356\377\356\361\356\377" - "\354\357\354\377\353\357\354\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\352\357\355\377\354\363\357\377\354\363\357\377" - "\354\360\355\377\355\361\356\377\356\361\356\377\354\357\354\377\354\357" - "\353\377\352\355\351\377\354\360\355\377\354\360\355\377\360\365\360\377" - "\361\365\360\377\357\363\357\377\356\361\356\377\353\357\354\377\351\355" - "\352\377\351\357\355\377\351\355\352\377\354\357\354\377\353\356\352\377" - "\353\356\352\377\353\356\352\377\353\357\354\377\350\355\353\377\351\356" - "\354\377\352\357\355\377\351\355\352\377\347\355\353\377\346\355\354\377" - "\346\355\354\377\353\360\356\377\345\351\346\377\344\350\345\377\344\350" - "\345\377\352\356\353\377\351\361\357\377\354\360\355\377\354\363\357\377" - "\354\360\355\377\355\360\355\377\355\360\355\377\354\360\355\377\355\361" - "\356\377\354\360\355\377\355\360\355\377\353\357\354\377\354\360\355\377" - "\354\360\355\377\355\357\353\377\344\346\342\377\347\351\345\377\350\354" - "\350\377\350\354\351\377\347\355\353\377\350\355\353\377\351\356\354\377" - "\351\360\356\377\351\355\352\377\354\357\354\377\354\357\354\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\351\355\352\377\353\356\352\377" - "\351\355\352\377\353\357\354\377\352\356\353\377\353\356\352\377\351\355" - "\352\377\352\355\351\377\352\356\353\377\351\355\352\377\350\355\353\377" - "\350\355\353\377\347\354\352\377\351\356\354\377\352\356\353\377\352\357" - "\355\377\352\356\353\377\354\360\355\377\356\361\356\377\355\360\355\377" - "\356\361\356\377\356\361\356\377\355\360\355\377\356\363\357\377\356\363" - "\357\377\354\363\357\377\353\363\360\377\353\363\360\377\355\364\360\377" - "\356\363\357\377\356\363\357\377\355\361\356\377\355\361\356\377\357\363" - "\357\377\356\361\356\377\357\363\357\377\354\357\354\377\355\360\355\377" - "\355\360\355\377\355\360\355\377\354\363\357\377\353\357\354\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\356\363\357\377\353\357\354\377" - "\352\355\351\377\345\351\346\377\350\354\350\377\353\356\352\377\354\357" - "\354\377\356\361\356\377\353\357\354\377\352\356\353\377\354\357\354\377" - "\353\357\354\377\355\361\356\377\355\360\355\377\355\361\356\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\352\357\355\377\351\357\355\377" - "\352\357\355\377\355\361\356\377\356\363\357\377\354\360\355\377\357\363" - "\357\377\355\361\356\377\353\356\352\377\353\357\354\377\352\356\353\377" - "\355\361\356\377\357\363\357\377\361\365\360\377\364\367\363\377\357\363" - "\357\377\355\361\356\377\353\357\354\377\351\357\355\377\355\361\356\377" - "\355\360\355\377\353\356\352\377\353\356\352\377\353\356\352\377\353\356" - "\352\377\352\356\353\377\351\355\352\377\353\357\354\377\352\356\353\377" - "\347\355\353\377\347\355\353\377\352\357\355\377\355\364\360\377\360\365" - "\361\377\351\355\352\377\352\356\353\377\351\356\354\377\352\357\355\377" - "\350\356\354\377\354\360\355\377\354\360\355\377\354\360\355\377\354\357" - "\354\377\355\361\356\377\354\360\355\377\351\357\355\377\355\361\356\377" - "\347\355\353\377\352\357\355\377\356\363\357\377\360\361\355\377\353\355" - "\350\377\337\340\334\377\350\354\350\377\351\356\354\377\351\356\354\377" - "\352\357\355\377\347\355\354\377\347\355\354\377\351\356\354\377\352\356" - "\353\377\352\356\353\377\353\357\354\377\353\357\354\377\352\356\353\377" - "\353\356\352\377\350\354\350\377\350\354\351\377\352\356\353\377\351\355" - "\352\377\353\357\354\377\353\357\354\377\353\356\352\377\351\355\352\377" - "\350\360\356\377\347\356\355\377\347\355\354\377\347\355\354\377\347\355" - "\354\377\347\355\353\377\353\357\354\377\352\356\353\377\352\356\353\377" - "\354\360\355\377\355\360\355\377\355\360\355\377\356\361\356\377\354\357" - "\354\377\353\357\354\377\355\361\356\377\355\364\360\377\354\363\357\377" - "\353\363\360\377\356\363\357\377\356\363\357\377\356\361\356\377\357\363" - "\357\377\357\363\357\377\356\363\357\377\357\363\357\377\353\357\354\377" - "\356\361\356\377\356\361\356\377\355\360\355\377\355\361\356\377\351\357" - "\355\377\352\356\353\377\353\357\354\377\355\361\356\377\356\361\356\377" - "\353\356\352\377\347\351\345\377\327\330\324\377\313\315\311\377\345\350" - "\344\377\352\355\351\377\353\356\352\377\355\361\356\377\353\357\354\377" - "\352\356\353\377\353\356\352\377\352\356\353\377\351\357\355\377\354\360" - "\355\377\355\361\356\377\356\361\356\377\353\357\354\377\353\357\354\377" - "\353\360\356\377\353\363\360\377\354\363\357\377\355\361\356\377\356\363" - "\357\377\355\361\356\377\356\363\357\377\356\363\357\377\356\361\356\377" - "\354\363\357\377\353\357\354\377\354\360\355\377\357\363\357\377\356\361" - "\356\377\356\363\357\377\355\361\356\377\354\360\355\377\354\360\355\377" - "\353\361\356\377\350\354\350\377\352\355\351\377\352\355\351\377\352\355" - "\351\377\353\356\352\377\352\355\351\377\351\355\352\377\353\357\354\377" - "\353\357\354\377\352\356\353\377\350\356\354\377\353\360\356\377\355\364" - "\360\377\357\366\364\377\364\370\365\377\356\363\357\377\354\360\355\377" - "\351\356\354\377\350\355\353\377\351\356\354\377\352\357\355\377\353\357" - "\354\377\352\356\353\377\354\357\354\377\354\360\355\377\354\357\354\377" - "\351\357\355\377\355\361\356\377\351\356\354\377\354\363\357\377\363\366" - "\361\377\365\370\364\377\357\361\355\377\356\360\354\377\352\356\353\377" - "\354\357\354\377\351\356\354\377\350\355\353\377\347\355\354\377\346\356" - "\355\377\352\357\355\377\351\357\355\377\352\356\353\377\347\355\353\377" - "\350\354\351\377\353\356\352\377\350\354\350\377\350\355\353\377\350\355" - "\353\377\351\356\354\377\350\354\351\377\351\355\352\377\354\357\354\377" - "\352\355\351\377\352\357\355\377\347\356\355\377\347\356\355\377\351\360" - "\356\377\346\354\353\377\350\356\355\377\352\356\353\377\353\357\354\377" - "\351\355\352\377\352\356\353\377\354\360\355\377\354\357\354\377\355\357" - "\353\377\357\361\355\377\352\357\355\377\354\363\357\377\351\361\357\377" - "\353\360\356\377\352\357\355\377\353\360\356\377\355\360\355\377\356\361" - "\356\377\357\363\357\377\355\360\355\377\356\361\356\377\355\360\355\377" - "\357\361\355\377\356\363\357\377\355\360\355\377\355\357\353\377\353\356" - "\352\377\353\357\354\377\353\357\354\377\353\357\354\377\354\357\354\377" - "\356\361\356\377\361\366\361\377\360\365\360\377\365\367\361\377\317\320" - "\313\377\262\266\263\377\317\323\317\377\347\351\345\377\352\355\351\377" - "\354\360\355\377\353\357\354\377\353\360\356\377\353\360\356\377\352\357" - "\355\377\350\356\354\377\351\357\355\377\353\357\354\377\355\361\356\377" - "\353\357\354\377\353\360\356\377\352\357\355\377\351\356\354\377\352\357" - "\355\377\353\361\356\377\356\363\357\377\356\361\356\377\356\363\357\377" - "\355\361\356\377\356\361\356\377\355\361\356\377\354\360\355\377\356\363" - "\357\377\357\363\357\377\357\363\357\377\355\364\360\377\353\360\356\377" - "\354\360\355\377\356\361\356\377\355\361\356\377\347\353\347\377\341\343" - "\337\377\344\346\342\377\350\354\350\377\351\355\352\377\352\355\351\377" - "\352\355\351\377\351\355\352\377\350\354\351\377\352\356\353\377\350\356" - "\353\377\347\357\354\377\347\356\355\377\351\361\357\377\355\361\356\377" - "\354\360\355\377\352\356\353\377\355\360\355\377\354\357\354\377\353\357" - "\354\377\352\356\353\377\355\357\353\377\353\357\354\377\353\356\352\377" - "\354\357\354\377\355\360\355\377\354\363\357\377\353\361\356\377\351\357" - "\355\377\356\363\357\377\360\365\360\377\367\371\365\377\364\367\363\377" - "\361\365\360\377\355\361\356\377\354\360\355\377\352\357\355\377\351\356" - "\354\377\351\356\354\377\352\356\353\377\350\354\351\377\347\355\353\377" - "\352\356\353\377\353\357\354\377\351\355\352\377\350\354\351\377\341\350" - "\346\377\346\353\351\377\344\353\351\377\347\355\353\377\351\356\354\377" - "\354\357\354\377\352\356\353\377\353\356\352\377\347\355\353\377\347\355" - "\353\377\351\356\354\377\350\356\355\377\346\354\353\377\350\355\353\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\353\357\354\377\354\360" - "\355\377\355\361\356\377\356\361\356\377\355\360\355\377\354\363\357\377" - "\350\360\356\377\350\360\356\377\354\364\361\377\353\360\356\377\353\360" - "\356\377\356\361\356\377\357\363\357\377\356\361\356\377\355\360\355\377" - "\355\361\356\377\356\363\357\377\355\360\355\377\355\361\356\377\355\357" - "\353\377\354\357\354\377\360\363\356\377\355\361\356\377\356\361\356\377" - "\353\356\352\377\352\355\351\377\355\360\355\377\357\363\357\377\374\377" - "\374\377\377\377\377\377\377\377\374\377\376\377\373\377\364\367\363\377" - "\355\357\353\377\353\356\352\377\353\357\354\377\352\356\353\377\352\357" - "\355\377\351\356\354\377\351\356\354\377\352\356\353\377\351\357\355\377" - "\353\357\354\377\354\360\355\377\355\361\356\377\355\364\360\377\353\360" - "\356\377\353\360\356\377\353\360\356\377\353\357\354\377\355\361\356\377" - "\354\363\357\377\353\360\356\377\352\357\355\377\353\360\356\377\352\357" - "\355\377\353\360\356\377\356\361\356\377\356\361\356\377\357\363\357\377" - "\354\363\357\377\353\360\356\377\347\355\353\377\354\360\355\377\361\366" - "\363\377\373\376\372\377\372\374\370\377\357\363\357\377\353\356\352\377" - "\352\356\353\377\352\355\351\377\352\355\351\377\351\355\352\377\351\355" - "\352\377\351\355\352\377\347\355\352\377\347\355\353\377\350\355\353\377" - "\352\357\355\377\355\361\356\377\354\360\355\377\354\360\355\377\357\363" - "\357\377\355\360\355\377\353\357\354\377\347\355\353\377\355\360\355\377" - "\354\357\354\377\354\357\354\377\354\357\354\377\353\356\352\377\355\361" - "\356\377\355\361\356\377\360\365\361\377\356\363\357\377\355\361\356\377" - "\356\361\356\377\357\363\357\377\357\363\357\377\356\361\356\377\353\356" - "\352\377\353\357\354\377\351\356\354\377\351\355\352\377\351\355\352\377" - "\351\355\352\377\352\356\353\377\353\357\354\377\353\357\354\377\351\355" - "\352\377\352\356\353\377\345\353\352\377\345\353\352\377\350\355\353\377" - "\350\355\353\377\351\356\354\377\350\354\351\377\346\354\352\377\350\356" - "\354\377\351\355\352\377\353\357\354\377\353\357\354\377\351\356\354\377" - "\352\356\353\377\351\355\352\377\353\357\354\377\352\356\353\377\352\356" - "\353\377\353\357\354\377\353\356\352\377\354\360\355\377\354\363\357\377" - "\354\360\355\377\355\364\360\377\353\360\356\377\352\357\355\377\355\364" - "\360\377\355\364\360\377\356\363\357\377\356\361\356\377\357\363\357\377" - "\357\363\357\377\360\365\360\377\356\363\357\377\360\365\361\377\357\363" - "\357\377\356\363\357\377\352\357\355\377\352\357\355\377\354\360\355\377" - "\360\365\361\377\360\365\360\377\352\355\351\377\353\356\352\377\356\360" - "\354\377\357\363\357\377\364\370\365\377\374\376\372\377\377\377\377\377" - "\377\377\377\377\370\372\366\377\353\356\352\377\355\360\355\377\353\357" - "\354\377\353\360\356\377\353\357\354\377\353\357\354\377\351\355\352\377" - "\345\351\346\377\350\356\354\377\354\360\355\377\353\360\356\377\354\363" - "\357\377\353\360\356\377\355\364\360\377\352\357\355\377\353\357\354\377" - "\354\360\355\377\352\357\355\377\353\360\356\377\352\357\355\377\352\356" - "\353\377\352\357\355\377\352\357\355\377\352\357\355\377\353\357\354\377" - "\352\357\355\377\350\360\356\377\351\361\357\377\355\364\360\377\355\364" - "\360\377\360\365\361\377\366\371\366\377\372\374\370\377\372\376\372\377" - "\363\367\364\377\355\357\353\377\355\360\355\377\354\357\354\377\354\357" - "\354\377\353\356\352\377\353\356\352\377\345\351\346\377\350\354\351\377" - "\350\354\351\377\351\355\352\377\355\361\356\377\353\357\354\377\354\357" - "\354\377\354\360\355\377\355\360\355\377\356\360\354\377\350\354\350\377" - "\346\351\346\377\350\354\350\377\354\357\354\377\353\356\352\377\355\360" - "\355\377\353\357\354\377\353\357\354\377\353\357\354\377\354\360\355\377" - "\355\361\356\377\360\365\361\377\357\363\357\377\356\361\356\377\353\356" - "\352\377\354\357\354\377\354\357\354\377\353\357\354\377\354\360\355\377" - "\352\355\351\377\351\355\352\377\353\357\354\377\354\360\355\377\353\357" - "\354\377\352\356\353\377\351\355\352\377\350\354\351\377\344\351\347\377" - "\350\354\351\377\350\354\351\377\345\351\346\377\347\353\350\377\350\354" - "\351\377\347\355\353\377\347\355\353\377\352\356\353\377\352\356\353\377" - "\347\355\353\377\351\356\354\377\353\357\354\377\353\357\354\377\347\355" - "\353\377\350\356\354\377\352\356\353\377\355\360\355\377\354\360\355\377" - "\352\357\355\377\352\357\355\377\354\360\355\377\353\360\356\377\355\361" - "\356\377\353\360\356\377\356\363\357\377\355\361\356\377\356\363\357\377" - "\356\361\356\377\356\363\357\377\360\365\361\377\353\360\356\377\355\361" - "\356\377\355\361\356\377\357\363\357\377\357\363\357\377\355\364\360\377" - "\354\363\357\377\355\361\356\377\360\365\361\377\361\366\363\377\364\367" - "\363\377\361\366\361\377\356\363\357\377\356\363\357\377\361\366\363\377" - "\361\366\363\377\363\366\361\377\366\371\366\377\360\365\361\377\352\356" - "\353\377\353\357\354\377\355\361\356\377\353\357\354\377\352\356\353\377" - "\356\365\361\377\364\370\365\377\356\363\357\377\352\356\353\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\355\361\356\377\354\360\355\377" - "\355\361\356\377\355\361\356\377\355\361\356\377\356\363\357\377\354\363" - "\357\377\355\364\360\377\354\360\355\377\353\360\356\377\353\360\356\377" - "\353\360\356\377\353\360\356\377\353\360\356\377\353\360\356\377\354\364" - "\361\377\355\361\356\377\356\365\361\377\356\363\357\377\360\365\360\377" - "\357\363\357\377\355\357\353\377\352\356\353\377\352\355\351\377\353\357" - "\354\377\355\360\355\377\354\357\354\377\355\360\355\377\355\360\355\377" - "\354\360\355\377\347\355\353\377\352\357\355\377\354\360\355\377\353\357" - "\354\377\354\360\355\377\355\361\356\377\357\363\357\377\354\360\355\377" - "\361\366\363\377\361\366\363\377\356\363\357\377\353\357\354\377\356\361" - "\356\377\356\361\356\377\354\357\354\377\355\360\355\377\353\357\354\377" - "\351\355\352\377\352\356\353\377\354\360\355\377\360\363\356\377\357\363" - "\357\377\353\357\354\377\353\357\354\377\353\357\354\377\354\357\354\377" - "\353\357\354\377\353\357\354\377\350\354\351\377\350\354\351\377\352\355" - "\351\377\353\357\354\377\353\357\354\377\353\357\354\377\353\356\352\377" - "\351\355\352\377\353\357\354\377\351\355\352\377\350\354\351\377\352\355" - "\351\377\350\354\351\377\350\354\351\377\346\354\352\377\350\354\351\377" - "\347\355\353\377\353\357\354\377\352\357\355\377\353\357\354\377\352\357" - "\355\377\353\357\354\377\353\356\352\377\352\355\351\377\352\355\351\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\352\357\355\377\356\363" - "\357\377\356\363\357\377\354\360\355\377\354\360\355\377\356\363\357\377" - "\356\363\357\377\356\363\357\377\355\361\356\377\355\361\356\377\355\361" - "\356\377\355\364\360\377\354\363\357\377\355\364\360\377\354\360\355\377" - "\356\363\357\377\355\361\356\377\354\363\357\377\353\361\356\377\360\365" - "\361\377\356\365\361\377\360\365\360\377\357\363\357\377\355\361\356\377" - "\356\363\357\377\354\360\355\377\360\365\361\377\360\365\361\377\345\351" - "\346\377\352\356\353\377\352\356\353\377\352\356\353\377\354\357\354\377" - "\354\360\355\377\354\363\357\377\363\367\364\377\360\365\361\377\363\367" - "\364\377\355\361\356\377\353\357\354\377\353\357\354\377\353\357\354\377" - "\354\360\355\377\354\363\357\377\354\363\357\377\355\361\356\377\355\360" - "\355\377\356\361\356\377\356\363\357\377\355\360\355\377\354\360\355\377" - "\355\361\356\377\354\363\357\377\353\357\354\377\353\357\354\377\353\357" - "\354\377\354\363\357\377\356\363\357\377\355\361\356\377\360\365\360\377" - "\360\365\360\377\357\363\357\377\356\361\356\377\357\363\357\377\353\357" - "\354\377\353\356\352\377\353\357\354\377\354\357\354\377\353\356\352\377" - "\355\360\355\377\356\361\356\377\355\361\356\377\354\360\355\377\352\356" - "\353\377\354\357\354\377\355\360\355\377\356\361\356\377\356\361\356\377" - "\357\363\357\377\357\363\357\377\357\363\357\377\361\366\363\377\360\365" - "\361\377\353\361\356\377\353\357\354\377\353\357\354\377\356\361\356\377" - "\353\356\352\377\355\357\353\377\353\356\352\377\350\354\350\377\354\357" - "\354\377\361\365\360\377\356\360\354\377\357\361\355\377\354\360\355\377" - "\352\356\353\377\354\360\355\377\354\360\355\377\356\363\357\377\353\356" - "\352\377\353\356\352\377\352\355\351\377\354\357\354\377\355\360\355\377" - "\354\360\355\377\355\360\355\377\352\356\353\377\352\356\353\377\347\353" - "\350\377\350\354\350\377\350\354\350\377\350\354\350\377\347\353\350\377" - "\350\354\351\377\351\356\354\377\347\355\353\377\351\355\352\377\353\357" - "\354\377\353\357\354\377\354\360\355\377\353\357\354\377\352\355\351\377" - "\354\357\354\377\354\357\354\377\355\360\355\377\355\360\355\377\356\363" - "\357\377\357\363\357\377\357\363\357\377\356\363\357\377\354\363\357\377" - "\355\364\360\377\355\360\355\377\360\365\360\377\357\363\357\377\354\360" - "\355\377\353\360\356\377\355\361\356\377\356\363\357\377\354\360\355\377" - "\354\363\357\377\352\357\355\377\353\357\354\377\355\361\356\377\355\361" - "\356\377\352\360\356\377\354\363\357\377\356\363\357\377\357\363\357\377" - "\356\363\357\377\354\360\355\377\355\361\356\377\354\360\355\377\360\365" - "\361\377\355\361\356\377\355\361\356\377\352\357\355\377\351\356\354\377" - "\353\357\354\377\353\357\354\377\355\361\356\377\352\356\353\377\355\361" - "\356\377\356\363\357\377\355\361\356\377\352\357\355\377\354\360\355\377" - "\354\360\355\377\354\357\354\377\355\360\355\377\354\360\355\377\351\361" - "\357\377\353\357\354\377\354\360\355\377\356\361\356\377\356\361\356\377" - "\355\360\355\377\352\356\353\377\353\357\354\377\354\363\357\377\353\357" - "\354\377\352\357\355\377\354\357\354\377\355\360\355\377\354\357\354\377" - "\356\361\356\377\360\365\360\377\356\363\357\377\355\360\355\377\354\357" - "\354\377\354\360\355\377\353\357\354\377\351\356\354\377\351\356\354\377" - "\351\360\356\377\353\360\356\377\354\363\357\377\354\360\355\377\355\361" - "\356\377\352\356\353\377\354\360\355\377\354\357\354\377\353\356\352\377" - "\354\360\355\377\356\361\356\377\356\361\356\377\360\365\360\377\360\365" - "\361\377\356\365\361\377\356\365\361\377\354\363\357\377\355\361\356\377" - "\354\360\355\377\355\361\356\377\356\360\354\377\355\357\353\377\355\357" - "\353\377\355\357\353\377\353\356\352\377\356\360\354\377\357\361\355\377" - "\355\361\356\377\354\360\355\377\352\356\353\377\353\357\354\377\354\360" - "\355\377\356\361\356\377\356\361\356\377\354\357\354\377\353\356\352\377" - "\353\360\356\377\354\360\355\377\355\361\356\377\355\360\355\377\356\361" - "\356\377\353\356\352\377\345\350\344\377\347\351\345\377\350\353\347\377" - "\352\356\353\377\351\355\352\377\350\354\351\377\351\355\352\377\350\354" - "\351\377\346\354\352\377\352\357\355\377\347\355\353\377\352\357\355\377" - "\353\360\356\377\350\354\351\377\350\354\350\377\353\356\352\377\353\356" - "\352\377\356\361\356\377\354\357\354\377\356\363\357\377\356\363\357\377" - "\360\365\360\377\356\365\361\377\354\363\357\377\355\364\360\377\356\365" - "\361\377\355\364\360\377\353\360\356\377\353\360\356\377\355\360\355\377" - "\357\363\357\377\355\361\356\377\355\364\360\377\354\360\355\377\354\363" - "\357\377\353\357\354\377\351\361\357\377\350\360\355\377\356\363\357\377" - "\353\357\354\377\357\363\357\377\355\361\356\377\355\361\356\377\353\357" - "\354\377\354\360\355\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\346\354\352\377\347\355\353\377\347\355\353\377\353\357\354\377\354\360" - "\355\377\354\360\355\377\355\361\356\377\353\357\354\377\351\356\354\377" - "\352\357\355\377\355\361\356\377\355\360\355\377\354\357\354\377\354\357" - "\354\377\351\357\355\377\352\357\355\377\353\361\356\377\354\360\355\377" - "\354\357\354\377\354\357\354\377\354\357\354\377\352\356\353\377\353\356" - "\352\377\355\360\355\377\353\356\352\377\352\356\353\377\353\356\352\377" - "\354\357\353\377\355\360\355\377\354\357\354\377\355\360\355\377\353\357" - "\354\377\355\364\360\377\354\360\355\377\354\360\355\377\351\356\354\377" - "\351\356\354\377\350\355\353\377\351\356\354\377\350\356\355\377\352\357" - "\355\377\352\357\355\377\354\360\355\377\352\356\353\377\353\356\352\377" - "\354\357\354\377\355\360\355\377\353\357\354\377\355\361\356\377\355\360" - "\355\377\356\361\356\377\355\360\355\377\353\360\356\377\353\360\356\377" - "\353\360\356\377\353\360\356\377\351\356\354\377\347\355\354\377\352\355" - "\351\377\352\355\351\377\353\356\352\377\355\357\353\377\353\356\352\377" - "\354\357\354\377\353\356\352\377\347\355\353\377\354\357\354\377\351\357" - "\355\377\352\356\353\377\350\356\354\377\354\360\355\377\353\356\352\377" - "\355\360\355\377\353\357\354\377\351\356\354\377\351\357\355\377\351\357" - "\355\377\351\357\355\377\353\357\354\377\360\363\356\377\352\353\346\377" - "\336\337\332\377\352\355\351\377\352\356\353\377\351\355\352\377\350\354" - "\350\377\350\354\351\377\350\354\351\377\347\355\353\377\345\354\352\377" - "\346\355\354\377\347\356\355\377\353\360\356\377\353\356\352\377\352\355" - "\351\377\353\356\352\377\355\360\355\377\355\360\355\377\355\360\355\377" - "\355\361\356\377\356\363\357\377\356\363\357\377\355\364\360\377\354\363" - "\357\377\354\363\357\377\352\357\355\377\355\364\360\377\354\363\357\377" - "\354\363\357\377\356\363\357\377\356\363\357\377\353\357\354\377\355\361" - "\356\377\355\361\356\377\354\360\355\377\353\357\354\377\347\356\355\377" - "\351\357\355\377\354\360\355\377\354\360\355\377\353\357\354\377\352\356" - "\353\377\353\357\354\377\354\363\357\377\353\357\354\377\352\356\353\377" - "\354\360\355\377\353\357\354\377\347\355\353\377\347\355\353\377\351\355" - "\352\377\351\355\352\377\352\356\353\377\353\357\354\377\351\355\352\377" - "\351\357\355\377\353\361\356\377\353\360\356\377\354\360\355\377\354\363" - "\357\377\355\361\356\377\353\357\354\377\355\361\356\377\351\361\357\377" - "\352\357\355\377\352\357\355\377\353\356\352\377\350\354\350\377\347\353" - "\347\377\354\357\354\377\354\357\354\377\354\357\354\377\355\360\355\377" - "\353\356\352\377\354\357\354\377\355\360\355\377\354\357\354\377\354\357" - "\354\377\352\357\355\377\352\357\355\377\355\361\356\377\355\360\355\377" - "\353\357\354\377\353\357\354\377\350\355\353\377\350\355\353\377\352\357" - "\355\377\351\356\354\377\352\357\355\377\354\360\355\377\352\356\353\377" - "\352\356\353\377\350\356\354\377\347\355\353\377\351\355\352\377\353\357" - "\354\377\354\360\355\377\355\361\356\377\355\361\356\377\354\360\355\377" - "\354\363\357\377\354\363\357\377\354\363\357\377\354\363\357\377\347\353" - "\350\377\327\336\335\377\326\330\324\377\344\346\342\377\347\351\345\377" - "\354\357\354\377\354\357\354\377\354\357\354\377\353\356\352\377\353\357" - "\354\377\354\357\354\377\353\357\354\377\352\356\353\377\353\360\356\377" - "\352\357\355\377\353\360\356\377\353\357\354\377\352\356\353\377\353\360" - "\356\377\353\357\354\377\352\357\355\377\353\357\354\377\355\361\356\377" - "\365\370\364\377\366\371\365\377\363\366\361\377\350\354\350\377\352\356" - "\353\377\353\357\354\377\352\356\353\377\350\354\351\377\350\354\351\377" - "\351\355\352\377\352\357\355\377\346\355\354\377\351\356\354\377\352\357" - "\355\377\354\357\354\377\355\360\355\377\353\357\354\377\354\357\354\377" - "\355\360\355\377\355\360\355\377\354\360\355\377\355\360\355\377\356\363" - "\357\377\356\365\361\377\355\361\356\377\353\360\356\377\352\357\355\377" - "\355\364\360\377\356\363\357\377\353\361\356\377\354\360\355\377\354\360" - "\355\377\353\357\354\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\355\361\356\377\354\363\357\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\352\357\355\377\353\360\356\377\352\357\355\377" - "\352\356\353\377\352\356\353\377\352\356\353\377\351\356\354\377\351\356" - "\354\377\352\357\355\377\351\357\355\377\351\355\352\377\352\356\353\377" - "\351\355\352\377\351\355\352\377\351\357\355\377\350\356\354\377\351\357" - "\355\377\354\363\357\377\351\357\355\377\352\357\355\377\354\363\357\377" - "\353\361\356\377\352\357\355\377\354\360\355\377\355\364\361\377\361\365" - "\357\377\360\365\360\377\355\360\355\377\356\361\356\377\354\357\354\377" - "\353\356\352\377\355\360\355\377\353\357\354\377\355\360\355\377\356\361" - "\356\377\356\361\356\377\355\361\356\377\354\363\357\377\352\357\355\377" - "\355\361\356\377\355\360\355\377\352\356\353\377\353\360\356\377\351\356" - "\354\377\352\357\355\377\351\356\354\377\352\356\353\377\355\360\355\377" - "\356\361\356\377\354\357\354\377\352\356\353\377\352\356\353\377\350\355" - "\353\377\347\354\352\377\346\354\353\377\351\356\354\377\351\355\352\377" - "\356\361\356\377\354\360\355\377\354\360\355\377\353\360\356\377\355\361" - "\356\377\356\363\357\377\364\370\365\377\366\371\366\377\355\357\353\377" - "\342\345\341\377\347\351\345\377\353\357\354\377\355\360\355\377\354\357" - "\354\377\354\357\354\377\354\357\354\377\353\356\352\377\353\357\354\377" - "\352\356\353\377\350\354\351\377\351\355\352\377\352\355\351\377\352\356" - "\353\377\351\355\352\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\354\357\354\377\361\366\361\377\364\367\363\377\367\372\367\377\361\365" - "\360\377\356\361\356\377\353\357\354\377\353\357\354\377\354\360\355\377" - "\352\356\353\377\345\353\350\377\346\354\352\377\351\356\354\377\351\356" - "\354\377\352\357\355\377\353\360\356\377\350\355\353\377\353\356\352\377" - "\353\357\354\377\353\357\354\377\354\360\355\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\354\360\355\377\356\363\357\377\355\364\360\377" - "\353\360\356\377\350\355\353\377\353\360\356\377\350\360\356\377\351\357" - "\355\377\353\357\354\377\354\360\355\377\353\357\354\377\353\361\356\377" - "\351\357\355\377\354\363\357\377\352\356\353\377\355\361\356\377\353\357" - "\354\377\355\361\356\377\354\360\355\377\352\356\353\377\342\353\352\377" - "\347\355\353\377\351\357\355\377\352\356\353\377\352\357\355\377\353\357" - "\354\377\353\360\356\377\351\357\355\377\351\357\355\377\352\356\353\377" - "\352\356\353\377\352\356\353\377\351\355\352\377\351\355\352\377\350\356" - "\354\377\347\355\353\377\350\356\354\377\353\361\356\377\352\363\360\377" - "\353\360\356\377\352\357\355\377\353\360\356\377\352\357\355\377\352\357" - "\355\377\355\361\356\377\356\361\356\377\354\363\357\377\354\360\355\377" - "\354\360\355\377\355\360\355\377\354\357\354\377\354\357\354\377\351\356" - "\354\377\352\357\355\377\355\360\355\377\356\361\356\377\355\361\356\377" - "\355\361\356\377\353\360\356\377\353\357\354\377\355\361\356\377\352\356" - "\353\377\353\357\354\377\352\356\353\377\352\356\353\377\352\356\353\377" - "\354\357\354\377\355\360\355\377\356\361\356\377\356\361\356\377\354\360" - "\355\377\352\356\353\377\350\356\355\377\347\355\354\377\347\355\354\377" - "\351\356\354\377\351\357\355\377\354\360\355\377\353\357\354\377\354\357" - "\354\377\353\357\354\377\353\357\354\377\356\363\357\377\361\366\363\377" - "\374\376\372\377\364\367\363\377\354\357\353\377\352\355\351\377\353\356" - "\352\377\354\360\355\377\355\360\355\377\354\357\354\377\354\357\354\377" - "\355\360\355\377\354\360\355\377\352\356\353\377\351\355\352\377\351\355" - "\352\377\351\355\352\377\352\356\353\377\351\356\354\377\352\357\355\377" - "\356\361\356\377\356\361\356\377\355\360\355\377\357\363\357\377\357\361" - "\355\377\360\365\360\377\356\361\356\377\360\365\360\377\354\360\355\377" - "\354\360\355\377\357\363\357\377\361\366\361\377\353\360\356\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\351\356\354\377\354\360\355\377" - "\354\360\355\377\354\357\354\377\353\356\352\377\354\357\354\377\353\357" - "\354\377\354\357\354\377\355\360\355\377\353\357\354\377\354\360\355\377" - "\356\363\357\377\353\360\356\377\354\363\357\377\353\360\356\377\352\356" - "\353\377\351\356\354\377\351\356\354\377\354\360\355\377\353\357\354\377" - "\354\360\355\377\352\357\355\377\352\356\353\377\355\361\356\377\355\361" - "\356\377\355\361\356\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\351\357\355\377\344\354\353\377\344\356\354\377\345\357\355\377\347\355" - "\353\377\352\356\353\377\352\356\353\377\353\357\354\377\353\357\354\377" - "\354\357\354\377\351\355\352\377\352\356\353\377\352\356\353\377\353\357" - "\354\377\351\355\352\377\350\354\351\377\351\356\354\377\351\356\354\377" - "\351\361\357\377\353\360\356\377\353\360\356\377\354\360\355\377\352\357" - "\355\377\352\357\355\377\352\357\355\377\355\361\356\377\355\361\356\377" - "\356\363\357\377\354\360\355\377\354\363\357\377\353\357\354\377\356\360" - "\354\377\354\357\354\377\353\360\356\377\353\360\356\377\355\361\356\377" - "\355\361\356\377\354\360\355\377\355\361\356\377\354\363\357\377\353\360" - "\356\377\353\360\356\377\352\356\353\377\353\357\354\377\352\356\353\377" - "\352\356\353\377\353\356\352\377\355\360\355\377\355\360\355\377\355\360" - "\355\377\354\360\355\377\353\360\356\377\347\355\353\377\351\356\354\377" - "\351\355\352\377\352\357\355\377\354\360\355\377\351\357\355\377\353\356" - "\352\377\353\357\354\377\353\357\354\377\355\361\356\377\357\363\357\377" - "\356\363\357\377\361\366\363\377\372\376\372\377\370\372\366\377\355\360" - "\355\377\354\357\353\377\353\356\352\377\353\357\354\377\353\356\352\377" - "\353\356\352\377\353\356\352\377\354\357\354\377\355\360\355\377\355\361" - "\356\377\355\360\355\377\354\357\354\377\354\357\354\377\354\360\355\377" - "\352\356\353\377\351\356\354\377\352\356\353\377\354\360\355\377\355\360" - "\355\377\360\365\361\377\356\363\357\377\360\365\361\377\356\361\356\377" - "\356\361\356\377\356\361\356\377\357\361\355\377\356\361\356\377\357\363" - "\357\377\355\361\356\377\355\361\356\377\355\361\356\377\353\356\352\377" - "\352\356\353\377\354\360\355\377\355\361\356\377\354\357\354\377\352\356" - "\353\377\353\356\352\377\354\357\354\377\355\360\355\377\355\360\355\377" - "\355\361\356\377\354\360\355\377\355\361\356\377\355\364\360\377\354\363" - "\357\377\353\360\356\377\352\356\353\377\346\354\352\377\347\355\353\377" - "\353\356\352\377\353\356\352\377\351\356\354\377\352\357\355\377\352\356" - "\353\377\354\360\355\377\353\357\354\377\354\360\355\377\352\356\353\377" - "\353\361\356\377\353\357\354\377\351\357\355\377\351\357\355\377\352\356" - "\353\377\346\353\351\377\347\354\352\377\352\356\353\377\353\357\354\377" - "\353\357\354\377\354\360\355\377\353\356\352\377\351\355\352\377\353\357" - "\354\377\350\354\351\377\352\355\351\377\353\356\352\377\346\354\351\377" - "\347\355\353\377\351\356\354\377\351\356\354\377\351\356\354\377\352\357" - "\355\377\353\357\354\377\352\357\355\377\353\360\356\377\354\363\357\377" - "\353\357\354\377\353\357\354\377\352\357\355\377\352\357\355\377\351\356" - "\354\377\353\357\354\377\353\356\352\377\354\357\354\377\355\360\355\377" - "\354\357\354\377\354\357\354\377\353\357\354\377\354\357\354\377\355\361" - "\356\377\354\360\355\377\353\361\356\377\351\356\354\377\352\356\353\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\353\357\354\377\355\360" - "\355\377\354\360\355\377\355\361\356\377\353\361\356\377\351\356\354\377" - "\351\356\354\377\352\357\355\377\352\356\353\377\353\357\354\377\353\357" - "\354\377\354\360\355\377\353\357\354\377\354\360\355\377\353\357\354\377" - "\354\360\355\377\355\361\356\377\356\363\357\377\355\364\360\377\357\366" - "\364\377\365\370\364\377\361\366\361\377\361\366\361\377\353\357\354\377" - "\354\360\355\377\353\360\356\377\354\363\357\377\354\357\354\377\352\356" - "\353\377\352\356\353\377\353\357\354\377\354\360\355\377\355\360\355\377" - "\355\360\355\377\354\357\354\377\353\356\352\377\353\360\356\377\352\357" - "\355\377\353\357\354\377\355\361\356\377\360\365\361\377\355\361\356\377" - "\357\363\357\377\354\360\355\377\353\357\354\377\356\361\356\377\356\361" - "\356\377\356\361\356\377\355\360\355\377\355\361\356\377\354\360\355\377" - "\354\360\355\377\352\356\353\377\353\356\352\377\354\363\357\377\356\363" - "\357\377\354\357\354\377\353\357\354\377\353\356\352\377\356\361\356\377" - "\355\360\355\377\356\361\356\377\355\361\356\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\353\360\356\377\352\357\355\377\355\361\356\377" - "\354\360\355\377\355\360\355\377\355\360\355\377\353\356\352\377\351\356" - "\354\377\353\357\354\377\353\356\352\377\354\357\354\377\354\360\355\377" - "\352\356\353\377\352\356\353\377\355\361\356\377\356\361\356\377\354\360" - "\355\377\356\363\357\377\354\360\355\377\346\353\351\377\344\351\347\377" - "\351\355\352\377\352\356\353\377\351\355\352\377\354\360\355\377\352\356" - "\353\377\351\355\352\377\350\354\351\377\351\355\352\377\350\354\350\377" - "\350\354\350\377\352\356\353\377\350\354\351\377\352\357\355\377\351\356" - "\354\377\350\355\353\377\353\360\356\377\352\357\355\377\354\363\357\377" - "\352\357\355\377\353\360\356\377\354\360\355\377\351\356\354\377\352\357" - "\355\377\347\355\353\377\352\356\353\377\352\356\353\377\352\355\351\377" - "\353\356\352\377\354\357\354\377\354\357\354\377\354\357\354\377\353\356" - "\352\377\352\356\353\377\353\357\354\377\353\361\356\377\352\357\355\377" - "\351\356\354\377\353\357\354\377\354\360\355\377\353\357\354\377\352\356" - "\353\377\353\356\352\377\355\360\355\377\353\357\354\377\354\363\357\377" - "\354\360\355\377\352\356\353\377\341\345\342\377\350\354\351\377\351\355" - "\352\377\351\355\352\377\354\360\355\377\354\360\355\377\355\360\355\377" - "\355\361\356\377\352\356\353\377\354\360\355\377\355\361\356\377\354\363" - "\357\377\354\363\357\377\360\365\361\377\356\363\357\377\353\361\356\377" - "\350\360\356\377\352\356\353\377\353\360\356\377\352\357\355\377\352\357" - "\355\377\351\356\354\377\352\356\353\377\352\356\353\377\353\357\354\377" - "\352\356\353\377\355\360\355\377\355\360\355\377\354\357\354\377\352\356" - "\353\377\351\356\354\377\347\357\355\377\353\357\354\377\353\357\354\377" - "\354\360\355\377\354\360\355\377\354\357\354\377\353\360\356\377\353\360" - "\356\377\352\357\355\377\354\357\354\377\355\360\355\377\354\360\355\377" - "\352\356\353\377\353\357\354\377\353\357\354\377\354\360\355\377\353\356" - "\352\377\354\360\355\377\356\361\356\377\356\361\356\377\357\361\355\377" - "\355\360\355\377\354\357\354\377\354\360\355\377\354\360\355\377\354\360" - "\355\377\355\364\360\377\357\363\357\377\354\363\357\377\354\363\357\377" - "\354\363\357\377\355\361\356\377\355\360\355\377\356\361\356\377\354\360" - "\355\377\355\360\355\377\356\361\356\377\354\357\354\377\354\357\354\377" - "\353\356\352\377\353\357\354\377\354\360\355\377\354\357\354\377\355\360" - "\355\377\354\357\354\377\356\361\356\377\356\361\356\377\360\365\361\377" - "\353\357\354\377\351\355\352\377\351\355\352\377\354\360\355\377\355\360" - "\355\377\351\356\354\377\352\357\355\377\352\357\355\377\354\360\355\377" - "\351\355\352\377\352\356\353\377\352\356\353\377\352\356\353\377\347\353" - "\350\377\352\355\351\377\350\354\351\377\347\355\353\377\347\355\353\377" - "\352\357\355\377\352\357\355\377\353\360\356\377\351\356\354\377\354\360" - "\355\377\354\360\355\377\352\357\355\377\350\356\354\377\354\360\355\377" - "\354\360\355\377\353\357\354\377\354\360\355\377\352\356\353\377\353\356" - "\352\377\351\355\352\377\352\356\353\377\352\356\353\377\352\356\353\377" - "\352\356\353\377\353\357\354\377\352\356\353\377\352\356\353\377\354\360" - "\355\377\355\360\355\377\355\361\356\377\354\357\354\377\354\357\354\377" - "\352\356\353\377\354\360\355\377\363\367\363\377\365\371\366\377\355\364" - "\360\377\353\360\356\377\352\356\353\377\351\355\352\377\354\357\354\377" - "\352\356\353\377\354\360\355\377\354\360\355\377\353\357\354\377\352\356" - "\353\377\353\360\356\377\353\357\354\377\353\357\354\377\351\357\355\377" - "\353\361\356\377\354\363\357\377\351\361\357\377\354\360\355\377\353\357" - "\354\377\352\357\355\377\353\360\356\377\357\363\357\377\357\361\355\377" - "\355\361\356\377\355\361\356\377\353\357\354\377\355\360\355\377\354\357" - "\354\377\352\356\353\377\353\357\354\377\351\356\354\377\353\360\356\377" - "\353\361\356\377\353\361\356\377\353\357\354\377\355\361\356\377\356\361" - "\356\377\353\360\356\377\353\357\354\377\352\357\355\377\352\356\353\377" - "\352\355\351\377\353\356\352\377\352\356\353\377\351\356\354\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\354\363\357\377\354\357\354\377" - "\355\360\355\377\355\360\355\377\353\356\352\377\355\360\355\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\355\360\355\377\353\357\354\377" - "\353\360\356\377\352\357\355\377\354\363\357\377\354\360\355\377\353\357" - "\354\377\355\361\356\377\353\360\356\377\353\357\354\377\355\360\355\377" - "\356\361\356\377\355\360\355\377\355\360\355\377\353\357\354\377\354\357" - "\354\377\355\357\353\377\355\357\353\377\356\361\356\377\355\360\355\377" - "\360\363\356\377\356\363\357\377\356\363\357\377\356\363\357\377\355\361" - "\356\377\354\360\355\377\350\356\354\377\351\356\354\377\353\360\356\377" - "\351\356\354\377\353\357\354\377\352\356\353\377\350\354\351\377\350\356" - "\353\377\347\355\353\377\352\356\353\377\351\356\354\377\351\356\354\377" - "\350\355\353\377\351\356\354\377\351\356\354\377\352\356\353\377\353\360" - "\356\377\351\357\355\377\355\360\355\377\355\360\355\377\354\360\355\377" - "\350\356\354\377\354\363\357\377\355\360\355\377\353\361\356\377\352\356" - "\353\377\352\357\355\377\353\357\354\377\353\356\352\377\352\355\351\377" - "\352\357\355\377\353\357\354\377\354\360\355\377\354\360\355\377\354\357" - "\354\377\354\357\354\377\355\361\356\377\353\356\352\377\356\361\356\377" - "\355\360\355\377\353\356\352\377\347\353\350\377\352\356\353\377\355\361" - "\356\377\354\360\355\377\352\357\355\377\351\356\354\377\351\356\354\377" - "\354\360\355\377\355\360\355\377\355\360\355\377\353\357\354\377\353\357" - "\354\377\352\356\353\377\351\355\352\377\352\357\355\377\352\356\353\377" - "\350\354\351\377\353\357\354\377\352\356\353\377\353\361\356\377\353\361" - "\356\377\354\360\355\377\354\360\355\377\352\357\355\377\354\363\357\377" - "\356\361\356\377\355\360\355\377\352\355\351\377\353\357\354\377\353\357" - "\354\377\355\361\356\377\356\361\356\377\355\360\355\377\353\357\354\377" - "\352\356\353\377\353\357\354\377\350\356\353\377\350\356\354\377\351\356" - "\354\377\351\355\352\377\356\360\354\377\355\357\353\377\353\356\352\377" - "\352\357\355\377\354\360\355\377\356\360\354\377\353\356\352\377\353\356" - "\352\377\351\356\354\377\354\360\355\377\353\357\354\377\354\360\355\377" - "\355\361\356\377\355\360\355\377\356\360\354\377\353\356\352\377\353\356" - "\352\377\355\357\353\377\355\357\353\377\353\356\352\377\355\360\355\377" - "\355\360\355\377\354\363\357\377\351\361\357\377\351\361\357\377\352\357" - "\355\377\353\357\354\377\353\357\354\377\354\363\357\377\354\363\357\377" - "\354\363\357\377\355\360\355\377\355\360\355\377\355\361\356\377\353\360" - "\356\377\354\363\357\377\355\360\355\377\353\356\352\377\355\357\353\377" - "\354\360\355\377\353\357\354\377\356\363\357\377\356\363\357\377\356\363" - "\357\377\356\363\357\377\356\363\357\377\355\361\356\377\352\357\355\377" - "\352\357\355\377\354\360\355\377\352\356\353\377\353\357\354\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\351\355\352\377\350\354\351\377" - "\350\354\350\377\351\355\352\377\351\356\354\377\351\356\354\377\350\356" - "\356\377\352\357\355\377\352\361\357\377\352\356\353\377\355\360\355\377" - "\353\356\352\377\352\355\351\377\352\356\353\377\353\357\354\377\353\357" - "\354\377\352\356\353\377\350\355\353\377\352\357\355\377\354\360\355\377" - "\355\360\355\377\354\360\355\377\347\355\353\377\351\356\354\377\354\360" - "\355\377\355\361\356\377\354\360\355\377\352\356\353\377\354\360\355\377" - "\354\357\354\377\357\361\355\377\355\360\355\377\352\355\351\377\344\350" - "\344\377\353\356\352\377\353\356\352\377\354\357\354\377\350\355\353\377" - "\350\355\353\377\350\355\353\377\351\355\352\377\354\357\354\377\355\360" - "\355\377\354\357\354\377\354\360\355\377\354\357\354\377\352\356\353\377" - "\351\356\354\377\353\357\354\377\351\355\352\377\352\355\351\377\346\354" - "\352\377\347\355\353\377\353\360\356\377\353\357\354\377\354\360\355\377" - "\353\357\354\377\354\363\357\377\356\361\356\377\357\363\357\377\351\355" - "\352\377\351\356\354\377\352\357\355\377\352\357\355\377\355\360\355\377" - "\354\360\355\377\355\361\356\377\355\361\356\377\354\360\355\377\353\361" - "\356\377\347\355\353\377\347\355\353\377\351\355\352\377\354\357\354\377" - "\355\360\355\377\354\360\355\377\354\360\355\377\355\361\356\377\356\361" - "\356\377\355\360\355\377\354\357\354\377\352\357\355\377\352\357\355\377" - "\353\360\356\377\354\363\357\377\355\361\356\377\356\363\357\377\355\361" - "\356\377\356\361\356\377\352\355\351\377\351\354\350\377\353\356\352\377" - "\354\357\353\377\355\360\355\377\354\360\355\377\352\357\355\377\350\360" - "\356\377\350\360\356\377\352\357\355\377\353\360\356\377\356\363\357\377" - "\354\360\355\377\353\360\356\377\353\357\354\377\354\357\354\377\355\360" - "\355\377\350\356\354\377\352\357\355\377\353\360\356\377\356\360\354\377" - "\357\361\355\377\355\360\355\377\352\357\355\377\353\360\356\377\354\360" - "\355\377\356\363\357\377\355\361\356\377\355\361\356\377\355\361\356\377" - "\355\361\356\377\353\360\356\377\351\356\354\377\352\357\355\377\353\356" - "\352\377\352\356\353\377\354\360\355\377\353\357\354\377\352\356\353\377" - "\352\356\353\377\353\357\354\377\347\355\353\377\347\354\352\377\351\356" - "\354\377\350\355\353\377\350\355\353\377\346\355\354\377\353\360\356\377" - "\352\356\353\377\351\355\352\377\353\356\352\377\352\355\351\377\354\357" - "\354\377\353\357\354\377\353\356\352\377\352\356\353\377\352\356\353\377" - "\354\360\355\377\354\360\355\377\353\357\354\377\352\356\353\377\351\356" - "\354\377\351\356\354\377\352\357\355\377\354\360\355\377\355\361\356\377" - "\354\360\355\377\354\357\354\377\355\357\353\377\356\361\356\377\357\361" - "\355\377\352\355\351\377\344\350\344\377\344\350\345\377\352\356\353\377" - "\353\356\352\377\353\357\354\377\354\360\355\377\352\356\353\377\353\357" - "\354\377\353\356\352\377\353\356\352\377\352\355\351\377\353\356\352\377" - "\353\356\352\377\351\356\354\377\352\357\355\377\351\356\354\377\351\356" - "\354\377\354\357\354\377\346\354\352\377\351\355\352\377\347\355\353\377" - "\351\356\354\377\351\356\354\377\354\360\355\377\354\363\357\377\356\361" - "\356\377\357\363\357\377\352\355\351\377\351\356\354\377\352\357\355\377" - "\354\360\355\377\355\361\356\377\355\360\355\377\355\361\356\377\355\361" - "\356\377\354\360\355\377\353\357\354\377\353\357\354\377\351\356\354\377" - "\353\356\352\377\354\357\354\377\351\355\352\377\353\357\354\377\352\356" - "\353\377\353\357\354\377\354\360\355\377\354\360\355\377\354\360\355\377" - "\353\357\354\377\352\357\355\377\352\357\355\377\352\357\355\377\356\361" - "\356\377\355\361\356\377\355\361\356\377\354\357\354\377\353\356\352\377" - "\352\354\347\377\353\356\352\377\353\356\352\377\353\356\352\377\354\357" - "\354\377\352\356\353\377\351\356\354\377\353\361\356\377\353\357\354\377" - "\357\361\355\377\356\361\356\377\356\361\356\377\355\361\356\377\354\360" - "\355\377\354\357\354\377\354\357\354\377\355\361\356\377\351\356\354\377" - "\354\360\355\377\355\361\356\377\355\361\356\377\353\357\354\377\351\357" - "\355\377\352\356\353\377\353\356\352\377\355\360\355\377\354\360\355\377" - "\353\357\354\377\354\360\355\377\355\361\356\377\353\357\354\377\352\356" - "\353\377\353\357\354\377\355\360\355\377\352\356\353\377\347\355\352\377" - "\352\356\353\377\353\357\354\377\352\355\351\377\353\357\354\377\347\355" - "\353\377\350\355\353\377\350\355\353\377\350\355\353\377\352\357\355\377" - "\355\361\356\377\357\363\357\377\354\357\354\377\352\355\351\377\352\355" - "\351\377\352\355\351\377\354\357\354\377\354\357\354\377\355\360\355\377" - "\354\360\355\377\352\356\353\377\354\360\355\377\353\357\354\377\353\357" - "\354\377\351\355\352\377\352\357\355\377\351\356\354\377\351\360\356\377" - "\354\360\355\377\355\361\356\377\355\361\356\377\356\361\356\377\356\361" - "\356\377\360\365\360\377\366\371\366\377\366\372\367\377\363\367\364\377" - "\355\364\360\377\350\360\356\377\352\356\353\377\354\360\355\377\355\360" - "\355\377\353\357\354\377\353\357\354\377\347\353\347\377\353\356\352\377" - "\352\355\351\377\354\357\354\377\354\357\354\377\351\356\354\377\350\355" - "\353\377\351\356\354\377\347\355\353\377\354\357\354\377\352\356\353\377" - "\347\355\353\377\346\354\352\377\352\357\355\377\353\360\356\377\351\355" - "\352\377\356\363\357\377\356\361\356\377\360\365\361\377\357\363\357\377" - "\353\357\354\377\352\357\355\377\352\357\355\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\353\357\354\377\354\360\355\377" - "\350\356\354\377\351\355\352\377\352\355\351\377\352\355\351\377\353\356" - "\352\377\352\356\353\377\353\357\354\377\351\355\352\377\350\355\353\377" - "\347\355\353\377\350\354\351\377\346\354\352\377\351\356\354\377\347\355" - "\353\377\353\357\354\377\356\361\356\377\355\361\356\377\356\363\357\377" - "\357\363\357\377\355\360\354\377\355\360\355\377\356\360\354\377\353\356" - "\352\377\353\356\352\377\355\360\355\377\354\357\354\377\355\360\355\377" - "\354\360\355\377\354\360\355\377\355\360\355\377\356\361\356\377\356\361" - "\356\377\356\361\356\377\353\357\354\377\354\357\354\377\354\357\354\377" - "\353\357\354\377\354\360\355\377\353\357\354\377\355\360\355\377\352\356" - "\353\377\354\360\355\377\353\357\354\377\355\361\356\377\354\357\354\377" - "\354\357\354\377\354\357\354\377\354\357\354\377\354\360\355\377\354\360" - "\355\377\355\361\357\377\353\357\354\377\352\356\353\377\353\356\352\377" - "\354\360\355\377\353\357\354\377\351\357\355\377\353\356\352\377\352\355" - "\351\377\352\356\353\377\350\354\351\377\351\355\352\377\350\355\353\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\361\366\361\377\357\363" - "\357\377\343\346\342\377\356\360\354\377\352\355\351\377\347\353\350\377" - "\352\356\353\377\354\357\354\377\354\357\354\377\353\356\353\377\353\360" - "\356\377\353\360\356\377\350\354\351\377\352\355\351\377\347\353\350\377" - "\352\356\353\377\352\356\353\377\355\360\355\377\353\357\354\377\354\357" - "\354\377\353\357\354\377\354\360\355\377\355\361\356\377\361\366\363\377" - "\360\367\365\377\357\366\364\377\354\364\361\377\351\356\354\377\350\355" - "\353\377\354\360\355\377\354\360\355\377\355\361\356\377\353\357\354\377" - "\351\356\354\377\353\356\352\377\354\357\354\377\351\355\352\377\353\360" - "\356\377\351\356\354\377\350\355\353\377\347\355\353\377\353\357\354\377" - "\353\357\354\377\353\357\354\377\350\356\354\377\351\356\354\377\347\355" - "\353\377\352\357\355\377\352\356\353\377\356\361\356\377\355\360\355\377" - "\357\363\357\377\355\360\355\377\355\361\356\377\354\360\355\377\353\360" - "\356\377\352\356\353\377\354\360\355\377\354\360\355\377\353\360\356\377" - "\354\360\355\377\353\357\354\377\346\354\352\377\351\356\354\377\352\356" - "\353\377\352\355\351\377\352\355\351\377\352\356\353\377\352\356\353\377" - "\351\355\352\377\351\356\354\377\347\355\353\377\347\355\353\377\346\354" - "\352\377\346\354\352\377\344\351\347\377\352\355\351\377\356\360\354\377" - "\354\360\355\377\356\361\356\377\360\363\356\377\360\363\356\377\364\367" - "\363\377\365\370\364\377\360\363\356\377\357\363\357\377\357\361\355\377" - "\352\355\351\377\351\354\350\377\356\360\354\377\353\357\354\377\353\357" - "\354\377\355\360\355\377\356\361\356\377\356\361\356\377\357\363\357\377" - "\356\361\356\377\354\360\355\377\355\357\353\377\354\357\354\377\354\360" - "\355\377\354\360\355\377\355\361\356\377\354\360\355\377\354\357\354\377" - "\355\361\356\377\365\370\364\377\360\365\360\377\357\363\357\377\356\360" - "\354\377\355\360\355\377\354\357\354\377\354\360\355\377\353\360\356\377" - "\353\360\356\377\353\361\356\377\353\357\354\377\354\360\355\377\352\355" - "\351\377\346\351\346\377\350\354\350\377\352\356\353\377\352\356\353\377" - "\352\357\355\377\351\356\354\377\353\357\354\377\354\360\355\377\355\361" - "\356\377\365\371\366\377\373\376\373\377\372\376\372\377\360\365\360\377" - "\353\357\354\377\351\355\352\377\353\360\356\377\352\356\353\377\353\356" - "\352\377\353\357\354\377\352\357\355\377\355\364\360\377\361\366\363\377" - "\361\366\361\377\356\361\356\377\354\357\354\377\354\357\354\377\355\360" - "\355\377\354\360\355\377\352\355\351\377\351\355\352\377\352\356\353\377" - "\353\357\354\377\353\357\354\377\353\360\356\377\352\361\357\377\347\356" - "\355\377\352\357\355\377\352\357\355\377\353\357\354\377\353\357\354\377" - "\355\361\356\377\353\357\354\377\350\354\351\377\346\351\346\377\351\354" - "\350\377\351\355\352\377\352\357\355\377\347\355\353\377\352\356\353\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\352\356\353\377\353\356" - "\352\377\352\357\355\377\347\355\353\377\351\356\354\377\354\360\355\377" - "\354\357\354\377\353\357\354\377\356\361\356\377\354\357\354\377\355\360" - "\355\377\355\361\356\377\355\361\356\377\356\361\356\377\354\357\354\377" - "\353\356\352\377\354\357\354\377\352\355\351\377\350\354\350\377\352\356" - "\353\377\351\355\352\377\352\356\353\377\352\356\353\377\350\354\350\377" - "\350\354\351\377\350\354\351\377\350\354\351\377\353\357\354\377\353\357" - "\354\377\352\357\355\377\351\356\354\377\350\355\353\377\342\346\343\377" - "\347\353\347\377\354\357\354\377\352\357\355\377\353\360\356\377\357\363" - "\357\377\357\363\357\377\356\361\356\377\354\357\354\377\352\357\355\377" - "\356\361\356\377\361\366\361\377\360\365\360\377\354\357\354\377\353\357" - "\354\377\353\357\354\377\353\357\354\377\354\357\354\377\354\357\354\377" - "\354\357\354\377\356\361\356\377\356\363\357\377\360\365\360\377\356\360" - "\354\377\355\357\353\377\353\357\354\377\354\360\355\377\354\360\355\377" - "\355\361\356\377\354\360\355\377\354\360\355\377\360\363\356\377\364\367" - "\363\377\364\367\363\377\360\365\360\377\360\365\360\377\355\361\356\377" - "\353\360\356\377\352\357\355\377\351\356\354\377\352\356\353\377\355\361" - "\356\377\361\366\363\377\355\360\355\377\343\346\343\377\342\345\341\377" - "\350\354\350\377\353\356\352\377\354\357\354\377\352\357\355\377\354\360" - "\355\377\355\361\356\377\355\361\356\377\360\365\361\377\356\365\361\377" - "\363\370\366\377\360\365\361\377\360\365\361\377\353\357\354\377\354\360" - "\355\377\352\356\353\377\353\357\354\377\353\356\352\377\354\360\355\377" - "\355\361\356\377\360\365\361\377\361\366\361\377\361\365\360\377\356\360" - "\354\377\354\357\353\377\355\360\355\377\355\360\355\377\356\361\356\377" - "\356\361\356\377\354\360\355\377\354\360\355\377\355\360\355\377\356\363" - "\357\377\354\363\357\377\352\357\355\377\354\363\357\377\353\357\354\377" - "\355\361\357\377\353\357\354\377\361\366\363\377\364\371\367\377\367\373" - "\370\377\357\363\357\377\353\356\352\377\354\357\354\377\352\356\353\377" - "\350\356\354\377\351\357\355\377\353\357\354\377\353\357\354\377\353\357" - "\354\377\353\357\354\377\354\357\354\377\353\356\352\377\351\355\352\377" - "\353\357\354\377\352\356\353\377\353\357\354\377\353\357\354\377\354\357" - "\354\377\353\357\354\377\353\356\352\377\355\361\356\377\355\360\355\377" - "\355\360\355\377\355\360\355\377\353\356\352\377\356\360\354\377\354\357" - "\353\377\352\355\351\377\354\357\354\377\352\357\355\377\351\356\354\377" - "\354\360\355\377\350\354\350\377\344\346\342\377\346\351\346\377\347\353" - "\350\377\352\356\353\377\355\361\356\377\354\360\355\377\352\357\355\377" - "\360\365\361\377\356\363\357\377\354\357\354\377\352\356\353\377\353\360" - "\356\377\352\357\355\377\356\360\354\377\357\363\357\377\354\357\354\377" - "\351\355\352\377\353\360\356\377\355\361\356\377\361\366\361\377\360\365" - "\360\377\355\361\356\377\355\361\356\377\353\357\354\377\355\360\355\377" - "\353\356\352\377\353\357\354\377\353\357\354\377\356\363\357\377\355\361" - "\356\377\357\363\357\377\361\365\360\377\353\360\356\377\352\357\355\377" - "\352\357\355\377\353\360\356\377\353\360\356\377\354\360\355\377\355\360" - "\355\377\360\365\360\377\360\365\360\377\360\365\360\377\357\363\357\377" - "\356\363\357\377\357\363\357\377\355\361\356\377\353\360\356\377\350\356" - "\355\377\350\355\353\377\354\360\355\377\365\370\364\377\371\374\371\377" - "\350\354\350\377\350\353\347\377\350\353\347\377\350\354\350\377\353\357" - "\354\377\353\360\356\377\352\357\355\377\354\360\355\377\354\360\355\377" - "\355\361\356\377\355\364\360\377\355\364\360\377\360\365\361\377\356\363" - "\357\377\355\361\356\377\355\364\360\377\354\360\355\377\352\356\353\377" - "\353\356\352\377\352\355\351\377\354\357\354\377\360\365\360\377\357\363" - "\357\377\361\365\360\377\360\361\355\377\356\360\354\377\355\360\355\377" - "\357\363\357\377\357\361\355\377\357\361\355\377\355\361\356\377\356\361" - "\356\377\355\360\355\377\354\357\354\377\353\357\354\377\355\360\355\377" - "\356\360\354\377\355\357\353\377\354\360\355\377\353\360\356\377\356\363" - "\357\377\356\363\357\377\360\365\360\377\356\363\357\377\356\361\356\377" - "\354\357\354\377\352\356\353\377\350\356\354\377\352\356\353\377\353\357" - "\354\377\354\357\354\377\353\356\352\377\354\360\355\377\353\357\354\377" - "\355\360\355\377\352\355\351\377\354\357\354\377\352\356\353\377\351\356" - "\354\377\352\357\355\377\353\357\354\377\353\357\354\377\354\357\354\377" - "\354\357\354\377\353\356\352\377\354\357\353\377\353\356\352\377\355\360" - "\355\377\357\361\355\377\357\361\355\377\357\361\355\377\354\360\355\377" - "\353\360\356\377\353\360\356\377\357\363\357\377\352\354\347\377\344\346" - "\342\377\343\346\343\377\346\351\346\377\350\354\350\377\353\356\352\377" - "\354\360\355\377\354\360\355\377\355\361\356\377\354\360\355\377\354\357" - "\354\377\353\357\354\377\354\360\355\377\355\361\356\377\357\363\357\377" - "\356\361\356\377\353\357\354\377\351\355\352\377\351\355\352\377\354\357" - "\354\377\361\366\363\377\361\366\361\377\360\365\361\377\355\361\356\377" - "\353\357\354\377\354\357\354\377\354\357\354\377\353\356\352\377\352\356" - "\353\377\354\360\355\377\355\361\356\377\356\363\357\377\356\363\357\377" - "\354\363\357\377\353\360\356\377\352\357\355\377\353\360\356\377\353\361" - "\356\377\354\360\355\377\356\361\356\377\356\361\356\377\360\363\356\377" - "\360\365\360\377\355\361\356\377\356\363\357\377\354\360\355\377\355\361" - "\356\377\353\360\356\377\353\357\354\377\351\356\354\377\354\360\355\377" - "\365\370\364\377\367\372\367\377\355\360\355\377\350\354\350\377\342\345" - "\341\377\350\354\350\377\351\355\352\377\353\360\356\377\353\360\356\377" - "\356\361\356\377\360\365\361\377\355\364\360\377\355\364\360\377\360\365" - "\361\377\356\361\356\377\356\363\357\377\357\363\357\377\353\357\354\377" - "\353\356\352\377\355\357\353\377\353\356\352\377\353\356\352\377\355\360" - "\355\377\356\361\356\377\363\366\361\377\360\363\356\377\360\361\355\377" - "\356\360\354\377\355\360\355\377\356\363\357\377\357\363\357\377\356\361" - "\356\377\354\357\354\377\355\360\355\377\354\357\354\377\355\360\355\377" - "\354\357\354\377\355\360\355\377\356\360\354\377\354\360\355\377\353\360" - "\356\377\353\360\356\377\355\364\360\377\357\366\364\377\356\363\357\377" - "\360\365\361\377\354\357\354\377\355\357\353\377\354\357\354\377\350\354" - "\351\377\351\355\352\377\352\356\353\377\352\356\353\377\352\356\353\377" - "\352\357\355\377\353\357\354\377\354\357\354\377\355\357\353\377\354\357" - "\354\377\355\361\356\377\352\356\353\377\353\357\354\377\352\356\353\377" - "\352\356\353\377\356\361\356\377\354\357\354\377\355\360\355\377\354\357" - "\354\377\355\360\355\377\355\360\355\377\356\361\356\377\357\363\357\377" - "\355\360\355\377\353\357\354\377\351\357\355\377\354\360\355\377\357\363" - "\357\377\353\356\352\377\345\350\344\377\350\353\347\377\350\353\347\377" - "\351\354\350\377\355\360\355\377\353\357\354\377\355\361\356\377\356\361" - "\356\377\356\361\356\377\355\360\355\377\353\357\354\377\354\357\354\377" - "\354\357\354\377\355\357\353\377\356\361\356\377\355\360\355\377\353\356" - "\352\377\351\355\352\377\353\357\354\377\353\360\356\377\355\361\356\377" - "\354\360\355\377\353\357\354\377\354\360\355\377\357\361\355\377\356\361" - "\356\377\356\361\356\377\354\360\355\377\353\357\354\377\353\357\354\377" - "\354\363\357\377\354\363\357\377\353\360\356\377\354\363\357\377\352\356" - "\353\377\354\360\355\377\353\357\354\377\355\360\355\377\357\361\355\377" - "\356\361\356\377\356\361\356\377\356\363\357\377\355\364\360\377\354\360" - "\355\377\356\363\357\377\353\357\354\377\354\360\355\377\354\357\354\377" - "\352\357\355\377\356\361\356\377\360\365\360\377\364\367\363\377\364\367" - "\363\377\356\360\354\377\353\356\352\377\350\353\347\377\351\355\352\377" - "\353\357\354\377\353\357\354\377\355\360\355\377\354\357\354\377\351\357" - "\355\377\351\357\355\377\353\357\354\377\356\361\356\377\356\361\356\377" - "\357\363\357\377\356\363\357\377\357\361\355\377\354\356\351\377\353\356" - "\352\377\353\356\352\377\354\357\354\377\354\357\354\377\355\360\355\377" - "\356\363\357\377\357\363\357\377\357\363\357\377\354\360\355\377\355\361" - "\356\377\354\360\355\377\355\361\356\377\356\361\356\377\355\360\355\377" - "\354\357\354\377\352\356\353\377\352\356\353\377\352\356\353\377\354\360" - "\355\377\352\357\355\377\350\356\355\377\350\360\356\377\351\355\352\377" - "\351\361\357\377\350\356\354\377\353\360\356\377\356\361\356\377\356\360" - "\354\377\360\363\356\377\350\354\350\377\347\353\347\377\350\354\350\377" - "\352\356\353\377\352\356\353\377\350\355\353\377\347\355\353\377\355\360" - "\355\377\355\360\355\377\353\356\352\377\355\360\355\377\354\360\355\377" - "\354\360\355\377\355\360\355\377\353\357\354\377\353\356\352\377\353\356" - "\352\377\355\360\355\377\355\360\355\377\355\360\355\377\356\361\356\377" - "\356\361\356\377\356\361\356\377\357\363\357\377\356\361\356\377\354\363" - "\357\377\355\361\356\377\364\367\363\377\365\370\364\377\360\363\356\377" - "\355\357\353\377\353\356\352\377\352\355\351\377\354\357\354\377\353\356" - "\352\377\353\356\353\377\354\356\351\377\356\360\354\377\354\356\351\377" - "\352\356\353\377\355\360\355\377\354\360\355\377\356\363\357\377\356\363" - "\357\377\356\361\356\377\354\357\354\377\352\355\351\377\352\356\353\377" - "\347\355\353\377\354\363\357\377\354\363\357\377\351\356\354\377\352\356" - "\353\377\355\360\355\377\355\360\355\377\354\357\354\377\354\360\355\377" - "\354\357\354\377\355\361\356\377\354\363\357\377\352\357\355\377\353\357" - "\354\377\353\357\354\377\355\360\355\377\353\357\354\377\355\357\353\377" - "\354\357\354\377\353\356\352\377\355\360\355\377\356\361\356\377\353\361" - "\356\377\354\363\357\377\354\360\355\377\355\361\356\377\355\361\356\377" - "\353\357\354\377\354\360\355\377\354\363\357\377\355\361\356\377\360\365" - "\361\377\356\361\356\377\361\366\361\377\361\365\360\377\357\361\355\377" - "\350\354\350\377\352\356\353\377\352\356\353\377\353\357\354\377\355\360" - "\355\377\353\357\354\377\352\357\355\377\353\360\356\377\354\360\355\377" - "\355\360\355\377\356\361\356\377\355\360\355\377\355\360\355\377\357\363" - "\357\377\353\357\355\377\351\356\354\377\353\356\352\377\355\360\355\377" - "\354\357\354\377\354\360\355\377\354\360\355\377\356\363\357\377\354\360" - "\355\377\355\361\356\377\352\357\355\377\353\360\356\377\354\360\355\377" - "\355\360\355\377\354\357\354\377\354\357\354\377\353\357\354\377\352\356" - "\353\377\354\360\355\377\351\356\354\377\350\356\355\377\351\360\356\377" - "\352\361\357\377\352\361\357\377\352\357\355\377\350\354\351\377\353\360" - "\356\377\354\360\355\377\356\361\356\377\360\365\360\377\356\360\354\377" - "\347\353\347\377\351\354\350\377\352\355\351\377\353\356\352\377\351\355" - "\352\377\352\356\353\377\352\355\351\377\353\356\352\377\355\360\355\377" - "\353\357\354\377\353\357\354\377\352\356\353\377\355\360\355\377\355\361" - "\356\377\355\361\356\377\355\360\355\377\355\360\355\377\355\357\353\377" - "\354\357\354\377\352\356\353\377\353\357\354\377\355\360\355\377\355\360" - "\355\377\355\360\355\377\354\360\355\377\351\354\350\377\352\355\351\377" - "\361\365\360\377\363\366\361\377\360\363\356\377\356\360\354\377\353\356" - "\352\377\356\360\354\377\353\356\352\377\353\356\352\377\354\357\353\377" - "\356\361\356\377\356\361\356\377\354\360\355\377\353\357\354\377\353\357" - "\355\377\353\360\356\377\355\361\356\377\361\366\363\377\360\365\360\377" - "\356\361\356\377\352\356\353\377\352\357\355\377\351\356\354\377\354\360" - "\355\377\354\360\355\377\354\360\355\377\353\356\352\377\355\360\355\377" - "\354\357\354\377\353\356\352\377\354\357\354\377\354\357\354\377\354\360" - "\355\377\352\357\355\377\354\360\355\377\354\357\354\377\355\360\355\377" - "\356\361\356\377\357\361\355\377\356\361\356\377\355\357\353\377\356\361" - "\356\377\355\360\355\377\353\361\356\377\352\357\355\377\350\356\354\377" - "\354\363\357\377\354\360\355\377\354\360\355\377\354\360\355\377\352\357" - "\355\377\353\357\354\377\353\360\356\377\351\357\355\377\356\363\357\377" - "\355\361\357\377\355\361\357\377\354\360\355\377\351\360\356\377\355\361" - "\356\377\353\357\354\377\354\360\355\377\353\357\354\377\353\357\354\377" - "\351\357\355\377\354\360\355\377\354\360\355\377\354\357\354\377\352\357" - "\355\377\352\357\355\377\350\354\350\377\346\353\351\377\347\354\352\377" - "\353\356\352\377\355\360\355\377\355\361\356\377\354\360\355\377\354\360" - "\355\377\354\360\355\377\355\361\356\377\354\360\355\377\353\357\354\377" - "\352\357\355\377\355\361\356\377\354\360\355\377\354\360\355\377\355\360" - "\355\377\355\361\356\377\352\356\353\377\354\360\355\377\351\356\354\377" - "\352\357\355\377\351\356\354\377\354\360\355\377\352\356\353\377\347\355" - "\353\377\351\356\354\377\352\357\355\377\353\360\356\377\355\361\357\377" - "\357\363\357\377\361\365\360\377\363\366\361\377\363\366\361\377\357\363" - "\357\377\355\360\355\377\355\360\355\377\353\357\354\377\354\360\355\377" - "\350\355\353\377\351\356\354\377\352\356\353\377\354\357\354\377\353\357" - "\354\377\352\356\353\377\354\360\355\377\353\357\354\377\354\360\355\377" - "\356\361\356\377\356\360\354\377\353\357\354\377\353\357\354\377\352\357" - "\355\377\354\357\354\377\357\363\357\377\363\367\363\377\364\370\365\377" - "\367\371\365\377\342\343\337\377\302\303\275\377\334\336\330\377\350\353" - "\347\377\354\357\354\377\354\360\355\377\356\361\356\377\356\360\354\377" - "\355\360\354\377\356\361\356\377\355\360\355\377\356\361\356\377\356\363" - "\357\377\351\356\354\377\352\356\354\377\352\357\355\377\351\356\354\377" - "\352\357\355\377\353\357\354\377\353\356\352\377\353\356\352\377\354\360" - "\355\377\347\355\353\377\347\355\353\377\351\356\354\377\352\356\353\377" - "\353\356\352\377\353\356\352\377\351\356\354\377\352\356\353\377\355\360" - "\355\377\356\361\356\377\356\361\356\377\353\357\354\377\353\357\354\377" - "\354\357\354\377\356\361\356\377\356\361\356\377\356\361\355\377\355\360" - "\355\377\356\360\354\377\355\360\355\377\354\357\354\377\353\361\356\377" - "\352\357\355\377\347\356\355\377\347\356\355\377\347\355\353\377\353\357" - "\354\377\354\360\355\377\353\360\356\377\353\357\354\377\351\357\355\377" - "\353\361\356\377\352\357\355\377\354\360\355\377\353\360\356\377\347\355" - "\354\377\346\355\354\377\351\355\352\377\354\360\355\377\355\361\356\377" - "\354\360\355\377\353\357\354\377\354\363\357\377\355\361\356\377\353\357" - "\354\377\355\360\355\377\355\361\357\377\356\365\363\377\364\370\364\377" - "\357\363\357\377\355\360\355\377\355\360\355\377\353\357\354\377\355\361" - "\356\377\353\357\354\377\355\360\355\377\357\363\357\377\355\361\356\377" - "\354\360\355\377\353\357\354\377\354\357\354\377\353\357\354\377\354\357" - "\354\377\352\356\353\377\352\356\353\377\355\361\356\377\353\356\352\377" - "\353\357\354\377\352\356\353\377\351\356\354\377\355\360\355\377\355\360" - "\355\377\354\357\354\377\354\357\354\377\356\361\356\377\352\356\353\377" - "\351\356\354\377\353\357\354\377\360\365\361\377\361\365\360\377\361\366" - "\361\377\356\360\354\377\353\357\354\377\354\357\354\377\354\357\354\377" - "\352\355\351\377\353\357\354\377\350\355\353\377\352\356\354\377\353\357" - "\354\377\352\356\353\377\354\360\355\377\352\356\353\377\354\360\355\377" - "\354\360\355\377\353\357\354\377\352\356\353\377\353\356\352\377\355\361" - "\356\377\352\357\355\377\354\363\357\377\356\361\356\377\354\360\355\377" - "\357\365\363\377\373\377\374\377\377\377\375\377\377\377\377\377\361\363" - "\355\377\355\356\351\377\357\360\353\377\353\356\352\377\353\356\352\377" - "\354\357\354\377\352\356\353\377\351\355\352\377\351\356\354\377\355\360" - "\355\377\354\360\355\377\354\360\355\377\354\360\355\377\353\357\354\377" - "\354\360\355\377\352\356\353\377\346\353\351\377\346\353\351\377\352\356" - "\353\377\352\356\353\377\352\356\353\377\351\356\354\377\351\356\354\377" - "\353\360\356\377\355\360\355\377\355\360\355\377\352\355\351\377\347\356" - "\355\377\353\360\356\377\354\357\354\377\356\361\356\377\356\361\356\377" - "\354\357\354\377\352\356\353\377\352\355\351\377\354\357\354\377\356\361" - "\356\377\355\361\356\377\354\360\355\377\354\357\354\377\355\360\355\377" - "\356\361\356\377\354\360\355\377\352\357\355\377\347\356\355\377\346\354" - "\353\377\347\355\354\377\354\360\355\377\353\357\354\377\352\356\353\377" - "\354\360\355\377\353\357\354\377\351\357\355\377\353\356\352\377\355\360" - "\355\377\355\360\355\377\352\357\355\377\351\356\354\377\353\360\356\377" - "\354\360\355\377\355\361\356\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\355\361\356\377\355\361\356\377\354\360\355\377\355\361\356\377" - "\360\365\361\377\360\365\360\377\361\365\360\377\356\361\356\377\355\360" - "\355\377\352\356\353\377\352\357\355\377\352\356\353\377\353\357\354\377" - "\354\360\355\377\353\360\356\377\355\361\357\377\355\361\357\377\357\363" - "\357\377\357\363\357\377\356\361\356\377\354\363\357\377\353\357\354\377" - "\355\361\356\377\353\357\354\377\355\361\356\377\353\357\354\377\354\357" - "\354\377\355\360\355\377\354\357\354\377\354\357\354\377\356\361\356\377" - "\355\360\355\377\356\361\356\377\356\361\356\377\356\363\357\377\357\363" - "\357\377\356\361\356\377\361\366\361\377\355\361\356\377\352\356\353\377" - "\353\357\354\377\352\355\351\377\350\355\353\377\353\357\354\377\352\356" - "\353\377\353\357\354\377\352\356\353\377\354\360\355\377\354\357\354\377" - "\354\360\355\377\353\357\354\377\354\360\355\377\355\361\356\377\356\360" - "\354\377\356\360\354\377\352\356\353\377\350\356\355\377\353\357\354\377" - "\360\365\361\377\350\355\353\377\351\356\354\377\372\376\373\377\376\377" - "\374\377\377\377\377\377\377\377\376\377\373\374\367\377\363\365\357\377" - "\356\360\354\377\353\356\352\377\354\357\354\377\354\357\354\377\352\356" - "\353\377\347\355\353\377\352\356\353\377\355\361\356\377\355\361\356\377" - "\355\361\356\377\353\357\354\377\355\360\355\377\353\356\352\377\352\356" - "\353\377\351\356\354\377\342\346\343\377\333\337\334\377\345\351\346\377" - "\346\354\351\377\347\355\353\377\351\355\352\377\354\357\354\377\355\360" - "\355\377\352\356\353\377\352\357\355\377\352\363\360\377\353\357\354\377" - "\355\360\355\377\357\363\357\377\356\361\356\377\354\360\355\377\354\357" - "\354\377\354\357\354\377\353\357\354\377\353\357\354\377\353\357\354\377" - "\354\357\354\377\356\361\356\377\354\357\354\377\355\361\356\377\353\357" - "\354\377\351\356\354\377\351\355\352\377\353\357\354\377\352\356\353\377" - "\353\357\354\377\353\357\354\377\354\360\355\377\352\356\353\377\353\357" - "\354\377\354\357\354\377\354\357\353\377\354\357\354\377\352\356\353\377" - "\352\356\353\377\353\357\354\377\354\360\355\377\353\357\354\377\356\361" - "\356\377\354\360\355\377\354\360\355\377\354\360\355\377\356\361\356\377" - "\356\361\356\377\354\363\357\377\354\360\355\377\356\361\356\377\357\363" - "\357\377\354\360\355\377\356\363\357\377\355\361\356\377\354\360\355\377" - "\353\357\354\377\353\357\354\377\353\357\354\377\356\363\357\377\355\361" - "\356\377\355\361\356\377\356\361\356\377\360\363\356\377\357\363\357\377" - "\355\361\356\377\353\357\354\377\354\360\355\377\353\357\354\377\354\360" - "\355\377\356\361\356\377\356\360\354\377\352\355\351\377\352\355\351\377" - "\353\356\352\377\354\357\354\377\355\360\355\377\355\360\355\377\355\360" - "\355\377\353\357\354\377\357\363\357\377\356\361\356\377\356\363\357\377" - "\354\360\355\377\351\355\352\377\350\354\351\377\352\355\351\377\352\355" - "\351\377\353\356\352\377\354\360\355\377\353\357\354\377\354\360\355\377" - "\354\360\355\377\353\356\352\377\353\357\354\377\353\357\354\377\352\356" - "\353\377\353\356\352\377\356\360\354\377\356\360\354\377\356\361\356\377" - "\353\357\354\377\354\360\355\377\354\360\355\377\351\356\354\377\352\356" - "\353\377\365\371\366\377\374\376\372\377\377\377\376\377\376\377\372\377" - "\367\371\365\377\357\361\355\377\354\357\354\377\355\357\353\377\353\356" - "\352\377\353\356\352\377\354\360\355\377\354\357\354\377\353\357\354\377" - "\352\356\353\377\354\363\357\377\353\360\356\377\354\363\357\377\356\361" - "\356\377\354\357\354\377\353\356\352\377\346\351\346\377\334\337\334\377" - "\314\316\312\377\336\340\334\377\350\354\351\377\350\354\351\377\353\357" - "\354\377\354\360\355\377\354\360\355\377\354\360\355\377\351\356\354\377" - "\354\363\357\377\355\361\356\377\354\360\355\377\355\360\355\377\356\363" - "\357\377\356\363\357\377\355\361\356\377\355\360\355\377\357\363\357\377" - "\354\360\355\377\354\360\355\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\355\361\356\377\354\360\355\377\353\357\354\377\352\356\353\377" - "\355\361\356\377\354\360\355\377\355\360\355\377\355\360\355\377\353\357" - "\354\377\354\360\355\377\352\356\353\377\355\360\355\377\353\356\352\377" - "\351\355\352\377\352\356\353\377\353\357\354\377\354\360\355\377\354\360" - "\355\377\353\357\354\377\354\357\354\377\353\357\354\377\354\360\355\377" - "\354\360\355\377\357\363\357\377\356\361\356\377\354\360\355\377\354\360" - "\355\377\360\363\356\377\356\363\357\377\354\360\355\377\356\363\357\377" - "\355\361\356\377\354\360\355\377\353\357\354\377\353\357\354\377\355\361" - "\356\377\356\361\356\377\357\363\357\377\357\363\357\377\360\363\356\377" - "\360\365\360\377\355\360\355\377\353\357\354\377\352\356\353\377\353\357" - "\354\377\353\357\354\377\354\360\355\377\357\361\355\377\356\360\354\377" - "\352\355\351\377\345\350\344\377\353\356\352\377\354\357\354\377\356\361" - "\356\377\356\361\356\377\355\360\355\377\356\361\356\377\356\361\356\377" - "\357\363\357\377\356\361\356\377\355\361\356\377\352\356\353\377\354\360" - "\355\377\351\355\352\377\353\356\352\377\352\355\351\377\354\357\354\377" - "\354\360\355\377\353\357\354\377\354\360\355\377\353\357\354\377\352\356" - "\353\377\353\357\354\377\352\357\355\377\355\360\355\377\354\357\354\377" - "\354\357\354\377\352\356\353\377\354\360\355\377\355\361\356\377\360\365" - "\361\377\356\363\357\377\354\360\355\377\363\367\364\377\371\373\367\377" - "\367\371\365\377\356\360\354\377\360\363\356\377\357\361\355\377\354\357" - "\354\377\353\356\352\377\353\357\354\377\353\356\352\377\354\357\354\377" - "\355\357\353\377\354\357\354\377\352\356\353\377\352\356\353\377", -}; diff --git a/3d-viewer/trackball.cpp b/3d-viewer/trackball.cpp deleted file mode 100644 index b70ef438fe..0000000000 --- a/3d-viewer/trackball.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* - * (c) Copyright 1993, 1994, Silicon Graphics, Inc. - * ALL RIGHTS RESERVED - * Permission to use, copy, modify, and distribute this software for - * any purpose and without fee is hereby granted, provided that the above - * copyright notice appear in all copies and that both the copyright notice - * and this permission notice appear in supporting documentation, and that - * the name of Silicon Graphics, Inc. not be used in advertising - * or publicity pertaining to distribution of the software without specific, - * written prior permission. - * - * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" - * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON - * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, - * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY - * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, - * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF - * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN - * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE - * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. - * - * US Government Users Restricted Rights - * Use, duplication, or disclosure by the Government is subject to - * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph - * (c)(1)(ii) of the Rights in Technical Data and Computer Software - * clause at DFARS 252.227-7013 and/or in similar or successor - * clauses in the FAR or the DOD or NASA FAR Supplement. - * Unpublished-- rights reserved under the copyright laws of the - * United States. Contractor/manufacturer is Silicon Graphics, - * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. - * - * OpenGL(TM) is a trademark of Silicon Graphics, Inc. - */ -/* - * Trackball code: - * - * Implementation of a virtual trackball. - * Implemented by Gavin Bell, lots of ideas from Thant Tessman and - * the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129. - * - * Vector manip code: - * - * Original code from: - * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli - * - * Much mucking with by: - * Gavin Bell - */ -#include -#include // used only to define GLfloat -#include - -/* - * This size should really be based on the distance from the center of - * rotation to the point on the object underneath the mouse. That - * point would then track the mouse as closely as possible. This is a - * simple example, though, so that is left as an Exercise for the - * Programmer. - */ -#define TRACKBALLSIZE (0.8f) - -/* - * Local function prototypes (not defined in trackball.h) - */ -static double tb_project_to_sphere(double, double, double); -static void normalize_quat(double [4]); - -void -vzero(double *v) -{ - v[0] = 0.0; - v[1] = 0.0; - v[2] = 0.0; -} - -void -vset(double *v, double x, double y, double z) -{ - v[0] = x; - v[1] = y; - v[2] = z; -} - -void -vsub(const double *src1, const double *src2, double *dst) -{ - dst[0] = src1[0] - src2[0]; - dst[1] = src1[1] - src2[1]; - dst[2] = src1[2] - src2[2]; -} - -void -vcopy(const double *v1, double *v2) -{ - register int i; - for (i = 0 ; i < 3 ; i++) - v2[i] = v1[i]; -} - -void -vcross(const double *v1, const double *v2, double *cross) -{ - double temp[3]; - - temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]); - temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]); - temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]); - vcopy(temp, cross); -} - -double -vlength(const double *v) -{ - return (double) sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); -} - -void -vscale(double *v, double div) -{ - v[0] *= div; - v[1] *= div; - v[2] *= div; -} - -void -vnormal(double *v) -{ - vscale(v, 1.0f/vlength(v)); -} - -double -vdot(const double *v1, const double *v2) -{ - return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; -} - -void -vadd(const double *src1, const double *src2, double *dst) -{ - dst[0] = src1[0] + src2[0]; - dst[1] = src1[1] + src2[1]; - dst[2] = src1[2] + src2[2]; -} - -/* - * Ok, simulate a track-ball. Project the points onto the virtual - * trackball, then figure out the axis of rotation, which is the cross - * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) - * Note: This is a deformed trackball-- is a trackball in the center, - * but is deformed into a hyperbolic sheet of rotation away from the - * center. This particular function was chosen after trying out - * several variations. - * - * It is assumed that the arguments to this routine are in the range - * (-1.0 ... 1.0) - */ -void -trackball(double q[4], double p1x, double p1y, double p2x, double p2y) -{ - double a[3]; /* Axis of rotation */ - double phi; /* how much to rotate about axis */ - double p1[3], p2[3], d[3]; - double t; - - if (p1x == p2x && p1y == p2y) { - /* Zero rotation */ - vzero(q); - q[3] = 1.0; - return; - } - - /* - * First, figure out z-coordinates for projection of P1 and P2 to - * deformed sphere - */ - vset(p1, p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y)); - vset(p2, p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y)); - - /* - * Now, we want the cross product of P1 and P2 - */ - vcross(p2,p1,a); - - /* - * Figure out how much to rotate around that axis. - */ - vsub(p1, p2, d); - t = vlength(d) / (2.0f*TRACKBALLSIZE); - - /* - * Avoid problems with out-of-control values... - */ - if (t > 1.0) t = 1.0; - if (t < -1.0) t = -1.0; - phi = 2.0f * (double) asin(t); - - axis_to_quat(a,phi,q); -} - -/* - * Given an axis and angle, compute quaternion. - */ -void -axis_to_quat(double a[3], double phi, double q[4]) -{ - vnormal(a); - vcopy(a, q); - vscale(q, (double) sin(phi/2.0)); - q[3] = (double) cos(phi/2.0); -} - -/* - * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet - * if we are away from the center of the sphere. - */ -static double -tb_project_to_sphere(double r, double x, double y) -{ - double d, t, z; - - d = (double) sqrt(x*x + y*y); - if (d < r * 0.70710678118654752440) { /* Inside sphere */ - z = (double) sqrt(r*r - d*d); - } else { /* On hyperbola */ - t = r / 1.41421356237309504880f; - z = t*t / d; - } - return z; -} - -/* - * Given two rotations, e1 and e2, expressed as quaternion rotations, - * figure out the equivalent single rotation and stuff it into dest. - * - * This routine also normalizes the result every RENORMCOUNT times it is - * called, to keep error from creeping in. - * - * NOTE: This routine is written so that q1 or q2 may be the same - * as dest (or each other). - */ - -#define RENORMCOUNT 97 - -void -add_quats(double q1[4], double q2[4], double dest[4]) -{ - static int count=0; - double t1[4], t2[4], t3[4]; - double tf[4]; - - vcopy(q1,t1); - vscale(t1,q2[3]); - - vcopy(q2,t2); - vscale(t2,q1[3]); - - vcross(q2,q1,t3); - vadd(t1,t2,tf); - vadd(t3,tf,tf); - tf[3] = q1[3] * q2[3] - vdot(q1,q2); - - dest[0] = tf[0]; - dest[1] = tf[1]; - dest[2] = tf[2]; - dest[3] = tf[3]; - - if (++count > RENORMCOUNT) { - count = 0; - normalize_quat(dest); - } -} - -/* - * Quaternions always obey: a^2 + b^2 + c^2 + d^2 = 1.0 - * If they don't add up to 1.0, dividing by their magnitued will - * renormalize them. - * - * Note: See the following for more information on quaternions: - * - * - Shoemake, K., Animating rotation with quaternion curves, Computer - * Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985. - * - Pletinckx, D., Quaternion calculus as a basic tool in computer - * graphics, The Visual Computer 5, 2-13, 1989. - */ -static void normalize_quat(double q[4]) -{ - int i; - double mag; - - mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); - for (i = 0; i < 4; i++) q[i] /= mag; -} - -/* - * Build a rotation matrix, given a quaternion rotation. - * - */ -void build_rotmatrix(GLfloat m[4][4], double q[4]) -{ - m[0][0] = 1.0f - 2.0f * (q[1] * q[1] + q[2] * q[2]); - m[0][1] = 2.0f * (q[0] * q[1] - q[2] * q[3]); - m[0][2] = 2.0f * (q[2] * q[0] + q[1] * q[3]); - m[0][3] = 0.0f; - - m[1][0] = 2.0f * (q[0] * q[1] + q[2] * q[3]); - m[1][1]= 1.0f - 2.0f * (q[2] * q[2] + q[0] * q[0]); - m[1][2] = 2.0f * (q[1] * q[2] - q[0] * q[3]); - m[1][3] = 0.0f; - - m[2][0] = 2.0f * (q[2] * q[0] - q[1] * q[3]); - m[2][1] = 2.0f * (q[1] * q[2] + q[0] * q[3]); - m[2][2] = 1.0f - 2.0f * (q[1] * q[1] + q[0] * q[0]); - m[2][3] = 0.0f; - - m[3][0] = 0.0f; - m[3][1] = 0.0f; - m[3][2] = 0.0f; - m[3][3] = 1.0f; -} - diff --git a/3d-viewer/trackball.h b/3d-viewer/trackball.h deleted file mode 100644 index a860dff673..0000000000 --- a/3d-viewer/trackball.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * (c) Copyright 1993, 1994, Silicon Graphics, Inc. - * ALL RIGHTS RESERVED - * Permission to use, copy, modify, and distribute this software for - * any purpose and without fee is hereby granted, provided that the above - * copyright notice appear in all copies and that both the copyright notice - * and this permission notice appear in supporting documentation, and that - * the name of Silicon Graphics, Inc. not be used in advertising - * or publicity pertaining to distribution of the software without specific, - * written prior permission. - * - * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" - * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON - * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, - * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY - * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, - * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF - * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN - * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE - * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. - * - * US Government Users Restricted Rights - * Use, duplication, or disclosure by the Government is subject to - * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph - * (c)(1)(ii) of the Rights in Technical Data and Computer Software - * clause at DFARS 252.227-7013 and/or in similar or successor - * clauses in the FAR or the DOD or NASA FAR Supplement. - * Unpublished-- rights reserved under the copyright laws of the - * United States. Contractor/manufacturer is Silicon Graphics, - * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. - * - * OpenGL(TM) is a trademark of Silicon Graphics, Inc. - */ -/* - * trackball.h - * A virtual trackball implementation - * Written by Gavin Bell for Silicon Graphics, November 1988. - */ - -/* - * Pass the x and y coordinates of the last and current positions of - * the mouse, scaled so they are from (-1.0 ... 1.0). - * - * The resulting rotation is returned as a quaternion rotation in the - * first paramater. - */ -void trackball(double q[4], double p1x, double p1y, double p2x, double p2y); - -/* - * Given two quaternions, add them together to get a third quaternion. - * Adding quaternions to get a compound rotation is analagous to adding - * translations to get a compound translation. When incrementally - * adding rotations, the first argument here should be the new - * rotation, the second and third the total rotation (which will be - * over-written with the resulting new total rotation). - */ -void add_quats(double *q1, double *q2, double *dest); - -/* - * A useful function, builds a rotation matrix in Matrix based on - * given quaternion. - */ -void build_rotmatrix(GLfloat m[4][4], double q[4]); - -/* - * This function computes a quaternion based on an axis (defined by - * the given vector) and an angle about which to rotate. The angle is - * expressed in radians. The result is put into the third argument. - */ -void axis_to_quat(double a[3], double phi, double q[4]); - diff --git a/3d-viewer/vrml_aux.cpp b/3d-viewer/vrml_aux.cpp deleted file mode 100644 index 31bcb0cfb3..0000000000 --- a/3d-viewer/vrml_aux.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file vrml_aux.cpp - * @brief implements auxiliar functions to parse VRML files - */ - -#include "vrml_aux.h" - - -bool GetString( FILE* File, char* aDstString, size_t maxDstLen ) -{ - - if( (!aDstString) || (maxDstLen == 0) ) - return false; - - int c; - - while( ( c = fgetc( File ) ) != EOF ) - { - if( c == '\"' ) - { - break; - } - } - - if( c != '\"' ) - { - return false; - } - - while( (( c = fgetc( File ) ) != EOF) && (maxDstLen > 0) ) - { - if( c == '\"' ) - { - break; - } - - maxDstLen--; - - *aDstString = c; - aDstString++; - - } - - *aDstString = 0; - - if( c == '\"' ) - { - return true; - } - - return false; -} - - -static int SkipGetChar ( FILE* File ); - - -static int SkipGetChar( FILE* File ) -{ - int c; - bool re_parse; - - if( ( c = fgetc( File ) ) == EOF ) - { - // DBG( printf( "EOF\n" ) ); - return EOF; - } - - // DBG( printf( "c %c 0x%02X\n", c, c ) ); - - do - { - re_parse = false; - - if( (c == ' ') || (c == '\t') || (c == '{') || (c == '[') ) - { - // DBG( printf( "Skipping space \\t or { or [\n" ) ); - do - { - if( ( c = fgetc( File ) ) == EOF ) - { - // DBG( printf( "EOF\n" ) ); - - return EOF; - } - } while( (c == ' ') || (c == '\t') || (c == '{') || (c == '[') ); - } - - if( (c == '#') || (c == '\n') || (c == '\r') || (c == 0) || (c == ',') ) - { - if( c == '#' ) - { - // DBG( printf( "Skipping # \\n or \\r or 0, 0x%02X\n", c ) ); - do - { - if( ( c = fgetc( File ) ) == EOF ) - { - // DBG( printf( "EOF\n" ) ); - return EOF; - } - } while( (c != '\n') && (c != '\r') && (c != 0) && (c != ',') ); - } - else - { - if( ( c = fgetc( File ) ) == EOF ) - { - // DBG( printf( "EOF\n" ) ); - return EOF; - } - } - - re_parse = true; - } - } while( re_parse == true ); - - return c; -} - - -bool GetNextTag( FILE* File, char* tag, size_t len ) -{ - int c = SkipGetChar( File ); - - if( c == EOF ) - { - return false; - } - - tag[0] = c; - tag[1] = 0; - - // DBG( printf( "tag[0] %c\n", tag[0] ) ); - if( (c != '}') && (c != ']') ) - { - len--; - char* dst = &tag[1]; - - while( fscanf( File, "%c", dst ) && len > 0 ) - { - if( (*dst == ' ') || (*dst == '[') || (*dst == '{') - || (*dst == '\t') || (*dst == '\n')|| (*dst == '\r') ) - { - *dst = 0; - break; - } - - dst++; - len--; - } - - - // DBG( printf( "tag %s\n", tag ) ); - c = SkipGetChar( File ); - - if( c != EOF ) - { - // Puts again the read char in the buffer - ungetc( c, File ); - } - } - - return true; -} - - -int Read_NotImplemented( FILE* File, char closeChar ) -{ - int c; - - // DBG( printf( "look for %c\n", closeChar) ); - while( ( c = fgetc( File ) ) != EOF ) - { - if( c == '{' ) - { - // DBG( printf( "{\n") ); - Read_NotImplemented( File, '}' ); - } - else if( c == '[' ) - { - // DBG( printf( "[\n") ); - Read_NotImplemented( File, ']' ); - } - else if( c == closeChar ) - { - // DBG( printf( "%c\n", closeChar) ); - return 0; - } - } - - // DBG( printf( " NotImplemented failed\n" ) ); - return -1; -} - - -int ParseVertexList( FILE* File, std::vector& dst_vector ) -{ - // DBG( printf( " ParseVertexList\n" ) ); - - dst_vector.clear(); - - glm::vec3 vertex; - - while( ParseVertex( File, vertex ) ) - { - dst_vector.push_back( vertex ); - } - - return 0; -} - - -bool ParseVertex( FILE* File, glm::vec3& dst_vertex ) -{ - float a, b, c; - int ret = fscanf( File, "%e %e %e", &a, &b, &c ); - - dst_vertex.x = a; - dst_vertex.y = b; - dst_vertex.z = c; - - int s = SkipGetChar( File ); - - if( s != EOF ) - { - // Puts again the read char in the buffer - ungetc( s, File ); - } - - // DBG( printf( "ret%d(%.9f,%.9f,%.9f)", ret, a,b,c) ); - - return ret == 3; -} - - -bool ParseFloat( FILE* aFile, float *aDstFloat, float aDefaultValue ) -{ - float value; - int ret = fscanf( aFile, "%e", &value ); - - if( ret == 1 ) - *aDstFloat = value; - else - *aDstFloat = aDefaultValue; - - return ret == 1; -} diff --git a/3d-viewer/vrml_aux.h b/3d-viewer/vrml_aux.h deleted file mode 100644 index e3c40ccbdb..0000000000 --- a/3d-viewer/vrml_aux.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file vrml_aux.h - * @brief auxiliar functions to parse VRML files - */ - -#ifndef _VRML_AUX_H -#define _VRML_AUX_H - -#include -#include -#include -#include -#define GLM_FORCE_RADIANS -#include -#include -#include -#include -#ifdef __WXMAC__ -# ifdef __DARWIN__ -# include -# else -# include -# endif -#else -# include -#endif -#include - -/** - * Function GetEpoxyThicknessBIU - * skip a VRML block and eventualy internal blocks until it find the close char - * @param File file to read from - * @param closeChar the expected close char of the block - * @return int - -1 if failed, 0 if OK - */ -int Read_NotImplemented( FILE* File, char closeChar); - - -/** - * Function ParseVertexList - * parse a vertex list - * @param File file to read from - * @param dst_vector destination vector list - * @return int - -1 if failed, 0 if OK - */ -int ParseVertexList( FILE* File, std::vector< glm::vec3 > &dst_vector); - - -/** - * Function ParseVertex - * parse a vertex - * @param File file to read from - * @param dst_vertex destination vector - * @return bool - return true if the 3 elements are read - */ -bool ParseVertex( FILE* File, glm::vec3 &dst_vertex ); - - -/** - * Function ParseFloat - * parse a float value - * @param aFile file to read from - * @param aDstFloat destination float - * @param aDefaultValue = the default value, when the actual value cannot be read - * @return bool - Return true if the float was read without error - */ -bool ParseFloat( FILE* aFile, float *aDstFloat, float aDefaultValue ); - -/** - * Function GetNextTag - * parse the next tag - * @param File file to read from - * @param tag destination pointer - * @param len max length of storage - * @return bool - true if succeeded, false if EOF - */ -bool GetNextTag( FILE* File, char* tag, size_t len ); - -/** - * Function GetString - * parse a string, it expects starting by " and end with " - * @param File file to read from - * @param aDstString destination pointer - * @param maxDstLen max length of storage - * @return bool - true if successful read the string, false if failed to get a string - */ -bool GetString( FILE* File, char* aDstString, size_t maxDstLen ); - -#endif diff --git a/3d-viewer/vrml_v1_modelparser.cpp b/3d-viewer/vrml_v1_modelparser.cpp deleted file mode 100644 index 975af17b54..0000000000 --- a/3d-viewer/vrml_v1_modelparser.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014 Mario Luzeiro - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file vrml_v1_modelparser.cpp - */ - -#include -#include -#include -#include -#include - -#include "3d_struct.h" -#include "modelparsers.h" -#include "vrml_aux.h" - -#define BUFLINE_SIZE 32 - - /** - * Trace mask used to enable or disable the trace output of the VRML V1 parser code. - * The debug output can be turned on by setting the WXTRACE environment variable to - * "KI_TRACE_VRML_V1_PARSER". See the wxWidgets documentation on wxLogTrace for - * more information. - */ -static const wxChar* traceVrmlV1Parser = wxT( "KI_TRACE_VRML_V1_PARSER" ); - - -VRML1_MODEL_PARSER::VRML1_MODEL_PARSER( S3D_MODEL_PARSER* aModelParser ) -{ - m_ModelParser = aModelParser; - m_Master = m_ModelParser->GetMaster(); - m_model.reset(); - m_file = NULL; - m_normalPerVertex = true; - colorPerVertex = true; -} - - -VRML1_MODEL_PARSER::~VRML1_MODEL_PARSER() -{ -} - - -bool VRML1_MODEL_PARSER::Load( const wxString& aFilename ) -{ - char text[BUFLINE_SIZE]; - - wxLogTrace( traceVrmlV1Parser, wxT( "Loading: %s" ), GetChars( aFilename ) ); - - m_file = wxFopen( aFilename, wxT( "rt" ) ); - - if( m_file == NULL ) - return false; - - // Switch the locale to standard C (needed to print floating point numbers) - LOCALE_IO toggle; - - m_ModelParser->childs.clear(); - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( ( *text == '}' ) || ( *text == ']' ) ) - { - continue; - } - - if( strcmp( text, "Separator" ) == 0 ) - { - m_model.reset( new S3D_MESH() ); - m_ModelParser->childs.push_back( m_model ); - read_separator(); - } - } - - fclose( m_file ); - - return true; -} - - -int VRML1_MODEL_PARSER::read_separator() -{ - char text[BUFLINE_SIZE]; - - // DBG( printf( "Separator\n" ) ); - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( strcmp( text, "Material" ) == 0 ) - { - readMaterial(); - } - else if( strcmp( text, "Coordinate3" ) == 0 ) - { - readCoordinate3(); - } - else if( strcmp( text, "IndexedFaceSet" ) == 0 ) - { - readIndexedFaceSet(); - } - else if( strcmp( text, "Separator" ) == 0 ) - { - S3D_MESH_PTR parent( m_model.get() ); - - S3D_MESH_PTR new_mesh_model( new S3D_MESH() ); - - m_model->childs.push_back( new_mesh_model ); - - m_model.reset( new_mesh_model.get() ); - - // recursive - read_separator(); - - m_model.reset( parent.get() ); - } - else if( ( *text != '}' ) ) - { - // DBG( printf( "read_NotImplemented %s\n", text ) ); - Read_NotImplemented( m_file, '}' ); - } - else - break; - } - - return 0; -} - - -int VRML1_MODEL_PARSER::readMaterial() -{ - char text[BUFLINE_SIZE]; - S3D_MATERIAL* material = NULL; - - // DBG( printf( " readMaterial\n" ) ); - - wxString mat_name; - - material = new S3D_MATERIAL( m_Master, mat_name ); - - m_Master->Insert( material ); - - m_model->m_Materials = material; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - return 0; - } - - if( strcmp( text, "ambientColor" ) == 0 ) - { - readMaterial_ambientColor(); - } - else if( strcmp( text, "diffuseColor" ) == 0 ) - { - readMaterial_diffuseColor(); - } - else if( strcmp( text, "emissiveColor" ) == 0 ) - { - readMaterial_emissiveColor(); - } - else if( strcmp( text, "specularColor" ) == 0 ) - { - readMaterial_specularColor(); - } - else if( strcmp( text, "shininess" ) == 0 ) - { - readMaterial_shininess(); - } - else if( strcmp( text, "transparency" ) == 0 ) - { - readMaterial_transparency(); - } - } - - wxLogTrace( traceVrmlV1Parser, wxT( " readMaterial failed" ) ); - return -1; -} - - -int VRML1_MODEL_PARSER::readCoordinate3() -{ - char text[BUFLINE_SIZE]; - - // DBG( printf( " readCoordinate3\n" ) ); - - while( GetNextTag( m_file, text, sizeof( text ) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - return 0; - } - - if( strcmp( text, "point" ) == 0 ) - { - readCoordinate3_point(); - } - } - - wxLogTrace( traceVrmlV1Parser, wxT( " readCoordinate3 failed" ) ); - return -1; -} - - -int VRML1_MODEL_PARSER::readIndexedFaceSet() -{ - char text[BUFLINE_SIZE]; - - // DBG( printf( " readIndexedFaceSet\n" ) ); - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - return 0; - } - - if( strcmp( text, "coordIndex" ) == 0 ) - { - readIndexedFaceSet_coordIndex(); - } - else if( strcmp( text, "materialIndex" ) == 0 ) - { - readIndexedFaceSet_materialIndex(); - } - } - - wxLogTrace( traceVrmlV1Parser, wxT( " readIndexedFaceSet failed" ) ); - return -1; -} - - -int VRML1_MODEL_PARSER::readMaterial_ambientColor() -{ - // DBG( printf( " readMaterial_ambientColor\n" ) ); - - return ParseVertexList( m_file, m_model->m_Materials->m_AmbientColor ); -} - - -int VRML1_MODEL_PARSER::readMaterial_diffuseColor() -{ - // DBG( printf( " readMaterial_diffuseColor\n" ) ); - - return ParseVertexList( m_file, m_model->m_Materials->m_DiffuseColor ); -} - - -int VRML1_MODEL_PARSER::readMaterial_emissiveColor() -{ - // DBG( printf( " readMaterial_emissiveColor\n" ) ); - - int ret = ParseVertexList( m_file, m_model->m_Materials->m_EmissiveColor ); - - if( m_Master->m_use_modelfile_emissiveColor == false ) - { - m_model->m_Materials->m_EmissiveColor.clear(); - } - - return ret; -} - - -int VRML1_MODEL_PARSER::readMaterial_specularColor() -{ - // DBG( printf( " readMaterial_specularColor\n" ) ); - - int ret = ParseVertexList( m_file, m_model->m_Materials->m_SpecularColor ); - - if( m_Master->m_use_modelfile_specularColor == false ) - { - m_model->m_Materials->m_SpecularColor.clear(); - } - - return ret; -} - - -int VRML1_MODEL_PARSER::readMaterial_shininess() -{ - // DBG( printf( " readMaterial_shininess\n" ) ); - - m_model->m_Materials->m_Shininess.clear(); - - float shininess_value; - - while( fscanf( m_file, "%f,", &shininess_value ) ) - { - // VRML value is normalized and openGL expects a value 0 - 128 - shininess_value = shininess_value * 128.0f; - m_model->m_Materials->m_Shininess.push_back( shininess_value ); - } - - if( m_Master->m_use_modelfile_shininess == false ) - { - m_model->m_Materials->m_Shininess.clear(); - } - - // DBG( printf( " m_Shininess.size: %ld\n", m_model->m_Materials->m_Shininess.size() ) ); - - return 0; -} - - -int VRML1_MODEL_PARSER::readMaterial_transparency() -{ - // DBG( printf( " readMaterial_transparency\n" ) ); - - m_model->m_Materials->m_Transparency.clear(); - - float tmp; - - while( fscanf( m_file, "%f,", &tmp ) ) - { - m_model->m_Materials->m_Transparency.push_back( tmp ); - } - - if( m_Master->m_use_modelfile_transparency == false ) - { - m_model->m_Materials->m_Transparency.clear(); - } - - // DBG( printf( " m_Transparency.size: %ld\n", m_model->m_Materials->m_Transparency.size() ) ); - - return 0; -} - - -int VRML1_MODEL_PARSER::readCoordinate3_point() -{ - // DBG( printf( " readCoordinate3_point\n" ) ); - - if( ParseVertexList( m_file, m_model->m_Point ) == 0 ) - { - return 0; - } - - wxLogTrace( traceVrmlV1Parser, wxT( " readCoordinate3_point failed" ) ); - return -1; -} - - -int VRML1_MODEL_PARSER::readIndexedFaceSet_coordIndex() -{ - // DBG( printf( " readIndexedFaceSet_coordIndex\n" ) ); - - m_model->m_CoordIndex.clear(); - - glm::ivec3 coord; - - int dummy; // should be -1 - - while( fscanf( m_file, "%d,%d,%d,%d,", &coord[0], &coord[1], &coord[2], &dummy ) ) - { - std::vector coord_list; - - coord_list.resize( 3 ); - coord_list[0] = coord[0]; - coord_list[1] = coord[1]; - coord_list[2] = coord[2]; - - if( (coord[0] == coord[1]) - || (coord[0] == coord[2]) - || (coord[2] == coord[1]) ) - { - wxLogTrace( traceVrmlV1Parser, wxT( " invalid coordIndex at index %zu (%d, %d, %d, %d)" ), - m_model->m_CoordIndex.size() + 1, coord[0], coord[1], coord[2], dummy ); - } - - if( dummy != -1 ) - { - wxLogTrace( traceVrmlV1Parser, wxT( " Error at index %zu, -1 Expected, got %d" ), - m_model->m_CoordIndex.size() + 1, dummy ); - } - - m_model->m_CoordIndex.push_back( coord_list ); - } - - // DBG( printf( " m_CoordIndex.size: %ld\n", m_model->m_CoordIndex.size() ) ); - - return 0; -} - - -int VRML1_MODEL_PARSER::readIndexedFaceSet_materialIndex() -{ - // DBG( printf( " readIndexedFaceSet_materialIndex\n" ) ); - - m_model->m_MaterialIndexPerFace.clear(); - - int index; - - while( fscanf( m_file, "%d,", &index ) ) - { - m_model->m_MaterialIndexPerFace.push_back( index ); - } - - // DBG( printf( " m_MaterialIndexPerFace.size: %ld\n", m_model->m_MaterialIndexPerFace.size() ) ); - - return 0; -} diff --git a/3d-viewer/vrml_v2_modelparser.cpp b/3d-viewer/vrml_v2_modelparser.cpp deleted file mode 100644 index 348a1c46e0..0000000000 --- a/3d-viewer/vrml_v2_modelparser.cpp +++ /dev/null @@ -1,1779 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2014-2015 Mario Luzeiro - * Copyright (C) 2013 Tuomas Vaherkoski - * Copyright (C) 2012 Jean-Pierre Charras, jp.charras@wanadoo.fr - * Copyright (C) 2011 Wayne Stambaugh - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file vrml_v2_modelparser.cpp - */ - -#include -#include -#include -#include -#include - -#include "3d_struct.h" -#include "modelparsers.h" -#include "vrml_aux.h" - -#define BUFLINE_SIZE 1024 - -/** - * Trace mask used to enable or disable the trace output of the VRML V2 parser code. - * The debug output can be turned on by setting the WXTRACE environment variable to - * "KI_TRACE_VRML_V2_PARSER". See the wxWidgets documentation on wxLogTrace for - * more information. - */ -static const wxChar* traceVrmlV2Parser = wxT( "KI_TRACE_VRML_V2_PARSER" ); - - -VRML2_MODEL_PARSER::VRML2_MODEL_PARSER( S3D_MODEL_PARSER* aModelParser ) -{ - m_ModelParser = aModelParser; - m_Master = m_ModelParser->GetMaster(); - m_model.reset(); - m_file = NULL; - m_normalPerVertex = true; - colorPerVertex = true; - m_debugSpacer = ""; - m_counter_DEF_GROUP = 0; - m_counter_USE_GROUP = 0; - m_discardLastGeometry = false; -} - - -VRML2_MODEL_PARSER::~VRML2_MODEL_PARSER() -{ -} - - -void VRML2_MODEL_PARSER::debug_enter() -{ - m_debugSpacer.Append(' '); -} - - -void VRML2_MODEL_PARSER::debug_exit() -{ - m_debugSpacer.RemoveLast(); -} - - -bool VRML2_MODEL_PARSER::Load( const wxString& aFilename ) -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Loading: %s" ), GetChars( aFilename ) ); - debug_enter(); - - m_file = wxFopen( aFilename, wxT( "rt" ) ); - - if( m_file == NULL ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Failed to open file: %s" ), - GetChars( aFilename ) ); - return false; - } - - m_Filename = aFilename; - - // Switch the locale to standard C (needed to print floating point numbers) - LOCALE_IO toggle; - - loadFileModel( S3D_MESH_PTR() ); - - fclose( m_file ); - - debug_exit(); - return true; -} - - -bool VRML2_MODEL_PARSER::Load( const wxString& aFilename, S3D_MESH_PTR aTransformationModel ) -{ - if( aTransformationModel ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Loading: %s" ), - GetChars( aFilename ) ); - debug_enter(); - - m_file = wxFopen( aFilename, wxT( "rt" ) ); - - if( m_file == NULL ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Failed to open file: %s" ), - GetChars( aFilename ) ); - return false; - } - - m_Filename = aFilename; - - // Switch the locale to standard C (needed to print floating point numbers) - LOCALE_IO toggle; - - loadFileModel( aTransformationModel ); - - fclose( m_file ); - - debug_exit(); - return true; - } - - debug_exit(); - return false; -} - - -int VRML2_MODEL_PARSER::loadFileModel( S3D_MESH_PTR aTransformationModel ) -{ - char text[BUFLINE_SIZE]; - - debug_enter(); - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( ( *text == '}' ) || ( *text == ']' ) ) - { - continue; - } - - if( strcmp( text, "Transform" ) == 0 ) - { - m_model.reset( new S3D_MESH() ); - - S3D_MESH_PTR save_ptr( m_model ); - - if( read_Transform() == 0 ) - { - m_model = save_ptr; - - if( ((m_model->m_Point.size() == 0) || (m_model->m_CoordIndex.size() == 0)) && - (m_model->childs.size() == 0) ) - { - m_model.reset(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer - + wxT( "loadFileModel: skipping model with no points or childs" ) ); - } - else - { - if( aTransformationModel.get() != NULL ) - { - m_model->m_translation = aTransformationModel->m_translation; - m_model->m_rotation = aTransformationModel->m_rotation; - m_model->m_scale = aTransformationModel->m_scale; - } - wxLogTrace( traceVrmlV2Parser, m_debugSpacer - + wxT( "loadFileModel: Add model with %zu points, %zu coordIndex, %zu childs." ), - m_model->m_Point.size(), - m_model->m_CoordIndex.size(), - m_model->childs.size() ); - m_ModelParser->childs.push_back( m_model ); - } - } - else - { - m_model.reset(); - } - } - else if( strcmp( text, "DEF" ) == 0 ) - { - m_model.reset( new S3D_MESH() ); - - S3D_MESH_PTR save_ptr( m_model ); - - if( read_DEF() == 0 ) - { - m_model = save_ptr; - - if( ((m_model->m_Point.size() == 0) || (m_model->m_CoordIndex.size() == 0)) && - (m_model->childs.size() == 0) ) - { - m_model.reset(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer - + wxT( "loadFileModel: skipping model with no points or childs" ) ); - } - else - { - if( aTransformationModel.get() != NULL ) - { - m_model->m_translation = aTransformationModel->m_translation; - m_model->m_rotation = aTransformationModel->m_rotation; - m_model->m_scale = aTransformationModel->m_scale; - } - wxLogTrace( traceVrmlV2Parser, m_debugSpacer - + wxT( "loadFileModel: Add model with %zu points, %zu coordIndex, %zu childs." ), - m_model->m_Point.size(), - m_model->m_CoordIndex.size(), - m_model->childs.size() ); - m_ModelParser->childs.push_back( m_model ); - } - } - else - { - m_model.reset(); - } - } - else if( strcmp( text, "Shape" ) == 0 ) - { - m_model.reset( new S3D_MESH() ); - - S3D_MESH_PTR save_ptr = m_model; - - if( read_Shape() == 0 ) - { - m_model = save_ptr; - - if( ((m_model->m_Point.size() == 0) || (m_model->m_CoordIndex.size() == 0)) && - (m_model->childs.size() == 0) ) - { - m_model.reset(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer - + wxT( "loadFileModel: skipping model with no points or childs" ) ); - } - else - { - if( aTransformationModel.get() != NULL ) - { - m_model->m_translation = aTransformationModel->m_translation; - m_model->m_rotation = aTransformationModel->m_rotation; - m_model->m_scale = aTransformationModel->m_scale; - } - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer - + wxT( "loadFileModel: Add model with %zu points, %zu coordIndex, %zu childs." ), - m_model->m_Point.size(), - m_model->m_CoordIndex.size(), - m_model->childs.size() ); - - m_ModelParser->childs.push_back( m_model ); - } - } - else - { - m_model.reset(); - } - } - } - - // There are VRML2 files, as an example, exported by: - // "Generated by TraceParts CAD VRML Translator" that define the group (DEF * GROUP) - // but don't use it in the end, so force it to add the DEF GROUP - - if( ( m_counter_DEF_GROUP > 0 ) && (m_counter_USE_GROUP == 0 ) ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer - + wxT( "loadFileModel: groups defined with DEF * GROUP but not used with USE, forcing add it..." ) ); - - if( !m_defGroupMap.empty() ) - { - for( VRML2_DEF_GROUP_MAP::iterator groupIt = m_defGroupMap.begin(); - groupIt!=m_defGroupMap.end(); ++groupIt ) - { - wxString groupName = groupIt->first; - S3D_MESH_PTR ptrModel( groupIt->second ); - - - if( ((ptrModel->m_Point.size() == 0) || (ptrModel->m_CoordIndex.size() == 0)) && - (ptrModel->childs.size() == 0) ) - { - // Skip this because dont have data to add - continue; - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer - + wxT( "loadFileModel: forced added %s group" ), groupName ); - m_ModelParser->childs.push_back( ptrModel ); - } - } - } - } - - debug_exit(); - return 0; -} - - -int VRML2_MODEL_PARSER::read_Transform() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform" ) ); - - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform exit" ) ); - return 0; - } - - if( strcmp( text, "Transform" ) == 0 ) - { - m_model.reset( new S3D_MESH() ); - - S3D_MESH_PTR save_ptr( m_model ); - - if( read_Transform() == 0 ) - { - m_model = save_ptr; - - if( ((m_model->m_Point.size() == 0) || (m_model->m_CoordIndex.size() == 0)) && - (m_model->childs.size() == 0) ) - { - m_model.reset(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform: skipping model with no points or childs" ) ); - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform: Add child model with %zu points, %zu coordIndex, %zu childs." ), - m_model->m_Point.size(), - m_model->m_CoordIndex.size(), - m_model->childs.size() ); - - m_ModelParser->childs.push_back( m_model ); - } - } - else - { - m_model.reset(); - } - } - else if( strcmp( text, "translation" ) == 0 ) - { - ParseVertex( m_file, m_model->m_translation ); - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "translation (%f,%f,%f)" ), - m_model->m_translation.x, - m_model->m_translation.y, - m_model->m_translation.z ); - } - else if( strcmp( text, "rotation" ) == 0 ) - { - if( fscanf( m_file, "%f %f %f %f", &m_model->m_rotation[0], - &m_model->m_rotation[1], - &m_model->m_rotation[2], - &m_model->m_rotation[3] ) != 4 ) - { - m_model->m_rotation[0] = 0.0f; - m_model->m_rotation[1] = 0.0f; - m_model->m_rotation[2] = 0.0f; - m_model->m_rotation[3] = 0.0f; - - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "rotation failed, setting to zeros" ) ); - } - else - { - m_model->m_rotation[3] = m_model->m_rotation[3] * 180.0f / 3.14f; // !TODO: use constants or functions - } - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "rotation (%f,%f,%f,%f)" ), - m_model->m_rotation[0], - m_model->m_rotation[1], - m_model->m_rotation[2], - m_model->m_rotation[3] ); - } - else if( strcmp( text, "scale" ) == 0 ) - { - ParseVertex( m_file, m_model->m_scale ); - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "scale (%f,%f,%f)" ), - m_model->m_scale.x, m_model->m_scale.y, m_model->m_scale.z ); - } - else if( strcmp( text, "scaleOrientation" ) == 0 ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "scaleOrientation is not implemented, but it will be parsed" ) ); - - glm::vec4 vecDummy; - if( fscanf( m_file, "%f %f %f %f", &vecDummy[0], - &vecDummy[1], - &vecDummy[2], - &vecDummy[3] ) != 4 ) - { - vecDummy[0] = 0.0f; - vecDummy[1] = 0.0f; - vecDummy[2] = 0.0f; - vecDummy[3] = 0.0f; - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "scaleOrientation failed, setting to zeros" ) ); - } - - //wxLogTrace( traceVrmlV2Parser, wxT( " scaleOrientation (%f,%f,%f,%f)" ), - // m_model->m_scaleOrientation[0], - // m_model->m_scaleOrientation[1], - // m_model->m_scaleOrientation[2], - // m_model->m_scaleOrientation[3] ); - } - else if( strcmp( text, "center" ) == 0 ) - { - // this is not used - glm::vec3 vecDummy; - ParseVertex( m_file, vecDummy ); - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "center is not implemented (%f,%f,%f)" ), vecDummy.x, vecDummy.y, vecDummy.z ); - } - else if( strcmp( text, "children" ) == 0 ) - { - // skip - } - else if( strcmp( text, "Switch" ) == 0 ) - { - // skip - } - else if( strcmp( text, "whichChoice" ) == 0 ) - { - int dummy; - - if( fscanf( m_file, "%d", &dummy ) != 1 ) - { - // !TODO: log errors - } - } - else if( strcmp( text, "choice" ) == 0 ) - { - // skip - } - else if( strcmp( text, "Group" ) == 0 ) - { - // Keep looking for things in a transform - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Group" ) ); - read_Transform(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Transform Group exit" ) ); - //return ret; - } - else if( strcmp( text, "Inline" ) == 0 ) - { - read_Inline(); - } - else if( strcmp( text, "Shape" ) == 0 ) - { - // Save the pointer - S3D_MESH_PTR parent( m_model ); - - S3D_MESH_PTR new_mesh_model( new S3D_MESH() ); - - // Assign the current pointer - m_model = new_mesh_model; - - S3D_MESH_PTR save_ptr = m_model; - - if( read_Shape() == 0 ) - { - if( m_discardLastGeometry == false ) - { - m_model = save_ptr; - - if( ((m_model->m_Point.size() == 0) || (m_model->m_CoordIndex.size() == 0)) - && (m_model->childs.size() == 0) ) - { - m_model.reset(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform: Shape, skipping model with no points or childs" ) ); - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform: Shape, Add child model with %zu points, %zu coordIndex, %zu childs." ), - m_model->m_Point.size(), - m_model->m_CoordIndex.size(), - m_model->childs.size() ); - - parent->childs.push_back( m_model ); - } - } - else - { - m_model.reset(); - - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "read_Transform: Shape, discard child." ) ); - } - } - else - { - m_model.reset(); - } - - m_model = parent; - } - else if( strcmp( text, "DEF" ) == 0 ) - { - read_DEF(); - } - else if( strcmp( text, "USE" ) == 0 ) - { - char useLabel[BUFLINE_SIZE]; - - if( GetNextTag( m_file, useLabel, sizeof(useLabel) ) ) - { - // Check if a ',' is at the end and remove it - if( useLabel[strlen(useLabel) - 1] == ',' ) - { - useLabel[strlen(useLabel) - 1] = 0; - } - - std::string strUseLabel = useLabel; - - // Look for it in our group map. - VRML2_DEF_GROUP_MAP::iterator groupIt; - - groupIt = m_defGroupMap.find( strUseLabel ); - - // Checf if not previously defined. - if( groupIt == m_defGroupMap.end() ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "USE: group %s not previously defined " - "in a DEF section." ), strUseLabel ); - return -1; - } - - S3D_MESH_PTR ptrModel( groupIt->second ); - - if( ((ptrModel->m_Point.size() == 0) || (ptrModel->m_CoordIndex.size() == 0)) && - (ptrModel->childs.size() == 0) ) - { - // !TODO: delete in the end - //delete ptrModel; - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform: USE %s, skipping model with no points or childs" ), useLabel ); - } - else - { - - m_counter_USE_GROUP++; - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform: USE %s Add child model with %zu points, %zu coordIndex, %zu childs." ), - useLabel, - ptrModel->m_Point.size(), - ptrModel->m_CoordIndex.size(), - ptrModel->childs.size() ); - - m_model->childs.push_back( ptrModel ); - } - } - else - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "read_Transform: USE Failed to get the label name" ) ); - return -1; - } - } - else - { - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "read_Transform: %s NotImplemented" ), text ); - Read_NotImplemented( m_file, '}' ); - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Transform failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_Inline() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Inline" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - continue; - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Inline exit" ) ); - return 0; - } - - if( strcmp( text, "url" ) == 0 ) - { - if( GetString( m_file, text, sizeof(text) ) ) - { - wxString filename; - filename = filename.FromUTF8( text ); - - #ifdef __WINDOWS__ - filename.Replace( wxT( "/" ), wxT( "\\" ) ); - #else - filename.Replace( wxT( "\\" ), wxT( "/" ) ); - #endif - - bool fileExists = false; - - if( wxFileName::FileExists( filename ) ) - { - fileExists = true; - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "URL Failed to open file as a full path: \"%s\", will try now a relative path..." ), filename ); - - #ifdef __WINDOWS__ - filename = m_Filename.GetPath() + '\\' + filename; - #else - filename = m_Filename.GetPath() + '/' + filename; - #endif - - - if( wxFileName::FileExists( filename ) ) - { - fileExists = true; - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "URL Failed to open file: \"%s\"" ), filename ); - } - } - - if( fileExists ) - { - // Will now create a new parser and set the default - // transfomation model to apply on the root - VRML2_MODEL_PARSER *newParser = new VRML2_MODEL_PARSER( this->m_ModelParser ); - newParser->Load( filename, m_model ); - delete newParser; - } - else - { - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "URL Failed to open file: %s" ), text ); - } - } - else - { - // If fail get url text, exit with failure - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "URL failed read url string" ) ); - break; - } - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Inline failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_DEF_Coordinate() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF_Coordinate" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - // Get the name of the definition. - if( !GetNextTag( m_file, text, sizeof(text) ) ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "read_DEF_Coordinate failed to get next tag" ) ); - return -1; - } - - std::string coordinateName = text; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - continue; - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF_Coordinate exit" ) ); - return 0; - } - - if( strcmp( text, "Coordinate" ) == 0 ) - { - int retVal = read_CoordinateDef(); - - if( retVal == 0 ) - { - m_defCoordinateMap.insert( std::make_pair( coordinateName, m_model->m_Point ) ); - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "read_DEF_Coordinate insert %s" ), coordinateName ); - } - - debug_exit(); - return retVal; - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF_Coordinate failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_DEF() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - char tagName[BUFLINE_SIZE]; - - if( !GetNextTag( m_file, tagName, sizeof(tagName) ) ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "DEF failed GetNextTag first" ) ); - return -1; - } - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "skipping %c" ), *text ); - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF exit" ) ); - return 0; - } - - if( strcmp( text, "Transform" ) == 0 ) - { - int ret = read_Transform(); - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF exit after Transform, please check and validate" ) ); - return ret; - } - else if( strcmp( text, "children" ) == 0 ) - { - // skip - } - else if( strcmp( text, "Switch" ) == 0 ) - { - // skip - } - else if( strcmp( text, "whichChoice" ) == 0 ) - { - // skip - } - else if( strcmp( text, "choice" ) == 0 ) - { - // skip - } - else if( strcmp( text, "Shape" ) == 0 ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Shape" ) ); - - // Save the pointer - S3D_MESH_PTR parent = m_model; - - S3D_MESH_PTR new_mesh_model( new S3D_MESH() ); - - // Assign the current pointer - m_model = new_mesh_model; - - S3D_MESH_PTR save_ptr = m_model; - - if( read_Shape() == 0 ) - { - m_model = save_ptr; - - if( ((m_model->m_Point.size() == 0) || (m_model->m_CoordIndex.size() == 0)) && - (m_model->childs.size() == 0) ) - { - m_model.reset(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF: Shape, skipping model with no points or childs" ) ); - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF: Shape, Add child model with %zu points, %zu coordIndex, %zu childs." ), - m_model->m_Point.size(), - m_model->m_CoordIndex.size(), - m_model->childs.size() ); - - parent->childs.push_back( m_model ); - } - } - else - { - m_model.reset(); - } - - m_model = parent; - } - else if( strcmp( text, "IndexedFaceSet" ) == 0 ) - { - m_discardLastGeometry = false; - read_IndexedFaceSet(); - } - else if( strcmp( text, "Group" ) == 0 ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Group %s" ), tagName ); - - // Save the pointer - S3D_MESH_PTR parent = m_model; - - S3D_MESH_PTR new_mesh_model( new S3D_MESH() ); - - // Assign the current pointer - m_model = new_mesh_model; - - // It will be the same as read a new Transform - if( read_Transform() == 0 ) - { - m_counter_DEF_GROUP++; - - std::string groupName = tagName; - - m_defGroupMap[groupName] = new_mesh_model; - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Group %s: inserted model with %zu points, %zu coordIndex, %zu childs." ), - tagName, - new_mesh_model->m_Point.size(), - new_mesh_model->m_CoordIndex.size(), - new_mesh_model->childs.size() ); - } - else - { - m_model.reset(); - } - - // Restore current model pointer - m_model = parent; - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF %s Group exit" ), tagName ); - return 0; - } - else - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_DEF %s %s NotImplemented, skipping." ), tagName, text ); - Read_NotImplemented( m_file, '}' ); - return 0; - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "DEF failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_IndexedFaceSet_USE() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet_USE" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - // Get the name of the definition. - if( !GetNextTag( m_file, text, sizeof(text) ) ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet_USE failed to get next tag" ) ); - return -1; - } - - std::string coordinateName = text; - - // Look for it in our coordinate map. - VRML2_COORDINATE_MAP::iterator coordinate; - coordinate = m_defCoordinateMap.find( coordinateName ); - - // Not previously defined. - if( coordinate == m_defCoordinateMap.end() ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet_USE: coordinate %s not previously defined " - "in a DEF section." ), text ); - return -1; - } - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet_USE %s" ), text ); - - m_model->m_Point = coordinate->second; - debug_exit(); - return 0; -} - - -int VRML2_MODEL_PARSER::read_Shape() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Shape" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Shape exit" ) ); - return 0; - } - - if( strcmp( text, "appearance" ) == 0 ) - { - read_appearance(); - } - else if( strcmp( text, "geometry" ) == 0 ) - { - read_geometry(); - } - else if( strcmp( text, "IndexedFaceSet" ) == 0 ) - { - m_discardLastGeometry = false; - read_IndexedFaceSet(); - } - else if( strcmp( text, "IndexedLineSet" ) == 0 ) - { - m_discardLastGeometry = true; - read_IndexedLineSet(); - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Shape %s NotImplemented" ), text ); - Read_NotImplemented( m_file, '}' ); - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Shape failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_geometry() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_geometry" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - char tagName[BUFLINE_SIZE]; - tagName[0] = 0; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_geometry exit" ) ); - return 0; - } - - if( strcmp( text, "DEF" ) == 0 ) - { - if( !GetNextTag( m_file, tagName, sizeof(tagName) ) ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "DEF failed GetNextTag first" ) ); - return -1; - } - } - else if( strcmp( text, "IndexedFaceSet" ) == 0 ) - { - m_discardLastGeometry = false; - int ret = read_IndexedFaceSet(); - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_geometry exit, after IndexedFaceSet" ) ); - return ret; - } - else if( strcmp( text, "IndexedLineSet" ) == 0 ) - { - m_discardLastGeometry = true; - int ret = read_IndexedLineSet(); - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_geometry exit, after IndexedLineSet" ) ); - return ret; - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_geometry: %s NotImplemented" ), text ); - int ret = Read_NotImplemented( m_file, '}' ); - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_geometry exit, after %s" ), text); - return ret; - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_geometry failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_appearance() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_appearance" ) ); - debug_enter(); - - S3D_MATERIAL* material = NULL; - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_appearance exit" ) ); - return 0; - } - - if( strcmp( text, "Appearance" ) == 0 ) - { - int ret = read_Appearance(); - debug_exit(); - return ret; - } - else if( strcmp( text, "DEF" ) == 0 ) - { - if( GetNextTag( m_file, text, sizeof(text) ) ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_appearance adding new material %s" ), text ); - - wxString mat_name; - mat_name = FROM_UTF8( text ); - - material = new S3D_MATERIAL( m_Master, mat_name ); - m_Master->Insert( material ); - m_model->m_Materials = material; - - if( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( strcmp( text, "Appearance" ) == 0 ) - { - int ret = read_Appearance(); - debug_exit(); - return ret; - } - } - } - - // Exit loop with error - break; - } - else if( strcmp( text, "USE" ) == 0 ) - { - if( GetNextTag( m_file, text, sizeof(text) ) ) - { - wxString mat_name; - mat_name = FROM_UTF8( text ); - - S3D_MATERIAL* found_material = NULL; - - for( material = m_Master->m_Materials; material; material = material->Next() ) - { - if( material->m_Name == mat_name ) - { - found_material = material; - // We dont exit here, since it seems that VRML can have - // multiple material defined, so, it will copy the latest one that was defined. - } - } - - debug_exit(); - - if( found_material ) - { - // Create a new material instead of assign a pointer because - // the indexfaceset can set color pervertex and that will be stored in the material. - m_model->m_Materials = new S3D_MATERIAL( m_Master, found_material->m_Name ); - m_model->m_Materials->m_AmbientColor = found_material->m_AmbientColor; - m_model->m_Materials->m_DiffuseColor = found_material->m_DiffuseColor; - m_model->m_Materials->m_EmissiveColor = found_material->m_EmissiveColor; - m_model->m_Materials->m_SpecularColor = found_material->m_SpecularColor; - m_model->m_Materials->m_Shininess = found_material->m_Shininess; - m_model->m_Materials->m_Transparency = found_material->m_Transparency; - m_model->m_Materials->m_ColorPerVertex = false; - return 0; - } - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_appearance error: material not found" ) ); - return -1; - } - - // Exit loop with error - break; - } - else - { - // Exit loop with error - break; - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_appearance failed" ) ); - return -1; -} - -int VRML2_MODEL_PARSER::read_Appearance() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Appearance" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Appearance exit" ) ); - return 0; - } - - if( strcmp( text, "material" ) == 0 ) - { - read_material(); - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Appearance failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_material() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_material" ) ); - debug_enter(); - - S3D_MATERIAL* material = NULL; - char text[BUFLINE_SIZE]; - - if( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( strcmp( text, "Material" ) == 0 ) - { - // Check if it is NULL, if not, it is because we come - // from an appearance DEF and already have a pointer - if( m_model->m_Materials == NULL ) - { - wxString mat_name; - material = new S3D_MATERIAL( m_Master, mat_name ); - m_Master->Insert( material ); - m_model->m_Materials = material; - } - - int ret = read_Material(); - debug_exit(); - return ret; - } - else if( strcmp( text, "DEF" ) == 0 ) - { - if( GetNextTag( m_file, text, sizeof(text) ) ) - { - wxString mat_name; - mat_name = FROM_UTF8( text ); - - material = new S3D_MATERIAL( m_Master, mat_name ); - m_Master->Insert( material ); - m_model->m_Materials = material; - - if( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( strcmp( text, "Material" ) == 0 ) - { - int ret = read_Material(); - debug_exit(); - return ret; - } - } - } - } - else if( strcmp( text, "USE" ) == 0 ) - { - if( GetNextTag( m_file, text, sizeof(text) ) ) - { - wxString mat_name; - mat_name = FROM_UTF8( text ); - - S3D_MATERIAL* found_material = NULL; - - for( material = m_Master->m_Materials; material; material = material->Next() ) - { - if( material->m_Name == mat_name ) - { - found_material = material; - // We dont exit here, since it seems that VRML can have - // multiple material defined, so, it will copy the latest one that was defined. - } - } - - debug_exit(); - - if( found_material ) - { - // Create a new material instead of assign a pointer because - // the indexfaceset can set color pervertex and that will be stored in the material. - m_model->m_Materials = new S3D_MATERIAL( m_Master, found_material->m_Name ); - m_model->m_Materials->m_AmbientColor = found_material->m_AmbientColor; - m_model->m_Materials->m_DiffuseColor = found_material->m_DiffuseColor; - m_model->m_Materials->m_EmissiveColor = found_material->m_EmissiveColor; - m_model->m_Materials->m_SpecularColor = found_material->m_SpecularColor; - m_model->m_Materials->m_Shininess = found_material->m_Shininess; - m_model->m_Materials->m_Transparency = found_material->m_Transparency; - m_model->m_Materials->m_ColorPerVertex = false; - return 0; - } - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_material error: material not found" ) ); - return -1; - } - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "failed material" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_Material() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Material" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - glm::vec3 vertex; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Material exit" ) ); - return 0; - } - - if( strcmp( text, "diffuseColor" ) == 0 ) - { - ParseVertex( m_file, vertex ); - if( m_model->m_Materials->m_DiffuseColor.empty() ) - m_model->m_Materials->m_DiffuseColor.push_back( vertex ); - } - else if( strcmp( text, "emissiveColor" ) == 0 ) - { - ParseVertex( m_file, vertex ); - - if( m_Master->m_use_modelfile_emissiveColor == true ) - { - m_model->m_Materials->m_EmissiveColor.push_back( vertex ); - } - } - else if( strcmp( text, "specularColor" ) == 0 ) - { - ParseVertex( m_file, vertex ); - - if( m_Master->m_use_modelfile_specularColor == true ) - { - m_model->m_Materials->m_SpecularColor.push_back( vertex ); - } - } - else if( strcmp( text, "ambientIntensity" ) == 0 ) - { - float ambientIntensity; - ParseFloat( m_file, &ambientIntensity, 0.8 ); - - if( m_Master->m_use_modelfile_ambientIntensity == true ) - { - m_model->m_Materials->m_AmbientColor.push_back( glm::vec3( ambientIntensity, - ambientIntensity, ambientIntensity ) ); - } - } - else if( strcmp( text, "transparency" ) == 0 ) - { - float transparency; - ParseFloat( m_file, &transparency, 0.0 ); - - if( m_Master->m_use_modelfile_transparency == true ) - { - m_model->m_Materials->m_Transparency.push_back( transparency ); - } - } - else if( strcmp( text, "shininess" ) == 0 ) - { - float shininess; - ParseFloat( m_file, &shininess, 1.0 ); - - // VRML value is normalized and openGL expects a value 0 - 128 - if( m_Master->m_use_modelfile_shininess == true ) - { - shininess = shininess * 128.0f; - m_model->m_Materials->m_Shininess.push_back( shininess ); - } - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "Material failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_IndexedFaceSet() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - m_normalPerVertex = false; - colorPerVertex = false; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet exit" ) ); - return 0; - } - - if( strcmp( text, "normalPerVertex" ) == 0 ) - { - if( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( strcmp( text, "TRUE" ) == 0 ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet m_normalPerVertex TRUE" ) ); - m_normalPerVertex = true; - } - } - } - else if( strcmp( text, "colorPerVertex" ) == 0 ) - { - GetNextTag( m_file, text, sizeof(text) ); - - if( strcmp( text, "TRUE" ) == 0 ) - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet colorPerVertex TRUE" ) ); - colorPerVertex = true; - m_model->m_Materials->m_ColorPerVertex = true; - } - else - { - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedFaceSet colorPerVertex FALSE" ) ); - colorPerVertex = false; - } - } - else if( strcmp( text, "Coordinate" ) == 0 ) - { - read_Coordinate(); - } - else if( strcmp( text, "Normal" ) == 0 ) - { - read_Normal(); - } - else if( strcmp( text, "normalIndex" ) == 0 ) - { - read_NormalIndex(); - } - else if( strcmp( text, "Color" ) == 0 ) - { - read_Color(); - } - else if( strcmp( text, "coordIndex" ) == 0 ) - { - read_coordIndex(); - } - else if( strcmp( text, "colorIndex" ) == 0 ) - { - read_colorIndex(); - } - else if( strcmp( text, "USE" ) == 0 ) - { - read_IndexedFaceSet_USE(); - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "IndexedFaceSet failed %s" ), text ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_IndexedLineSet() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedLineSet" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - continue; - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedLineSet exit" ) ); - return 0; - } - - if( strcmp( text, "Coordinate" ) == 0 ) - read_Coordinate(); - else if( strcmp( text, "coordIndex" ) == 0 ) - read_coordIndex(); - else if( strcmp( text, "DEF" ) == 0 ) - read_DEF_Coordinate(); - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_IndexedLineSet failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_colorIndex() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_colorIndex" ) ); - debug_enter(); - - m_model->m_MaterialIndexPerFace.clear(); - m_model->m_MaterialIndexPerVertex.clear(); - - - if( colorPerVertex == true ) - { - int index; - - if( m_model->m_CoordIndex.size() > 0 ) - m_model->m_MaterialIndexPerVertex.reserve( m_model->m_CoordIndex.size() ); - - std::vector materialIndexPerVertex; - materialIndexPerVertex.reserve( 3 ); // Start at least with 3 - - while( fscanf( m_file, "%d, ", &index ) == 1 ) - { - if( index == -1 ) - { - m_model->m_MaterialIndexPerVertex.push_back( materialIndexPerVertex ); - materialIndexPerVertex.clear(); - materialIndexPerVertex.reserve( 3 ); - } - else - { - materialIndexPerVertex.push_back( index ); - } - } - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_colorIndex m_MaterialIndexPerVertex.size: %zu" ), m_model->m_MaterialIndexPerVertex.size() ); - } - else - { - int index; - - if( m_model->m_CoordIndex.size() > 0 ) - m_model->m_MaterialIndexPerFace.reserve( m_model->m_CoordIndex.size() ); - - while( fscanf( m_file, "%d,", &index ) ) - { - m_model->m_MaterialIndexPerFace.push_back( index ); - } - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_colorIndex m_MaterialIndexPerFace.size: %zu" ), m_model->m_MaterialIndexPerFace.size() ); - } - - debug_exit(); - return 0; -} - - -int VRML2_MODEL_PARSER::read_NormalIndex() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_NormalIndex" ) ); - debug_enter(); - - m_model->m_NormalIndex.clear(); - - glm::ivec3 coord; - - int dummy; // should be -1 - - std::vector coord_list; - coord_list.clear(); - - while( fscanf( m_file, "%d, ", &dummy ) == 1 ) - { - if( dummy == -1 ) - { - m_model->m_NormalIndex.push_back( coord_list ); - coord_list.clear(); - } - else - { - coord_list.push_back( dummy ); - } - } - - //wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_NormalIndex m_NormalIndex.size: %u" ), (unsigned int)m_model->m_NormalIndex.size() ); - debug_exit(); - return 0; -} - - -int VRML2_MODEL_PARSER::read_coordIndex() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_coordIndex" ) ); - debug_enter(); - - m_model->m_CoordIndex.clear(); - - glm::ivec3 coord; - - int coordIdx; // should be -1 - - std::vector coord_list; - coord_list.clear(); - - while( fscanf( m_file, "%d, ", &coordIdx ) == 1 ) - { - if( coordIdx == -1 ) - { - m_model->m_CoordIndex.push_back( coord_list ); - coord_list.clear(); - } - else - { - coord_list.push_back( coordIdx ); - } - } - - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_coordIndex m_CoordIndex.size: %zu" ), - m_model->m_CoordIndex.size() ); - debug_exit(); - return 0; -} - - -int VRML2_MODEL_PARSER::read_Color() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Color" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Color exit" ) ); - return 0; - } - - if( strcmp( text, "color" ) == 0 ) - { - if( m_model->m_Materials == NULL ) - { - m_model->m_Materials = new S3D_MATERIAL( m_Master, "" ); - m_Master->Insert( m_model->m_Materials ); - } - - m_model->m_Materials->m_DiffuseColor.clear(); - ParseVertexList( m_file, m_model->m_Materials->m_DiffuseColor ); - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Color failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_Normal() -{ - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Normal" ) ); - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - // Debug - if( m_normalPerVertex == false ) - { - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "read_Normal m_PerFaceNormalsNormalized.size: %zu" ), - m_model->m_PerFaceNormalsNormalized.size() ); - } - else - { - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "read_Normal m_PerVertexNormalsNormalized.size: %zu" ), - m_model->m_PerVertexNormalsNormalized.size() ); - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Normal exit" ) ); - return 0; - } - - if( strcmp( text, "vector" ) == 0 ) - { - if( m_normalPerVertex == false ) - { - ParseVertexList( m_file, m_model->m_PerFaceNormalsNormalized ); - } - else - { - ParseVertexList( m_file, m_model->m_PerVertexNormalsNormalized ); - } - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Normal failed" ) ); - return -1; -} - - -int VRML2_MODEL_PARSER::read_Coordinate() -{ - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - { - continue; - } - - if( *text == '}' ) - { - wxLogTrace( traceVrmlV2Parser, - m_debugSpacer + wxT( "read_Coordinate m_Point.size: %zu" ), - m_model->m_Point.size() ); - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Coordinate exit" ) ); - return 0; - } - - if( strcmp( text, "point" ) == 0 ) - { - ParseVertexList( m_file, m_model->m_Point ); - } - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_Coordinate failed" ) ); - return -1; -} - - -/** - * Read the point of the Coordinate for a DEF - */ -int VRML2_MODEL_PARSER::read_CoordinateDef() -{ - debug_enter(); - - char text[BUFLINE_SIZE]; - - while( GetNextTag( m_file, text, sizeof(text) ) ) - { - if( *text == ']' ) - continue; - - if( *text == '}' ) - { - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_CoordinateDef exit" ) ); - //wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_CoordinateDef m_Point.size: %u" ), (unsigned int)m_model->m_Point.size() ); - return 0; - } - - if( strcmp( text, "point" ) == 0 ) - ParseVertexList( m_file, m_model->m_Point ); - } - - debug_exit(); - wxLogTrace( traceVrmlV2Parser, m_debugSpacer + wxT( "read_CoordinateDef failed" ) ); - return -1; -} diff --git a/3d-viewer/vrmlmodelparser.cpp b/3d-viewer/vrmlmodelparser.cpp deleted file mode 100644 index db86d1e276..0000000000 --- a/3d-viewer/vrmlmodelparser.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2013 Tuomas Vaherkoski - * Copyright (C) 2012 Jean-Pierre Charras, jp.charras@wanadoo.fr - * Copyright (C) 2011 Wayne Stambaugh - * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file vrmlmodelparser.cpp - */ - -#include -#include -#include -#include -#include - -#include "3d_struct.h" -#include "modelparsers.h" - - -VRML_MODEL_PARSER::VRML_MODEL_PARSER( S3D_MASTER* aMaster ) : - S3D_MODEL_PARSER( aMaster ) -{ -} - -VRML_MODEL_PARSER::~VRML_MODEL_PARSER() -{ -} - -bool VRML_MODEL_PARSER::Load( const wxString& aFilename ) -{ - char line[11 + 1]; - FILE* file; - - //DBG( printf( "Load %s", GetChars( aFilename ) ) ); - - file = wxFopen( aFilename, wxT( "rt" ) ); - - if( file == NULL ) - return false; - - if( fgets( line, 11, file ) == NULL ) - { - fclose( file ); - return false; - } - - fclose( file ); - - childs.clear(); - - if( stricmp( line, "#VRML V2.0" ) == 0 ) - { - VRML2_MODEL_PARSER *vrml2_parser = new VRML2_MODEL_PARSER( this ); - vrml2_parser->Load( aFilename ); - delete vrml2_parser; - return true; - } - else if( stricmp( line, "#VRML V1.0" ) == 0 ) - { - VRML1_MODEL_PARSER *vrml1_parser = new VRML1_MODEL_PARSER( this ); - vrml1_parser->Load( aFilename ); - delete vrml1_parser; - return true; - } - - DBG( printf( "Unknown internal VRML file format: %s\n", line ) ); - return false; -} diff --git a/3d-viewer/x3dmodelparser.cpp b/3d-viewer/x3dmodelparser.cpp deleted file mode 100644 index b274553319..0000000000 --- a/3d-viewer/x3dmodelparser.cpp +++ /dev/null @@ -1,577 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2013 Tuomas Vaherkoski - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 2 - * 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, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file x3dmodelparser.cpp - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include <3d_struct.h> -#include -#include - -/** - * Trace mask used to enable or disable the trace output of the X3D parser code. - * The debug output can be turned on by setting the WXTRACE environment variable to - * "KI_TRACE_X3D_PARSER". See the wxWidgets documentation on wxLogTrace for - * more information. - */ -static const wxChar* traceX3DParser = wxT( "KI_TRACE_X3D_PARSER" ); - - -X3D_MODEL_PARSER::X3D_MODEL_PARSER( S3D_MASTER* aMaster ) : - S3D_MODEL_PARSER( aMaster ) -{ - m_model.reset(); -} - - -X3D_MODEL_PARSER::~X3D_MODEL_PARSER() -{ -} - - -bool X3D_MODEL_PARSER::Load( const wxString& aFilename ) -{ - wxLogTrace( traceX3DParser, wxT( "Loading: %s" ), GetChars( aFilename ) ); - - wxXmlDocument doc; - - if( !doc.Load( aFilename ) ) - { - wxLogTrace( traceX3DParser, wxT( "Error while parsing file: %s" ), GetChars( aFilename ) ); - return false; - } - - if( doc.GetRoot()->GetName() != wxT( "X3D" ) ) - { - wxLogTrace( traceX3DParser, wxT( "Filetype is not X3D: %s" ), GetChars( aFilename ) ); - return false; - } - - // Switch the locale to standard C (needed to print floating point numbers) - LOCALE_IO toggle; - - childs.clear(); - - // Shapes are inside of Transform nodes - // Transform node contains information about - // transition, scale and rotation of the shape - NODE_LIST transforms; - GetChildsByName( doc.GetRoot(), wxT( "Transform" ), transforms ); - - for( NODE_LIST::iterator node_it = transforms.begin(); - node_it != transforms.end(); - node_it++ ) - { - m_model.reset( new S3D_MESH() ); - childs.push_back( m_model ); - - wxXmlNode* node = *node_it; - - wxLogTrace( traceX3DParser, wxT( "Transform: %s %s" ), - node->GetAttributes()->GetName(), node->GetAttributes()->GetValue() ); - - readTransform( node ); - - - } - - return true; -} - - -void X3D_MODEL_PARSER::GetChildsByName( wxXmlNode* aParent, - const wxString aName, - std::vector& aResult ) -{ - // (-Breadth-first search (BFS)-) - // **NOTE** This function was changed to get only the first depth of ocorrences - // so it will be an workarround for the Bug #1443431 - std::queue found; - - found.push( aParent ); - - while( !found.empty() ) - { - wxXmlNode* elem = found.front(); - - for( wxXmlNode* child = elem->GetChildren(); - child != NULL; - child = child->GetNext() ) - { - if( child->GetName() == aName ) - aResult.push_back( child ); - else // **NOTE** This function was changed here to get only the first depth of ocorrences - found.push( child ); - } - - found.pop(); - } -} - - -void X3D_MODEL_PARSER::GetNodeProperties( wxXmlNode* aNode, PROPERTY_MAP& aProps ) -{ - wxXmlAttribute* prop; - - for( prop = aNode->GetAttributes(); prop != NULL; prop = prop->GetNext() ) - { - aProps[ prop->GetName() ] = prop->GetValue(); - } -} - - -/* Private ----- */ - -void X3D_MODEL_PARSER::readTransform( wxXmlNode* aTransformNode ) -{ - NODE_LIST childnodes; - - GetChildsByName( aTransformNode, wxT( "Material" ), childnodes ); - - for( NODE_LIST::iterator node = childnodes.begin(); - node != childnodes.end(); node++ ) - { - readMaterial( *node ); - } - - childnodes.clear(); - - PROPERTY_MAP properties; - - GetNodeProperties( aTransformNode, properties ); - - - - GetChildsByName( aTransformNode, wxT( "IndexedFaceSet" ), childnodes ); - - for( NODE_LIST::iterator node = childnodes.begin(); - node != childnodes.end(); node++ ) - { - readIndexedFaceSet( *node, properties ); - } - - childnodes.clear(); -} - - -void X3D_MODEL_PARSER::readMaterial( wxXmlNode* aMatNode ) -{ - glm::vec3 color; - - PROPERTY_MAP properties; - - GetNodeProperties( aMatNode, properties ); - - // DEFine new Material named as value of DEF - if( properties.find( wxT( "DEF" ) ) != properties.end() ) - { - double amb, shine, transp; - - S3D_MATERIAL* material = new S3D_MATERIAL( GetMaster(), properties[ wxT( "DEF" ) ] ); - GetMaster()->Insert( material ); - - m_model->m_Materials = material; - - if( !parseDoubleTriplet( properties[ wxT( "diffuseColor" ) ], color ) ) - { - // DBG( printf( "diffuseColor parsing error" ) ); - } - else - { - m_model->m_Materials->m_DiffuseColor.push_back( color ); - } - - if( !parseDoubleTriplet( properties[ wxT( "specularColor" ) ], color ) ) - { - // DBG( printf( "specularColor parsing error" ) ); - } - else - { - m_model->m_Materials->m_SpecularColor.push_back( color ); - } - - if( !parseDoubleTriplet( properties[ wxT( "emissiveColor" ) ], color ) ) - { - // DBG( printf( "emissiveColor parsing error" ) ); - } - else - { - m_model->m_Materials->m_EmissiveColor.push_back( color ); - } - - wxStringTokenizer values; - values.SetString( properties[ wxT( "ambientIntensity" ) ] ); - - if( values.GetNextToken().ToDouble( &amb ) ) - { - m_model->m_Materials->m_AmbientColor.push_back( glm::vec3( amb, amb, amb ) ); - } - else - { - // DBG( printf( "ambienterror" ) ); - } - - values.SetString( properties[ wxT( "shininess" ) ] ); - - if( values.GetNextToken().ToDouble( &shine ) ) - { - // VRML value is normalized and openGL expects a value 0 - 128 - if( shine > 1.0 ) - { - shine = 1.0; - } else if( shine < 0.0 ) - { - shine = 0.0; - } - shine = shine * 128.0f; - m_model->m_Materials->m_Shininess.push_back( shine ); - } - else - { - // DBG( printf( "shininess error" ) ); - } - - values.SetString( properties[ wxT( "transparency" ) ] ); - - if( values.GetNextToken().ToDouble( &transp ) ) - { - m_model->m_Materials->m_Transparency.push_back( transp ); - } - else - { - // DBG( printf( "trans error" ) ); - } - - // VRML - wxString vrml_material; - PROPERTY_MAP::const_iterator p = ++properties.begin(); // skip DEF - - for( ; p != properties.end(); p++ ) - { - vrml_material.Append( p->first + wxT( " " ) + p->second + wxT( "\n" ) ); - } - - vrml_materials.push_back( vrml_material ); - } - // USE existing material named by value of USE - else if( properties.find( wxT( "USE" ) ) != properties.end() ) - { - S3D_MATERIAL* material = NULL; - wxString mat_name = properties[ wxT( "USE" ) ]; - - for( material = GetMaster()->m_Materials; material; material = material->Next() ) - { - if( material->m_Name == mat_name ) - { - wxString vrml_material; - - vrml_material.Append( wxString::Format( wxT( "specularColor %f %f %f\n" ), - material->m_SpecularColor[0].x, - material->m_SpecularColor[0].y, - material->m_SpecularColor[0].z ) ); - - vrml_material.Append( wxString::Format( wxT( "diffuseColor %f %f %f\n" ), - material->m_DiffuseColor[0].x, - material->m_DiffuseColor[0].y, - material->m_DiffuseColor[0].z ) ); - - vrml_material.Append( wxString::Format( wxT( "emissiveColor %f %f %f\n" ), - material->m_EmissiveColor[0].x, - material->m_EmissiveColor[0].y, - material->m_EmissiveColor[0].z ) ); - - vrml_material.Append( wxString::Format( wxT( "ambientIntensity %f\n" ), - material->m_AmbientColor[0].x ) ); - - vrml_material.Append( wxString::Format( wxT( "shininess %f\n" ), - material->m_Shininess[0] ) ); - - vrml_material.Append( wxString::Format( wxT( "transparency %f\n" ), - material->m_Transparency[0] ) ); - - vrml_materials.push_back( vrml_material ); - - m_model->m_Materials = material; - - return; - } - } - - // DBG( printf( "ReadMaterial error: material not found\n" ) ); - } -} - - -bool X3D_MODEL_PARSER::parseDoubleTriplet( const wxString& aData, - S3D_VERTEX& aResult ) -{ - wxStringTokenizer tokens( aData ); - - double x = 0; - double y = 0; - double z = 0; - - bool ret = tokens.GetNextToken().ToDouble( &x ) - && tokens.GetNextToken().ToDouble( &y ) - && tokens.GetNextToken().ToDouble( &z ); - - aResult.x = x; - aResult.y = y; - aResult.z = z; - - return ret; -} - - -void X3D_MODEL_PARSER::rotate( S3D_VERTEX& aV, - S3D_VERTEX& aU, - double angle ) -{ - S3D_VERTEX rotated; - double C = cos( angle ); - double S = sin( angle ); - double t = 1.0 - C; - - rotated.x = ( t * aU.x * aU.x + C ) * aV.x + - ( t * aU.x * aU.y - S * aU.z ) * aV.y + - ( t * aU.x * aU.z + S * aU.y ) * aV.z; - - rotated.y = ( t * aU.x * aU.y + S * aU.z ) * aV.x + - ( t * aU.y * aU.y + C ) * aV.y + - ( t * aU.y * aU.z - S * aU.x ) * aV.z; - - rotated.z = ( t * aU.x * aU.z - S * aU.y ) * aV.x + - ( t * aU.y * aU.z + S * aU.x ) * aV.y + - ( t * aU.z * aU.z + C) * aV.z; - - aV.x = rotated.x; - aV.y = rotated.y; - aV.z = rotated.z; -} - - -/* Steps: - * 1. Read transform data - * 2. Read vertex triplets - * 3. Read coordinate indexes - * 4. Apply geometry to Master object - */ -void X3D_MODEL_PARSER::readIndexedFaceSet( wxXmlNode* aFaceNode, - PROPERTY_MAP& aTransformProps ) -{ - /* Step 1: Read transform data - * --------------------------- */ - - S3D_VERTEX translation; - - parseDoubleTriplet( aTransformProps[ wxT( "translation" ) ], translation ); - - S3D_VERTEX scale; - parseDoubleTriplet( aTransformProps[ wxT( "scale" ) ], scale ); - - S3D_VERTEX rotation; - double angle = 0.0; - wxStringTokenizer tokens( aTransformProps[ wxT( "rotation" ) ] ); - - double x = 0.0, y = 0.0, z = 0.0; - - if( !( tokens.GetNextToken().ToDouble( &x ) - && tokens.GetNextToken().ToDouble( &y ) - && tokens.GetNextToken().ToDouble( &z ) - && tokens.GetNextToken().ToDouble( &angle ) ) ) - { - // DBG( printf( "rotation read error" ) ); - } - else - { - rotation.x = x; - rotation.y = y; - rotation.z = z; - } - - /* Step 2: Read all coordinate points - * ---------------------------- */ - std::vector points; - NODE_LIST coordinates; - GetChildsByName( aFaceNode, wxT( "Coordinate" ), coordinates ); - - PROPERTY_MAP coordinate_properties; - // IndexedFaceSet has one Coordinate child node - GetNodeProperties( coordinates[0], coordinate_properties ); - - // Save points to vector as doubles - wxStringTokenizer point_tokens( coordinate_properties[ wxT( "point" ) ] ); - double dpoint = 0.0; - - while( point_tokens.HasMoreTokens() ) - { - if( point_tokens.GetNextToken().ToDouble( &dpoint ) ) - { - points.push_back( dpoint ); - } - else - { - wxLogTrace( traceX3DParser, wxT( "Error converting to double" ) ); - } - } - - if( points.size() % 3 != 0 ) - { - // DBG( printf( "Number of points is incorrect" ) ); - return; - } - - /* Create 3D vertex from 3 points and - * apply transforms in order of SCALE, ROTATION, TRANSLATION - */ - wxString vrml_pointlist; - std::vector triplets; - - for( unsigned id = 0; id < points.size() / 3; id++ ) - { - int triplet_indx = id * 3; - S3D_VERTEX vpoint( points[ triplet_indx ], - points[ triplet_indx + 1 ], - points[ triplet_indx + 2 ] ); - - vpoint.x *= scale.x; - vpoint.y *= scale.y; - vpoint.z *= scale.z; - - rotate( vpoint, rotation, angle ); - - vpoint.x += translation.x; - vpoint.y += translation.y; - vpoint.z += translation.z; - - m_model->m_Point.push_back( vpoint ); - - // VRML - vrml_pointlist.Append( wxString::Format( wxT( "%f %f %f\n" ), vpoint.x, vpoint.y, vpoint.z ) ); - } - - vrml_points.push_back( vrml_pointlist ); - - - /* Step 3: Read all color points - * ---------------------------- */ - std::vector color_points; - NODE_LIST color; - GetChildsByName( aFaceNode, wxT( "Color" ), color ); - - // Some models lack color information, need to handle this safely - if( !color.empty() ) - { - PROPERTY_MAP color_properties; - // IndexedFaceSet has one Coordinate child node - GetNodeProperties( color[0], color_properties ); - - // Save points to vector as doubles - wxStringTokenizer colorpoint_tokens( color_properties[ wxT( "color" ) ] ); - double color_point = 0.0; - - while( colorpoint_tokens.HasMoreTokens() ) - { - if( colorpoint_tokens.GetNextToken().ToDouble( &color_point ) ) - { - color_points.push_back( color_point ); - } - else - { - wxLogTrace( traceX3DParser, wxT( "Error converting to double" ) ); - } - } - - if( color_points.size() % 3 != 0 ) - { - // DBG( printf( "Number of points is incorrect" ) ); - return; - } - - /* Create 3D face color from 3 color points - */ - m_model->m_Materials->m_DiffuseColor.clear(); - - for( unsigned id = 0; id < color_points.size() / 3; id++ ) - { - m_model->m_MaterialIndexPerFace.push_back( id ); - - int color_triplet_indx = id * 3; - glm::vec3 colorface( color_points[ color_triplet_indx + 0 ], - color_points[ color_triplet_indx + 1 ], - color_points[ color_triplet_indx + 2 ] ); - - m_model->m_Materials->m_DiffuseColor.push_back( colorface ); - } - } - - - /* -- Read coordinate indexes -- */ - PROPERTY_MAP faceset_properties; - GetNodeProperties( aFaceNode, faceset_properties ); - - wxString coordIndex_str = faceset_properties[ wxT( "coordIndex" ) ]; - wxStringTokenizer index_tokens( coordIndex_str ); - - wxString vrml_coord_indx_list; - - std::vector coord_list; - coord_list.clear(); - - while( index_tokens.HasMoreTokens() ) - { - long index = 0; - - index_tokens.GetNextToken().ToLong( &index ); - - // -1 marks the end of polygon - if( index < 0 ) - { - /* Step 4: Apply geometry to Master object - * --------------------------------------- */ - m_model->m_CoordIndex.push_back( coord_list ); - - coord_list.clear(); - vrml_coord_indx_list.Append( wxT( "-1\n" ) ); - } - else - { - coord_list.push_back( index ); - vrml_coord_indx_list.Append( wxString::Format( wxT( "%ld " ), index ) ); - } - } - - vrml_coord_indexes.push_back( vrml_coord_indx_list ); -} diff --git a/bitmaps_png/CMakeLists.txt b/bitmaps_png/CMakeLists.txt index 7d210ed3ac..3e98564a76 100644 --- a/bitmaps_png/CMakeLists.txt +++ b/bitmaps_png/CMakeLists.txt @@ -178,6 +178,7 @@ set( BMAPS_MID contrast_mode create_cmp_file checked_ok + color_materials component_select_unit component_select_alternate_shape config @@ -411,6 +412,7 @@ set( BMAPS_MID open_library open_project open_document + options_3drender options_all_tracks_and_vias options_all_tracks options_all_vias @@ -479,6 +481,7 @@ set( BMAPS_MID red reload2 reload + render_mode reset_text resize_sheet rescue_pcbnew diff --git a/bitmaps_png/cpp_26/color_materials.cpp b/bitmaps_png/cpp_26/color_materials.cpp new file mode 100644 index 0000000000..f2d3d5d99a --- /dev/null +++ b/bitmaps_png/cpp_26/color_materials.cpp @@ -0,0 +1,69 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4a, 0x4c, + 0xce, 0x00, 0x00, 0x03, 0x3d, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x95, 0x4b, 0x68, 0x14, + 0x59, 0x14, 0x86, 0xcf, 0x7d, 0x55, 0xaa, 0x3b, 0x55, 0x95, 0x44, 0xbb, 0x63, 0x12, 0xe3, 0x33, + 0x59, 0xf8, 0x1c, 0x07, 0x67, 0x44, 0x50, 0x50, 0x17, 0x51, 0x37, 0xae, 0x1b, 0x99, 0xe5, 0x08, + 0x82, 0x2e, 0x44, 0xc5, 0x85, 0xcb, 0x46, 0x51, 0xdc, 0xea, 0x42, 0x5d, 0xba, 0x13, 0x9a, 0x01, + 0x67, 0x31, 0x83, 0xcb, 0x0c, 0x83, 0xe0, 0xc2, 0x57, 0x44, 0x82, 0x64, 0xc6, 0xb7, 0x89, 0xdd, + 0xe9, 0x98, 0xb4, 0xd5, 0xa9, 0xae, 0xea, 0xea, 0xaa, 0x7b, 0xaf, 0xa7, 0x82, 0x82, 0x01, 0x31, + 0xa5, 0x6e, 0x5c, 0xe4, 0xc0, 0xe1, 0x56, 0xd3, 0xdc, 0xff, 0xbb, 0xe7, 0x9c, 0xbf, 0x6e, 0x01, + 0x2c, 0xc6, 0x62, 0x7c, 0x08, 0xf2, 0x4d, 0xbb, 0x8a, 0x45, 0xba, 0x76, 0xd4, 0xb4, 0x43, 0xa3, + 0x69, 0x13, 0xc1, 0x32, 0x2a, 0x36, 0x68, 0xe4, 0x10, 0x77, 0xaa, 0xdb, 0xaf, 0xe2, 0x7f, 0xea, + 0xfb, 0x41, 0x08, 0x18, 0x78, 0x08, 0xb9, 0xc0, 0x68, 0xf5, 0x2b, 0xc3, 0xb4, 0x35, 0x50, 0x47, + 0x72, 0x66, 0x29, 0x83, 0xd9, 0x8a, 0x51, 0x5b, 0x0a, 0x8a, 0x82, 0xf4, 0xdf, 0xfa, 0xc5, 0xe3, + 0x77, 0xbe, 0x19, 0xf4, 0xcb, 0xe1, 0xc3, 0xa2, 0xec, 0x77, 0xaf, 0x8d, 0x63, 0x73, 0xe9, 0xa1, + 0x03, 0xdb, 0x37, 0x3a, 0x19, 0xd1, 0x33, 0x36, 0x39, 0x2d, 0xff, 0x1c, 0x79, 0xe6, 0x69, 0x4e, + 0x6d, 0x25, 0x98, 0xa5, 0x39, 0x38, 0x0a, 0xc1, 0x9a, 0xb1, 0xbb, 0xc1, 0xcf, 0xb9, 0xcb, 0x50, + 0x28, 0xc8, 0x8f, 0xfb, 0x79, 0x2a, 0xca, 0x9e, 0x22, 0x9f, 0xac, 0xd1, 0x75, 0x3b, 0xb7, 0xac, + 0x1a, 0xfc, 0x6d, 0x68, 0xdb, 0xee, 0x15, 0xb9, 0x8e, 0x75, 0xb5, 0x20, 0x8c, 0x6b, 0x32, 0x9e, + 0x50, 0x26, 0x9d, 0x54, 0x14, 0x41, 0x9c, 0xd8, 0x9a, 0x11, 0x04, 0x52, 0x0b, 0xab, 0x2b, 0xf0, + 0xb1, 0x9a, 0x13, 0x03, 0x9c, 0xfb, 0x28, 0x41, 0xd3, 0x70, 0xfa, 0xfb, 0xd8, 0x9a, 0xd5, 0x03, + 0x3d, 0x83, 0x07, 0x87, 0x7e, 0xdd, 0xbf, 0x75, 0xb0, 0x77, 0x08, 0x41, 0x9b, 0xdc, 0x66, 0xd8, + 0x36, 0x52, 0x99, 0x26, 0xca, 0xe0, 0x39, 0xd9, 0x86, 0x89, 0xab, 0x32, 0x44, 0x4e, 0x09, 0x91, + 0x97, 0x06, 0xcb, 0xc5, 0x42, 0x1c, 0x81, 0x0b, 0xd7, 0x76, 0xa5, 0x06, 0x6d, 0x2c, 0x96, 0x8c, + 0x48, 0xb0, 0xf5, 0x3b, 0x36, 0xaf, 0xf9, 0xa9, 0xdd, 0x34, 0x96, 0xbf, 0xf5, 0x9a, 0xcd, 0xe1, + 0xff, 0x5f, 0xfe, 0x77, 0x7d, 0x64, 0xac, 0x7a, 0xeb, 0x55, 0x85, 0x4b, 0x8e, 0x00, 0xc1, 0x30, + 0x69, 0x3e, 0x81, 0xc5, 0x82, 0xe6, 0x14, 0xe7, 0x1d, 0x40, 0x08, 0x0e, 0x4c, 0x1d, 0x4d, 0xdd, + 0xba, 0xb7, 0x93, 0x4f, 0xfb, 0xa3, 0x36, 0xd2, 0xfd, 0xca, 0xad, 0x67, 0x6e, 0x3d, 0x9f, 0x78, + 0x37, 0x13, 0xb4, 0xfc, 0xe1, 0x17, 0x13, 0xc1, 0x0b, 0xd7, 0x13, 0x0a, 0x2b, 0x01, 0xc6, 0x2d, + 0xc9, 0x89, 0xa3, 0x18, 0x9a, 0x42, 0x50, 0x1b, 0xf4, 0x27, 0x73, 0x27, 0x64, 0x07, 0x5c, 0x28, + 0x75, 0xc0, 0xe9, 0x82, 0xbb, 0x20, 0x48, 0x49, 0xd2, 0x97, 0x9c, 0xf4, 0xef, 0xb1, 0x97, 0x52, + 0x3f, 0x19, 0xaf, 0x29, 0x9c, 0x83, 0x44, 0x87, 0x81, 0x40, 0xa7, 0xa1, 0x09, 0x12, 0x23, 0x24, + 0xbf, 0x35, 0xa5, 0x06, 0x42, 0xe6, 0x87, 0xc6, 0x42, 0xb4, 0x3f, 0x88, 0x4f, 0xf7, 0x16, 0x04, + 0x45, 0xc2, 0x58, 0xaa, 0xa8, 0xce, 0x6b, 0x74, 0x93, 0x14, 0x7c, 0x4e, 0x1c, 0x12, 0x71, 0x8a, + 0x0e, 0x43, 0xa7, 0x29, 0xca, 0xac, 0x2f, 0x2b, 0x68, 0x3b, 0x55, 0xeb, 0x70, 0xb0, 0x80, 0xad, + 0xc9, 0xc9, 0xa4, 0x2d, 0x94, 0xcc, 0xd9, 0x58, 0x32, 0x70, 0x34, 0xe7, 0x96, 0x26, 0x44, 0x2c, + 0x00, 0x21, 0x60, 0xe8, 0x5a, 0x3a, 0x50, 0x1b, 0x7f, 0x83, 0xc2, 0xc9, 0xb0, 0xd1, 0xbe, 0x0c, + 0x2b, 0x41, 0x1b, 0x73, 0x9e, 0xfd, 0xa2, 0xb8, 0xc6, 0x94, 0x31, 0x85, 0x28, 0xf4, 0x61, 0x43, + 0xcf, 0xe3, 0x54, 0x20, 0xdf, 0x9c, 0x1a, 0x15, 0xb4, 0x2f, 0x23, 0x69, 0xe2, 0x26, 0xac, 0x8a, + 0x10, 0xf6, 0x99, 0xfb, 0x85, 0x24, 0xc3, 0x04, 0x89, 0x0d, 0x8d, 0x63, 0x02, 0x51, 0x8b, 0x40, + 0xd8, 0xa4, 0xd0, 0x8a, 0xee, 0x43, 0xe1, 0x58, 0x2b, 0xdd, 0x7b, 0x54, 0x2c, 0xfa, 0x91, 0xa0, + 0x7f, 0x60, 0xcb, 0x3a, 0xe7, 0x41, 0x92, 0x9d, 0x5a, 0x51, 0x90, 0x11, 0x83, 0xa0, 0xc9, 0xa1, + 0xd1, 0xe0, 0xe0, 0xd6, 0x38, 0xcc, 0x4c, 0x71, 0xa8, 0x56, 0x38, 0x54, 0x5e, 0x13, 0xf0, 0xfc, + 0xb3, 0x5f, 0x77, 0x33, 0x84, 0xe3, 0x57, 0x21, 0xbb, 0x72, 0x3b, 0x76, 0x65, 0xef, 0x9c, 0x78, + 0x72, 0xfa, 0x16, 0x9e, 0x3e, 0x39, 0x79, 0x92, 0xcd, 0x80, 0x82, 0x1f, 0x10, 0x08, 0x3c, 0x5c, + 0xeb, 0x04, 0x66, 0x67, 0x29, 0x64, 0x33, 0xe7, 0xe1, 0xc6, 0xe5, 0x07, 0x5f, 0x7f, 0xa9, 0x16, + 0x2f, 0x39, 0xc0, 0xcd, 0xab, 0x10, 0x07, 0xfb, 0x20, 0x8c, 0xb0, 0x35, 0x28, 0xde, 0x44, 0x71, + 0xbf, 0x41, 0xa0, 0x81, 0x00, 0x6f, 0x16, 0x2b, 0xa8, 0x51, 0x98, 0xad, 0x6b, 0xe8, 0x5c, 0x72, + 0x05, 0xfe, 0xb9, 0x79, 0xea, 0xbb, 0x3e, 0x13, 0xe6, 0xc9, 0x33, 0xbf, 0x87, 0x0d, 0xef, 0x18, + 0xb8, 0xef, 0x06, 0xb4, 0xe7, 0x22, 0x00, 0x2b, 0xa8, 0xbb, 0x58, 0x85, 0xab, 0xc8, 0x92, 0xfc, + 0x23, 0x33, 0xdf, 0x7b, 0x22, 0xf8, 0xeb, 0xfa, 0xed, 0xd4, 0xb7, 0x77, 0xa9, 0x54, 0x62, 0xe5, + 0x72, 0x79, 0x79, 0xb5, 0x5a, 0xed, 0x0f, 0x82, 0xa0, 0xd7, 0xf3, 0xbc, 0xde, 0x46, 0xa3, 0xd1, + 0x83, 0x39, 0xb7, 0x56, 0xc3, 0xd6, 0xea, 0xe9, 0x08, 0x96, 0x45, 0xf8, 0x02, 0x64, 0x28, 0xf5, + 0xfa, 0x0c, 0xf6, 0xbc, 0x33, 0x93, 0x99, 0xb0, 0x2c, 0xab, 0xd2, 0xde, 0xde, 0x5e, 0xc6, 0xb5, + 0x9c, 0xcd, 0x66, 0xcb, 0x5d, 0x5d, 0x5d, 0x6f, 0x1c, 0xc7, 0x19, 0x5f, 0xfc, 0xf4, 0xff, 0xf8, + 0xf1, 0x1e, 0xf2, 0xce, 0x63, 0x06, 0xf0, 0x92, 0xeb, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE color_materials_xpm[1] = {{ png, sizeof( png ), "color_materials_xpm" }}; + +//EOF diff --git a/bitmaps_png/cpp_26/options_3drender.cpp b/bitmaps_png/cpp_26/options_3drender.cpp new file mode 100644 index 0000000000..c720260e14 --- /dev/null +++ b/bitmaps_png/cpp_26/options_3drender.cpp @@ -0,0 +1,124 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4a, 0x4c, + 0xce, 0x00, 0x00, 0x06, 0xa8, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xb5, 0x56, 0x6b, 0x6c, 0x1c, + 0xd5, 0x15, 0xfe, 0xee, 0xcc, 0xee, 0x7a, 0x77, 0xed, 0x75, 0xec, 0x24, 0x8e, 0xf7, 0xe1, 0xdd, + 0x38, 0x60, 0x63, 0x9c, 0x94, 0xbc, 0xb1, 0x1a, 0x52, 0x48, 0x03, 0xb8, 0xa8, 0x72, 0x6d, 0x70, + 0xa2, 0x90, 0x10, 0xd1, 0x07, 0x55, 0x01, 0x13, 0x44, 0x8b, 0x12, 0x24, 0x88, 0x50, 0xa5, 0x48, + 0x95, 0x51, 0x2b, 0x68, 0x55, 0xe0, 0x4f, 0x0b, 0x55, 0xcb, 0xa3, 0x8a, 0x84, 0x2d, 0x44, 0x2d, + 0xd1, 0x8a, 0xa8, 0x46, 0x08, 0x24, 0x48, 0x21, 0x38, 0x56, 0xa2, 0x84, 0x6c, 0xfc, 0x88, 0xdf, + 0x1b, 0x67, 0x77, 0x6d, 0xef, 0x63, 0x76, 0x77, 0x66, 0xe7, 0x71, 0x39, 0x77, 0xbc, 0x80, 0x23, + 0x17, 0xc8, 0x1f, 0xae, 0x74, 0xf6, 0xce, 0xec, 0x9d, 0x7b, 0xbe, 0x7b, 0xce, 0xf9, 0xce, 0x39, + 0x97, 0xe1, 0x5a, 0x46, 0xdb, 0xfd, 0x6d, 0x90, 0xa5, 0x07, 0xc1, 0xad, 0x4b, 0xb0, 0xac, 0x61, + 0x48, 0x6c, 0x14, 0x8c, 0x5d, 0xc2, 0x8c, 0x77, 0x02, 0x03, 0x2f, 0xe9, 0xd7, 0xa2, 0x82, 0x7d, + 0xe3, 0xea, 0xbe, 0x7d, 0x1e, 0x49, 0xf3, 0xbe, 0x68, 0x71, 0xeb, 0x01, 0x4f, 0x38, 0x22, 0x99, + 0xa6, 0x09, 0x43, 0x51, 0x0c, 0xab, 0x90, 0xb3, 0x50, 0x2c, 0x3a, 0x09, 0x8c, 0x43, 0x92, 0x13, + 0xa4, 0x66, 0x1c, 0xdc, 0x3c, 0x47, 0x12, 0x05, 0xa7, 0x43, 0x80, 0x0e, 0xe1, 0xd1, 0x46, 0xd1, + 0xdb, 0xab, 0x7c, 0x3b, 0x50, 0xc7, 0x81, 0x2d, 0x12, 0x73, 0xbd, 0xe9, 0xf2, 0x7a, 0x42, 0x4d, + 0x3f, 0xd8, 0xed, 0x72, 0xaf, 0xa8, 0x42, 0x91, 0x80, 0x0a, 0x86, 0x81, 0x82, 0x6e, 0x20, 0x57, + 0x54, 0x91, 0x4d, 0x67, 0xa0, 0x29, 0x59, 0xa0, 0x50, 0x00, 0xb4, 0x82, 0x85, 0x5c, 0x4e, 0x85, + 0x9a, 0xe3, 0x74, 0x08, 0x37, 0x2c, 0x2e, 0x43, 0x76, 0x0c, 0xa0, 0xef, 0xd5, 0xed, 0x42, 0x9d, + 0x63, 0x19, 0xc0, 0xb1, 0x63, 0x92, 0x74, 0x7a, 0xf4, 0x49, 0x0e, 0xfe, 0xbb, 0xa6, 0xef, 0xdd, + 0xc4, 0x5a, 0x76, 0xde, 0x26, 0x71, 0x49, 0x82, 0x26, 0x00, 0x0c, 0x01, 0xa0, 0x23, 0x5b, 0x2c, + 0x22, 0xab, 0x39, 0x51, 0xe6, 0x70, 0x21, 0x5d, 0xe1, 0x83, 0x42, 0xef, 0x34, 0x24, 0x12, 0xaf, + 0xad, 0xe3, 0xcc, 0xa7, 0x1c, 0x99, 0x0c, 0xc8, 0xc2, 0x8d, 0x5f, 0xa8, 0xbd, 0x1a, 0xa8, 0xe3, + 0xfe, 0x06, 0xe7, 0x99, 0xf1, 0x37, 0x3d, 0x15, 0xbe, 0xa6, 0x83, 0xfb, 0xf6, 0xcb, 0xf5, 0x91, + 0x08, 0x0c, 0xcb, 0xb2, 0x41, 0x72, 0x3a, 0x01, 0x68, 0x45, 0xa4, 0x1d, 0x1a, 0x1c, 0x04, 0x2c, + 0x5c, 0x61, 0x71, 0x0e, 0x9d, 0xd6, 0x85, 0xa5, 0x42, 0xec, 0xf1, 0xd9, 0x59, 0x8e, 0x6c, 0x86, + 0xc1, 0x1f, 0x04, 0xe2, 0x97, 0xf5, 0xe5, 0x40, 0x6d, 0x07, 0x1f, 0x92, 0xc1, 0x9e, 0xdf, 0xb6, + 0x69, 0xb3, 0xfc, 0xf3, 0xce, 0x3d, 0xce, 0xd5, 0xbe, 0x4a, 0xfb, 0xef, 0x9c, 0x4e, 0xca, 0x55, + 0x52, 0x4e, 0x22, 0x14, 0x0b, 0x85, 0xaa, 0xc3, 0x80, 0x53, 0x96, 0x09, 0x90, 0x41, 0x66, 0x8c, + 0x42, 0x55, 0x8a, 0xc0, 0x70, 0x94, 0x63, 0x3e, 0xc9, 0xd0, 0x4c, 0x86, 0xc8, 0x64, 0x60, 0x62, + 0x76, 0x09, 0xd0, 0x3d, 0xbf, 0xa8, 0x72, 0x30, 0xde, 0x07, 0x8e, 0x5b, 0x3a, 0xdb, 0x3b, 0x1c, + 0xad, 0x3b, 0x76, 0xa2, 0xca, 0x5d, 0x06, 0x8f, 0xd3, 0x81, 0xa2, 0x61, 0x82, 0x74, 0xc3, 0xa4, + 0x1f, 0x61, 0x99, 0x61, 0x5a, 0x25, 0x0b, 0x68, 0xb6, 0xad, 0xa0, 0x67, 0xcb, 0xb4, 0x9f, 0x31, + 0x39, 0xce, 0x11, 0x8f, 0x31, 0x34, 0xdc, 0xc8, 0x11, 0x08, 0x31, 0xcc, 0xce, 0x90, 0xeb, 0x58, + 0xf1, 0x2b, 0x20, 0xc3, 0xf8, 0x8d, 0x01, 0x7e, 0x9b, 0xab, 0xac, 0x0c, 0xa7, 0xcf, 0x9e, 0xc1, + 0xfc, 0xc2, 0x02, 0xd6, 0x86, 0xea, 0x10, 0x21, 0x29, 0xaf, 0xa8, 0xb0, 0xe3, 0x22, 0x62, 0x90, + 0x11, 0x6e, 0x53, 0x55, 0xa4, 0xc8, 0x32, 0x31, 0xa7, 0x35, 0x0d, 0x99, 0xa2, 0x66, 0xbb, 0xd3, + 0x9a, 0x8d, 0x01, 0x53, 0x63, 0x0c, 0xe1, 0x06, 0x8e, 0x75, 0x0d, 0x0c, 0x02, 0x58, 0x08, 0xe3, + 0x4b, 0x80, 0x38, 0x06, 0xdd, 0x5e, 0x6f, 0xb6, 0xad, 0xf3, 0xa7, 0xbe, 0xc9, 0xd1, 0x28, 0x2e, + 0x0c, 0x5f, 0xc2, 0x47, 0x9f, 0x7c, 0x02, 0xb5, 0x90, 0x87, 0x44, 0xb1, 0xa8, 0x5a, 0xb9, 0x0a, + 0x2b, 0x6b, 0xfd, 0xf0, 0xd2, 0xec, 0x5c, 0xb1, 0x02, 0x7a, 0x99, 0xc7, 0x26, 0x83, 0x70, 0x67, + 0x8a, 0xc0, 0xcc, 0xc4, 0x15, 0x60, 0x24, 0x0a, 0x04, 0x23, 0x16, 0x9a, 0x37, 0x48, 0xb0, 0x4a, + 0xb1, 0x12, 0x33, 0x67, 0xda, 0xd2, 0x18, 0x25, 0x75, 0x5d, 0x77, 0xad, 0xaa, 0x5b, 0x0b, 0x7f, + 0xe4, 0x3a, 0xfb, 0x4f, 0xe1, 0x8a, 0x94, 0x92, 0xc6, 0x38, 0x29, 0x98, 0x1e, 0x1b, 0xc2, 0xf4, + 0xd4, 0x14, 0xb4, 0xe8, 0x05, 0x58, 0x86, 0x0e, 0x26, 0xd3, 0x96, 0xf2, 0x72, 0xce, 0x2b, 0x57, + 0x08, 0x36, 0x30, 0x0c, 0x9e, 0x04, 0xaa, 0x57, 0x73, 0xdc, 0xd0, 0x4c, 0x0c, 0x29, 0xc5, 0xca, + 0xf6, 0xb7, 0x00, 0xb4, 0xd4, 0x25, 0x16, 0x49, 0x49, 0xd3, 0xd0, 0x5d, 0xe9, 0x5c, 0x1e, 0x2e, + 0xe7, 0x22, 0x37, 0x44, 0x3c, 0x4c, 0xb2, 0x66, 0xf5, 0xba, 0x46, 0x94, 0xd7, 0x45, 0x88, 0xd2, + 0x9a, 0x2d, 0x19, 0xca, 0x99, 0xd4, 0xf4, 0x04, 0xf2, 0x57, 0x62, 0xcc, 0x9c, 0x9b, 0x83, 0x6b, + 0xec, 0x02, 0xee, 0x0a, 0x56, 0x43, 0x2b, 0xe6, 0x58, 0xff, 0xc8, 0x79, 0x58, 0x1b, 0xb6, 0x2e, + 0x82, 0x08, 0x21, 0x1d, 0xc4, 0xf8, 0x25, 0x40, 0x66, 0x21, 0x09, 0xc9, 0xc5, 0xe6, 0xd3, 0xf3, + 0xf0, 0x50, 0x4c, 0x50, 0xa2, 0x6d, 0xd1, 0x34, 0x6c, 0x5a, 0xab, 0x46, 0x11, 0x2a, 0x51, 0x5b, + 0x25, 0x6b, 0xf4, 0xf3, 0x9f, 0xa2, 0xec, 0xe2, 0x59, 0xe4, 0xee, 0xb8, 0x9b, 0x74, 0xc8, 0xf0, + 0x9d, 0x3b, 0xc9, 0x0f, 0xec, 0xef, 0x62, 0x8a, 0xa2, 0xe0, 0xd4, 0xbf, 0xdf, 0xc5, 0xc2, 0xfa, + 0x2d, 0xa0, 0x2c, 0x86, 0xff, 0x9d, 0x1e, 0x64, 0x02, 0x11, 0xe4, 0xb9, 0x95, 0xff, 0x0a, 0x68, + 0xc7, 0x86, 0x14, 0x06, 0x46, 0xac, 0xf9, 0xcc, 0x82, 0xe4, 0x73, 0xca, 0x8b, 0x40, 0x96, 0x60, + 0x99, 0x69, 0x83, 0x09, 0x00, 0x01, 0x94, 0x1f, 0xf8, 0x10, 0x6b, 0xcf, 0x9f, 0xc4, 0x8e, 0x6d, + 0x5b, 0xf1, 0xc6, 0x3f, 0x5f, 0x20, 0xf6, 0xca, 0xd8, 0xd2, 0xd4, 0x80, 0xf5, 0xeb, 0xd7, 0xdb, + 0x7b, 0x36, 0xf7, 0xf5, 0x61, 0xe4, 0x6f, 0x7f, 0x80, 0x87, 0x28, 0x7f, 0xf4, 0xb1, 0x47, 0xf1, + 0xf2, 0x6b, 0xaf, 0xe3, 0x94, 0x62, 0x95, 0xeb, 0x5f, 0x02, 0x1d, 0x3b, 0x66, 0xb1, 0x8e, 0x9f, + 0x29, 0xe9, 0x6c, 0xaa, 0x92, 0x97, 0x97, 0x97, 0x2c, 0xb2, 0x6c, 0xf7, 0xd9, 0x14, 0x36, 0x16, + 0xad, 0xc9, 0xaf, 0x09, 0x22, 0xf9, 0x61, 0x1a, 0x77, 0xb7, 0xb7, 0xe3, 0x8e, 0xdd, 0xbb, 0xb1, + 0x66, 0xcd, 0x1a, 0x84, 0xc3, 0x61, 0x56, 0x46, 0x6c, 0x75, 0xbb, 0xdd, 0x78, 0xbb, 0xb7, 0x17, + 0x63, 0x63, 0x63, 0x10, 0xef, 0xa9, 0x54, 0x0a, 0x97, 0x93, 0x49, 0xe8, 0x35, 0xeb, 0x2e, 0x5f, + 0x95, 0xb0, 0x4c, 0x92, 0xd2, 0x59, 0x25, 0x53, 0xc9, 0xd5, 0xea, 0x52, 0x2c, 0x39, 0xcc, 0x2f, + 0x80, 0x4a, 0x2e, 0xd4, 0x2a, 0x2a, 0x91, 0xa8, 0x8d, 0xe0, 0xbf, 0xfd, 0xfd, 0xe8, 0x7a, 0xf8, + 0x61, 0x5b, 0xa1, 0x48, 0xd4, 0xc1, 0xc1, 0x41, 0x7b, 0xde, 0xb8, 0x71, 0x23, 0x82, 0xc1, 0x20, + 0x65, 0x8b, 0x81, 0x3f, 0xfd, 0xf9, 0x79, 0xcc, 0x56, 0xf9, 0x01, 0x8f, 0x6f, 0xfe, 0x2a, 0x20, + 0x2e, 0xb3, 0x94, 0x92, 0x53, 0xc2, 0x96, 0x5a, 0xf8, 0x32, 0x46, 0x96, 0x48, 0xd0, 0x52, 0x32, + 0x6a, 0x04, 0x26, 0xc0, 0x03, 0xc9, 0x18, 0xf6, 0x74, 0xfe, 0x96, 0x48, 0x57, 0x8e, 0x89, 0x89, + 0x09, 0xfc, 0xf2, 0xd0, 0xa3, 0xb8, 0xac, 0x19, 0x90, 0xaa, 0x57, 0x21, 0x64, 0x69, 0xf8, 0xeb, + 0x1f, 0x9f, 0x83, 0xdf, 0xef, 0xc7, 0x81, 0x7b, 0xf7, 0xa1, 0xaf, 0xbf, 0x0b, 0x54, 0x66, 0xa5, + 0x52, 0x0d, 0xb4, 0x16, 0x83, 0xd2, 0xbc, 0x75, 0xbf, 0xe5, 0xf6, 0xd4, 0x17, 0x7d, 0x3e, 0x3a, + 0xbd, 0x6e, 0x8b, 0x5a, 0x12, 0x23, 0x36, 0x85, 0x50, 0xdf, 0x2b, 0x08, 0x44, 0x07, 0xd1, 0xba, + 0x73, 0x07, 0xee, 0xdd, 0xbb, 0x07, 0x1e, 0x8f, 0x07, 0x8f, 0x1c, 0x7e, 0x02, 0x1f, 0xc1, 0x6b, + 0x64, 0x43, 0xd7, 0x33, 0xe5, 0x47, 0x9d, 0x2c, 0xee, 0x70, 0x63, 0xb6, 0xff, 0x3f, 0xb8, 0x73, + 0xd7, 0x2e, 0xb8, 0x5c, 0x2e, 0xbb, 0xaa, 0x4b, 0x33, 0x63, 0xf5, 0x9a, 0xa6, 0x3d, 0x9e, 0xcb, + 0xe5, 0xde, 0x5a, 0xe4, 0x33, 0xe3, 0x73, 0xa6, 0x4e, 0xc9, 0x47, 0x66, 0x2f, 0x1b, 0xc4, 0xc6, + 0x7b, 0x7e, 0x78, 0x2b, 0x1e, 0x3b, 0x74, 0x08, 0x81, 0x40, 0xc0, 0x56, 0x22, 0xdc, 0x33, 0x36, + 0x33, 0x03, 0xb3, 0xae, 0xd9, 0xa2, 0x66, 0x28, 0x0b, 0x2a, 0x1b, 0x91, 0x46, 0x5c, 0xfc, 0xf8, + 0x5d, 0xea, 0x14, 0x39, 0xa8, 0x54, 0x39, 0xee, 0x6a, 0x6d, 0xc5, 0x96, 0x4d, 0x9b, 0x7c, 0xc7, + 0x8f, 0x1f, 0x77, 0x9e, 0x38, 0x71, 0x22, 0x50, 0x2a, 0xaa, 0x2c, 0x0e, 0xfd, 0x6b, 0x1a, 0xe5, + 0xca, 0x1a, 0xbc, 0xf5, 0xc1, 0xdb, 0x38, 0xf1, 0xbf, 0x07, 0x70, 0xfb, 0xcd, 0xdb, 0xf0, 0xec, + 0x33, 0xdd, 0x10, 0x0d, 0xf0, 0x86, 0xb5, 0x11, 0x8c, 0x5c, 0x89, 0xb3, 0x22, 0xad, 0x8b, 0x2a, + 0xe0, 0xfe, 0xec, 0x34, 0x1a, 0xeb, 0xc3, 0x36, 0x50, 0x3e, 0x9f, 0x47, 0x77, 0x77, 0x77, 0x86, + 0xc8, 0x41, 0x4d, 0x10, 0x43, 0x24, 0xa3, 0x8e, 0x52, 0x50, 0xbe, 0x1e, 0xa8, 0xc6, 0x8f, 0xd8, + 0xde, 0x5f, 0xd9, 0x8f, 0x66, 0xcf, 0x5f, 0x30, 0x39, 0x39, 0x89, 0xda, 0xda, 0x5a, 0x3c, 0x7d, + 0xe4, 0x30, 0x12, 0x4f, 0x3e, 0xe5, 0x1c, 0x1f, 0x9a, 0x00, 0x9f, 0x8a, 0xa2, 0xde, 0xe7, 0xc5, + 0x83, 0x8f, 0xff, 0xda, 0x06, 0x1a, 0x1d, 0x1d, 0xc5, 0xf8, 0xf8, 0xf8, 0xec, 0xcc, 0xcc, 0xcc, + 0xd6, 0xab, 0xdb, 0x84, 0x65, 0x5e, 0x44, 0x7a, 0xa1, 0x80, 0x8b, 0xe7, 0xe2, 0xa8, 0x09, 0x04, + 0x51, 0x55, 0xed, 0xa4, 0x42, 0xb7, 0x0c, 0x73, 0xae, 0xa6, 0x0e, 0x3d, 0x3d, 0x3d, 0xe8, 0xea, + 0xea, 0x42, 0x4d, 0x4d, 0x0d, 0x5e, 0x7a, 0xf1, 0x05, 0x90, 0x32, 0x50, 0x1c, 0x50, 0x41, 0xc9, + 0x5e, 0xa4, 0x1a, 0x28, 0x80, 0xa6, 0xa7, 0xa7, 0xc5, 0x73, 0x62, 0xe9, 0xde, 0x45, 0x32, 0xdc, + 0xd7, 0x79, 0x1e, 0xb1, 0xe4, 0x04, 0x7d, 0xe5, 0xa5, 0x1e, 0x12, 0x01, 0x95, 0x19, 0xcc, 0x25, + 0xe8, 0xa8, 0x5c, 0x22, 0x1e, 0xbb, 0xa9, 0x25, 0x83, 0xcd, 0xc5, 0xd1, 0x18, 0x3d, 0x85, 0xa3, + 0x47, 0x8e, 0x60, 0x78, 0x78, 0x18, 0xa2, 0x1a, 0xc8, 0xd4, 0x93, 0x44, 0xe1, 0x15, 0x22, 0xe2, + 0x12, 0x8b, 0xc5, 0x30, 0x30, 0x30, 0x80, 0x96, 0x96, 0x16, 0x24, 0x12, 0x89, 0xd5, 0xe4, 0x42, + 0x25, 0x93, 0xc9, 0x7c, 0xfc, 0xff, 0xef, 0x0c, 0xd4, 0xca, 0x71, 0x6a, 0x88, 0x6a, 0x09, 0xee, + 0xa4, 0xe5, 0x76, 0xfa, 0xe2, 0x16, 0x38, 0x9c, 0x29, 0x6f, 0x6e, 0x41, 0x6e, 0x91, 0xb4, 0xca, + 0x1b, 0x1b, 0x1b, 0xf0, 0xaf, 0xf7, 0x3e, 0x80, 0x93, 0x2a, 0x40, 0x63, 0xc0, 0xcf, 0xf7, 0x74, + 0xb4, 0xb3, 0xea, 0xea, 0x6a, 0xbc, 0xfc, 0xf7, 0x7f, 0xf0, 0xa1, 0xe8, 0x85, 0x69, 0x43, 0xd7, + 0x4f, 0x87, 0x42, 0xa1, 0x5b, 0x29, 0x89, 0x19, 0xb9, 0xf9, 0x30, 0x59, 0xfc, 0xca, 0xb7, 0xdf, + 0x82, 0xc4, 0xf8, 0xf1, 0xfe, 0xeb, 0xe1, 0x90, 0xdb, 0xa8, 0xe4, 0xb7, 0x79, 0xe7, 0x66, 0x6f, + 0x97, 0xd4, 0xbc, 0xa9, 0xdc, 0xb4, 0x3d, 0x8e, 0xda, 0x60, 0x40, 0x7a, 0xff, 0x1d, 0x7e, 0xb0, + 0x29, 0xe2, 0xbc, 0x79, 0xfb, 0x76, 0x74, 0x3f, 0xfb, 0xdc, 0x99, 0xf8, 0xf4, 0xd4, 0x66, 0xb1, + 0x85, 0xe8, 0xff, 0x7d, 0x72, 0x65, 0x98, 0xac, 0xea, 0xbd, 0xb6, 0xeb, 0xd6, 0xf2, 0xeb, 0x17, + 0x75, 0x42, 0x57, 0xab, 0x00, 0x85, 0xc4, 0x7f, 0x42, 0x77, 0x83, 0xda, 0xe6, 0xb9, 0x09, 0x84, + 0x03, 0x7e, 0x51, 0x21, 0x8e, 0x92, 0xe2, 0xdf, 0xe3, 0x3b, 0x18, 0x0c, 0xad, 0xf7, 0xed, 0x42, + 0xc3, 0x86, 0x57, 0x29, 0x58, 0x7b, 0xe9, 0x7d, 0xd5, 0x37, 0x7d, 0xfc, 0x39, 0x20, 0x01, 0x89, + 0x7a, 0x99, 0x8c, 0x57, 0x54, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, + 0x82, +}; + +const BITMAP_OPAQUE options_3drender_xpm[1] = {{ png, sizeof( png ), "options_3drender_xpm" }}; + +//EOF diff --git a/bitmaps_png/cpp_26/render_mode.cpp b/bitmaps_png/cpp_26/render_mode.cpp new file mode 100644 index 0000000000..9a90a39f44 --- /dev/null +++ b/bitmaps_png/cpp_26/render_mode.cpp @@ -0,0 +1,99 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4a, 0x4c, + 0xce, 0x00, 0x00, 0x05, 0x22, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xa5, 0x96, 0x5b, 0x6c, 0x54, + 0x45, 0x18, 0xc7, 0xff, 0x33, 0xe7, 0xb2, 0x97, 0xb6, 0xbb, 0xbd, 0xd0, 0x52, 0x8b, 0x45, 0x8b, + 0x10, 0xc3, 0xa5, 0x0b, 0x52, 0x84, 0x02, 0x6d, 0x2c, 0x92, 0x08, 0x42, 0x20, 0x80, 0x29, 0xd1, + 0x20, 0x06, 0x13, 0xa2, 0x44, 0x8d, 0x0f, 0x26, 0x3c, 0xf8, 0x46, 0x8c, 0x18, 0x1e, 0x7c, 0xe3, + 0x85, 0x98, 0xc8, 0x0b, 0x1a, 0x63, 0x30, 0x21, 0x6a, 0x8c, 0xef, 0x3c, 0x08, 0xa9, 0x2d, 0x18, + 0x2a, 0x01, 0x81, 0x4a, 0x4b, 0x5b, 0xb9, 0xb5, 0xb4, 0x7b, 0x3d, 0xf7, 0x33, 0x7e, 0x73, 0xce, + 0x6e, 0x77, 0xc5, 0x14, 0x29, 0x7e, 0xc9, 0xe4, 0x9c, 0x3d, 0x3b, 0x33, 0xbf, 0xf9, 0xff, 0xbf, + 0xf9, 0xe6, 0x1c, 0x86, 0xc7, 0x8d, 0x9d, 0xaf, 0xb7, 0xc0, 0xd5, 0xab, 0x51, 0x65, 0x0c, 0xe3, + 0xf4, 0x69, 0x1b, 0x73, 0x0c, 0xf6, 0x58, 0xbd, 0x76, 0xec, 0x7b, 0x8f, 0xa9, 0x91, 0x4f, 0x11, + 0x8d, 0x38, 0xc2, 0x30, 0x34, 0x1a, 0xe5, 0x42, 0xb0, 0x31, 0xba, 0x5e, 0x85, 0x6b, 0x5f, 0xa2, + 0x69, 0xae, 0xc3, 0xf3, 0x87, 0xa0, 0x46, 0x87, 0xf0, 0xc3, 0xc9, 0xec, 0xdc, 0x41, 0xaf, 0xf6, + 0x36, 0x2a, 0xb1, 0xea, 0x6f, 0xe3, 0xb5, 0xf5, 0xab, 0x5b, 0xd7, 0xad, 0x4f, 0x0a, 0x4d, 0x87, + 0xed, 0x7a, 0x28, 0xd8, 0x0e, 0x0a, 0xd9, 0x34, 0xf2, 0x99, 0x2c, 0xdc, 0x42, 0x96, 0x1e, 0xe4, + 0x33, 0xd4, 0x1c, 0x18, 0x86, 0x0a, 0xcf, 0x65, 0xe0, 0xca, 0x5d, 0xc0, 0xbf, 0x01, 0x0f, 0x7f, + 0xc0, 0x71, 0x3f, 0xc1, 0xcf, 0x5f, 0x67, 0x66, 0x05, 0x29, 0x3b, 0xdf, 0xdc, 0xae, 0xe8, 0xd1, + 0x53, 0xa9, 0xb5, 0x9d, 0x89, 0xb6, 0xa5, 0xcb, 0x14, 0xc7, 0xf7, 0x61, 0x11, 0xc4, 0x70, 0x5d, + 0xe4, 0x6d, 0x1b, 0x39, 0x82, 0x65, 0x6d, 0x0b, 0x19, 0xcb, 0xa6, 0x66, 0x95, 0x07, 0x0a, 0x01, + 0xc8, 0xdf, 0x97, 0x06, 0x68, 0x80, 0xc9, 0x49, 0xf9, 0x46, 0xfc, 0xf4, 0xd5, 0x2f, 0xea, 0xbf, + 0x08, 0xbd, 0xbd, 0xd5, 0x31, 0xd4, 0x9e, 0x4a, 0xd6, 0x26, 0x5f, 0xee, 0x7d, 0x6d, 0x6f, 0x22, + 0x91, 0x48, 0xc2, 0x25, 0x88, 0x49, 0x80, 0x82, 0x23, 0x27, 0x97, 0x13, 0xab, 0xd0, 0x15, 0x0b, + 0x0a, 0x0f, 0xd7, 0xe9, 0x09, 0x9f, 0xe0, 0x4e, 0xd1, 0x23, 0x7a, 0x76, 0x75, 0xd0, 0x83, 0xa6, + 0x72, 0x44, 0x12, 0x0e, 0x72, 0x53, 0x86, 0x7c, 0xfc, 0x4f, 0xd0, 0x8e, 0x37, 0xba, 0x62, 0x88, + 0x7f, 0xf7, 0x4a, 0xcf, 0xa6, 0xe4, 0xf6, 0x9e, 0x4d, 0xd1, 0x88, 0xaa, 0x06, 0x93, 0x48, 0x80, + 0x5c, 0xf9, 0xb4, 0x69, 0xd1, 0x3c, 0x8c, 0xd2, 0x21, 0x60, 0x7b, 0x21, 0x5c, 0x53, 0x14, 0x68, + 0x5c, 0xa1, 0xc1, 0x45, 0xd0, 0xe0, 0x05, 0x0f, 0x9e, 0xc7, 0xb0, 0xa6, 0x93, 0xe1, 0x62, 0x9f, + 0x20, 0x45, 0x15, 0xa0, 0x8e, 0x77, 0xb4, 0x48, 0x9b, 0xfb, 0x79, 0x54, 0x8f, 0x1d, 0x38, 0xb8, + 0x6f, 0x7f, 0x62, 0xd5, 0x92, 0xc5, 0xa8, 0x8b, 0x45, 0x11, 0xd7, 0x34, 0x4c, 0x19, 0x26, 0x7c, + 0x9a, 0xb8, 0xc0, 0x5d, 0x14, 0x05, 0x80, 0x46, 0xc3, 0x27, 0x8b, 0x3c, 0x6a, 0xf2, 0x3f, 0x8f, + 0x14, 0x07, 0x71, 0xed, 0x32, 0xe5, 0xab, 0xc0, 0xb1, 0xae, 0x8b, 0x41, 0xd5, 0x48, 0x2a, 0x75, + 0xf4, 0x2a, 0x40, 0xda, 0x33, 0xee, 0x79, 0xcf, 0xf5, 0x57, 0x6f, 0xe8, 0xd9, 0x08, 0xa1, 0x2a, + 0x34, 0xb9, 0x01, 0x83, 0x54, 0x90, 0xdb, 0xb0, 0x3c, 0x8f, 0xf2, 0x41, 0x76, 0x91, 0x9a, 0x69, + 0xcb, 0x0a, 0xf2, 0x91, 0xb5, 0xec, 0xe0, 0x99, 0xb4, 0x2b, 0xe7, 0xd0, 0x95, 0xfa, 0x62, 0x64, + 0xd8, 0xc7, 0xd4, 0x03, 0x82, 0x74, 0x33, 0x44, 0x63, 0x04, 0x71, 0x69, 0x45, 0x1e, 0x11, 0x84, + 0x39, 0x03, 0x72, 0x5c, 0xfb, 0x4a, 0xea, 0x85, 0x0d, 0xab, 0x47, 0xc6, 0xfe, 0xc2, 0xaf, 0x03, + 0xfd, 0xc8, 0xe5, 0x32, 0xa8, 0x8a, 0x57, 0x63, 0x7e, 0x73, 0xb3, 0x68, 0x59, 0xd8, 0x8a, 0x44, + 0xfd, 0x3c, 0xa6, 0x57, 0x27, 0x50, 0xa0, 0xc1, 0x12, 0x94, 0x2e, 0x42, 0xd3, 0xa6, 0x89, 0x29, + 0xba, 0x17, 0x63, 0x63, 0x02, 0xb7, 0x47, 0x39, 0x3a, 0xbb, 0x81, 0xaa, 0x6a, 0x82, 0x78, 0x08, + 0x56, 0x29, 0x95, 0x3a, 0xbc, 0xc2, 0x3a, 0x5f, 0xdc, 0xac, 0x9d, 0xdf, 0x6c, 0xa7, 0x56, 0xae, + 0xd3, 0x55, 0xce, 0x83, 0x1c, 0xa4, 0x09, 0x36, 0x32, 0x7c, 0x9d, 0xdd, 0x18, 0xba, 0x8a, 0xa9, + 0x81, 0x0b, 0x30, 0xb2, 0x59, 0xea, 0xe6, 0x41, 0x8d, 0x57, 0x0b, 0x5e, 0xd7, 0x00, 0x27, 0x1e, + 0x67, 0x4e, 0x55, 0x0d, 0x30, 0x35, 0x09, 0x8c, 0x0e, 0x31, 0xac, 0xed, 0x12, 0xa8, 0x49, 0xb2, + 0x10, 0x22, 0x02, 0x83, 0x09, 0xc4, 0x70, 0xaf, 0xaa, 0x02, 0xc4, 0xc4, 0x83, 0x4c, 0x3a, 0xed, + 0x64, 0x2d, 0x43, 0xe7, 0x8c, 0x07, 0xfe, 0xbb, 0x94, 0x90, 0x86, 0x85, 0x6d, 0x88, 0x35, 0x2f, + 0xa0, 0xba, 0xb1, 0xc8, 0x26, 0x5a, 0x7d, 0x7a, 0x12, 0x0f, 0x6e, 0x0d, 0xb3, 0xdc, 0xbd, 0x3b, + 0x70, 0xef, 0x8c, 0x03, 0x64, 0x5f, 0x50, 0x89, 0x2b, 0xd7, 0x78, 0xa8, 0x9f, 0xa7, 0x04, 0x10, + 0x09, 0x10, 0x15, 0x6d, 0xe0, 0x0b, 0xa7, 0x42, 0x11, 0xbb, 0x9f, 0xcd, 0x4e, 0xbb, 0x19, 0xd3, + 0xa0, 0x84, 0xb3, 0x10, 0x44, 0xb2, 0x6d, 0xd7, 0xa1, 0x9d, 0x45, 0x75, 0xe8, 0xda, 0xc1, 0xd5, + 0xd7, 0x22, 0xd0, 0x5a, 0x9f, 0x85, 0xde, 0xd4, 0x0c, 0x97, 0x72, 0x43, 0x75, 0x02, 0xfc, 0xd6, + 0x2f, 0xd0, 0xd4, 0xa2, 0x94, 0xb6, 0xc9, 0x0c, 0xc0, 0x17, 0xe1, 0xae, 0x29, 0x46, 0x08, 0xe2, + 0x98, 0xcc, 0xe5, 0xb2, 0xbe, 0x04, 0xc9, 0xed, 0x2b, 0x44, 0xb8, 0x93, 0x6c, 0xca, 0x89, 0x45, + 0xcd, 0xa4, 0x49, 0x0d, 0x6a, 0x26, 0x25, 0xdd, 0x22, 0xa0, 0x7c, 0x16, 0x8e, 0xe3, 0xc5, 0xc9, + 0xa5, 0x5d, 0xac, 0x42, 0x89, 0x1f, 0xb6, 0xd0, 0x43, 0x54, 0xd6, 0xd1, 0x84, 0x51, 0xc8, 0x89, + 0xb4, 0x59, 0x08, 0x14, 0xc9, 0xbf, 0x65, 0xfd, 0x38, 0x64, 0x45, 0x00, 0x93, 0xca, 0x08, 0x62, + 0x14, 0x81, 0x33, 0xdb, 0x99, 0xf1, 0xf0, 0x14, 0x93, 0xbf, 0xb9, 0x08, 0xaf, 0x41, 0x13, 0xa5, + 0x03, 0xee, 0x61, 0x10, 0x9f, 0xb0, 0x4c, 0x83, 0x57, 0x2a, 0x0a, 0xed, 0xf3, 0x02, 0x58, 0xa0, + 0x42, 0x5a, 0x48, 0x30, 0xf9, 0x6c, 0x26, 0x02, 0x45, 0x14, 0x74, 0x34, 0x41, 0x16, 0x6d, 0x09, + 0x24, 0x8a, 0xd7, 0x40, 0x6a, 0x25, 0x88, 0x69, 0x13, 0xae, 0x65, 0x2a, 0x21, 0xa8, 0x64, 0xb1, + 0x1f, 0xe4, 0x49, 0x4e, 0x6c, 0xbb, 0xa1, 0x85, 0xa2, 0xec, 0x44, 0xf9, 0xb8, 0x91, 0x9e, 0x79, + 0x94, 0x6f, 0x5f, 0x29, 0x43, 0x44, 0x51, 0x1d, 0xe3, 0x0f, 0x81, 0xe8, 0x68, 0xf7, 0xf7, 0xbc, + 0xcd, 0xa5, 0x75, 0x25, 0x45, 0x41, 0x9e, 0x8a, 0xf6, 0xcd, 0xfe, 0x92, 0x61, 0xa1, 0x75, 0x9e, + 0x5f, 0x61, 0x5b, 0xd1, 0xd6, 0xf1, 0x5b, 0xe0, 0xf9, 0x42, 0x4d, 0xd7, 0xa1, 0x8f, 0x4f, 0x74, + 0xae, 0x5f, 0x7b, 0x52, 0xad, 0x38, 0x74, 0x85, 0x41, 0xdb, 0x38, 0x1c, 0x3c, 0x97, 0x10, 0xe1, + 0x29, 0x20, 0x2d, 0xf5, 0x8b, 0x35, 0x44, 0xaf, 0x10, 0x8c, 0x8e, 0x20, 0xd5, 0xd0, 0x78, 0x7e, + 0xf9, 0xd2, 0x65, 0xfd, 0x6b, 0xde, 0xda, 0x35, 0xc0, 0xcb, 0xab, 0xe3, 0x26, 0x1e, 0xb5, 0xfa, + 0x59, 0x39, 0x2c, 0x3c, 0x09, 0x4a, 0xaa, 0xe4, 0xfd, 0xe5, 0x41, 0xb4, 0x82, 0xdd, 0x48, 0x2d, + 0x5f, 0xf1, 0xe3, 0xd2, 0x25, 0x0d, 0xa7, 0xf6, 0x32, 0xe6, 0x95, 0x41, 0x0a, 0xcb, 0x53, 0x71, + 0xe0, 0x09, 0x48, 0x65, 0x45, 0x32, 0xae, 0x5f, 0x41, 0xbc, 0x60, 0xe4, 0x3a, 0x96, 0xaf, 0xf8, + 0xbe, 0xfd, 0xc5, 0x45, 0xc7, 0x3f, 0xdc, 0xb6, 0xcd, 0x0a, 0x2b, 0xa8, 0x14, 0xae, 0x77, 0x13, + 0xe3, 0xa3, 0x7f, 0x52, 0x11, 0xba, 0x73, 0x07, 0x15, 0x2d, 0x9b, 0xb8, 0x0f, 0x76, 0xff, 0xae, + 0xe8, 0x5c, 0xf4, 0xdc, 0x99, 0xb6, 0xc5, 0xa9, 0xcf, 0x0e, 0x6f, 0xd9, 0x92, 0x9f, 0xd1, 0x31, + 0xd3, 0x7f, 0x51, 0xea, 0x1c, 0x79, 0x1b, 0xc7, 0xed, 0x5b, 0xf3, 0x30, 0x3e, 0x3e, 0x4d, 0xaf, + 0xe6, 0x61, 0xe8, 0xba, 0x0e, 0x3d, 0x12, 0x7f, 0x64, 0xde, 0x28, 0x17, 0x68, 0x6c, 0x0a, 0x6b, + 0x6a, 0xf0, 0x22, 0xda, 0x93, 0xb5, 0x67, 0x3b, 0x3a, 0x3a, 0x3e, 0x38, 0x7e, 0x68, 0xf7, 0xdd, + 0xff, 0xfe, 0x66, 0xd8, 0xbd, 0xbf, 0x09, 0x8e, 0xd8, 0x4a, 0x7a, 0x77, 0x50, 0x97, 0x6e, 0xa6, + 0xe9, 0x8d, 0x98, 0xff, 0x14, 0x17, 0x0b, 0x5a, 0x11, 0xbc, 0x67, 0x2a, 0xe3, 0xdc, 0x59, 0x60, + 0x59, 0x3b, 0x30, 0x3c, 0x84, 0xe6, 0x7c, 0xfe, 0xe6, 0xf6, 0xae, 0xee, 0x9d, 0x5f, 0x1e, 0x3e, + 0xf8, 0xfb, 0xdc, 0xbf, 0x82, 0x7a, 0x0e, 0x44, 0x11, 0x31, 0x37, 0x53, 0x7d, 0xbc, 0x4b, 0x90, + 0xcd, 0x50, 0x95, 0x18, 0x1d, 0xa0, 0x0c, 0x4f, 0x2f, 0x04, 0x6a, 0x12, 0x21, 0xa8, 0xae, 0x1e, + 0xda, 0xe4, 0x84, 0xb9, 0xab, 0x7d, 0xd5, 0x4b, 0xa7, 0x8f, 0x7e, 0xd4, 0xf7, 0xe4, 0x9f, 0x5b, + 0xa5, 0x38, 0x72, 0x84, 0xa3, 0xef, 0x5a, 0x17, 0x1c, 0xfb, 0x7d, 0x52, 0xb9, 0x45, 0x28, 0x4a, + 0x02, 0x8a, 0xc2, 0xe4, 0x6e, 0x5b, 0xdf, 0xd2, 0xb2, 0xe7, 0xdc, 0x89, 0x63, 0x67, 0xfe, 0xdf, + 0x77, 0xdd, 0x6c, 0xb1, 0xb5, 0xf7, 0x79, 0x26, 0xf8, 0xd1, 0x05, 0xf5, 0x75, 0xfd, 0x63, 0xdf, + 0x9c, 0x38, 0xf6, 0xa8, 0xae, 0x7f, 0x03, 0x4c, 0xb0, 0x0d, 0xd6, 0xc8, 0x58, 0x93, 0x13, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE render_mode_xpm[1] = {{ png, sizeof( png ), "render_mode_xpm" }}; + +//EOF diff --git a/bitmaps_png/sources/CREDITS b/bitmaps_png/sources/CREDITS index cedee67301..72b66d8104 100644 --- a/bitmaps_png/sources/CREDITS +++ b/bitmaps_png/sources/CREDITS @@ -28,3 +28,6 @@ Thorsten Behrens, http://lists.freedesktop.org/archives/libreoffice/2011-Februar Konstantin, http://electronix.ru/forum/index.php?showtopic=106246&view=findpost&p=1160223 +render_mode.svg +color_materials.svg +Mario Luzeiro 2016 License: CC SA diff --git a/bitmaps_png/sources/color_materials.svg b/bitmaps_png/sources/color_materials.svg new file mode 100644 index 0000000000..72d6bcdcc0 --- /dev/null +++ b/bitmaps_png/sources/color_materials.svg @@ -0,0 +1,215 @@ + + + + + + image/svg+xml + + + + + Mario Luzeiro + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bitmaps_png/sources/options_3drender.svg b/bitmaps_png/sources/options_3drender.svg new file mode 100644 index 0000000000..147a0da2d4 --- /dev/null +++ b/bitmaps_png/sources/options_3drender.svg @@ -0,0 +1,191 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bitmaps_png/sources/render_mode.svg b/bitmaps_png/sources/render_mode.svg new file mode 100644 index 0000000000..70742ccac4 --- /dev/null +++ b/bitmaps_png/sources/render_mode.svg @@ -0,0 +1,172 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 736a4de38d..bbcaffa3b2 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -503,3 +503,4 @@ target_link_libraries( dsntest common ${wxWidgets_LIBRARIES} rt ) add_dependencies( dsntest lib-dependencies ) +target_link_libraries( pcbcommon 3d-viewer ) diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index 1ea09d5586..7a6a929d8e 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -344,7 +344,7 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount ) } -void SHAPE_POLY_SET::importTree( PolyTree* tree) +void SHAPE_POLY_SET::importTree( PolyTree* tree ) { m_polys.clear(); @@ -353,6 +353,7 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree) if( !n->IsHole() ) { POLYGON paths; + paths.reserve( n->Childs.size() + 1 ); paths.push_back( convertFromClipper( n->Contour ) ); for( unsigned int i = 0; i < n->Childs.size(); i++ ) diff --git a/common/pcbcommon.cpp b/common/pcbcommon.cpp index 614764164a..02c9d9ea97 100644 --- a/common/pcbcommon.cpp +++ b/common/pcbcommon.cpp @@ -33,7 +33,6 @@ #include #include -#include <3d_viewer.h> wxString LayerMaskDescribe( const BOARD *aBoard, LSET aMask ) diff --git a/cvpcb/class_DisplayFootprintsFrame.cpp b/cvpcb/class_DisplayFootprintsFrame.cpp index 398122db9a..cbb8e4ae00 100644 --- a/cvpcb/class_DisplayFootprintsFrame.cpp +++ b/cvpcb/class_DisplayFootprintsFrame.cpp @@ -50,7 +50,7 @@ #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> BEGIN_EVENT_TABLE( DISPLAY_FOOTPRINTS_FRAME, PCB_BASE_FRAME ) @@ -149,7 +149,7 @@ DISPLAY_FOOTPRINTS_FRAME::~DISPLAY_FOOTPRINTS_FRAME() void DISPLAY_FOOTPRINTS_FRAME::OnCloseWindow( wxCloseEvent& event ) { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->Close( true ); @@ -387,7 +387,7 @@ bool DISPLAY_FOOTPRINTS_FRAME::GeneralControl( wxDC* aDC, const wxPoint& aPositi void DISPLAY_FOOTPRINTS_FRAME::Show3D_Frame( wxCommandEvent& event ) { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) { @@ -405,7 +405,7 @@ void DISPLAY_FOOTPRINTS_FRAME::Show3D_Frame( wxCommandEvent& event ) return; } - draw3DFrame = new EDA_3D_FRAME( &Kiway(), this, _( "3D Viewer" ) ); + draw3DFrame = new EDA_3D_VIEWER( &Kiway(), this, _( "3D Viewer" ) ); draw3DFrame->Raise(); // Needed with some Window Managers draw3DFrame->Show( true ); } @@ -538,7 +538,7 @@ void DISPLAY_FOOTPRINTS_FRAME::InitDisplay() GetCanvas()->Refresh(); - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->NewDisplay(); diff --git a/cvpcb/cvpcb.cpp b/cvpcb/cvpcb.cpp index dc3d9c42ac..a0c92e6541 100644 --- a/cvpcb/cvpcb.cpp +++ b/cvpcb/cvpcb.cpp @@ -55,8 +55,6 @@ const wxString EquFileExtension( wxT( "equ" ) ); // Wildcard for schematic retroannotation (import footprint names in schematic): const wxString EquFilesWildcard( _( "Component/footprint equ files (*.equ)|*.equ" ) ); -KIWAY* TheKiway = NULL; - namespace CV { static struct IFACE : public KIFACE_I @@ -78,7 +76,6 @@ static struct IFACE : public KIFACE_I case FRAME_CVPCB: { CVPCB_MAINFRAME* frame = new CVPCB_MAINFRAME( aKiway, aParent ); - TheKiway = aKiway; return frame; } break; diff --git a/include/bitmaps.h b/include/bitmaps.h index d537354fb5..ffd70e0b79 100644 --- a/include/bitmaps.h +++ b/include/bitmaps.h @@ -132,6 +132,7 @@ EXTERN_BITMAP( change_glabel_xpm ) EXTERN_BITMAP( change_text_xpm ) EXTERN_BITMAP( contrast_mode_xpm ) EXTERN_BITMAP( checked_ok_xpm ) +EXTERN_BITMAP( color_materials_xpm ) EXTERN_BITMAP( component_select_alternate_shape_xpm ) EXTERN_BITMAP( component_select_unit_xpm ) EXTERN_BITMAP( config_xpm ) @@ -381,6 +382,7 @@ EXTERN_BITMAP( open_brd_file_xpm ) EXTERN_BITMAP( open_document_xpm ) EXTERN_BITMAP( open_library_xpm ) EXTERN_BITMAP( open_project_xpm ) +EXTERN_BITMAP( options_3drender_xpm ) EXTERN_BITMAP( options_all_tracks_and_vias_xpm ) EXTERN_BITMAP( options_all_tracks_xpm ) EXTERN_BITMAP( options_all_vias_xpm ) @@ -470,6 +472,7 @@ EXTERN_BITMAP( ratsnest_xpm ) EXTERN_BITMAP( read_setup_xpm ) EXTERN_BITMAP( redo_xpm ) EXTERN_BITMAP( red_xpm ) +EXTERN_BITMAP( render_mode_xpm ) EXTERN_BITMAP( reload2_xpm ) EXTERN_BITMAP( reload_xpm ) EXTERN_BITMAP( repaint_xpm ) diff --git a/include/class_eda_rect.h b/include/class_eda_rect.h index 21a0f6ab1d..ba14b981cf 100644 --- a/include/class_eda_rect.h +++ b/include/class_eda_rect.h @@ -94,6 +94,13 @@ public: bool Contains( const EDA_RECT& aRect ) const; const wxSize& GetSize() const { return m_Size; } + + /** + * @brief GetSizeMax + * @return the max size dimension + */ + int GetSizeMax() const { return ( m_Size.x > m_Size.y )?m_Size.x:m_Size.y; } + int GetX() const { return m_Pos.x; } int GetY() const { return m_Pos.y; } diff --git a/include/id.h b/include/id.h index 8630c36b78..66b9858aad 100644 --- a/include/id.h +++ b/include/id.h @@ -59,6 +59,7 @@ // Define room for IDs, for each sub application #define ROOM_FOR_KICADMANAGER 50 #define ROOM_FOR_3D_VIEWER 100 +#define ROOM_FOR_PANEL_PREV_MODEL 50 enum main_id { @@ -286,6 +287,9 @@ enum main_id ID_KICAD_3D_VIEWER_START, ID_KICAD_3D_VIEWER_END = ID_KICAD_3D_VIEWER_START + ROOM_FOR_3D_VIEWER, + ID_KICAD_PANEL_PREV_MODEL_START, + ID_KICAD_PANEL_PREV_MODEL_END = ID_KICAD_PANEL_PREV_MODEL_START + ROOM_FOR_PANEL_PREV_MODEL, + ID_END_LIST }; diff --git a/include/lru_cache.h b/include/lru_cache.h new file mode 100644 index 0000000000..3d0ed82097 --- /dev/null +++ b/include/lru_cache.h @@ -0,0 +1,243 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Mario Luzeiro + * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file lru_cache.h + * @brief Template define a least-recently-used cache algo based on wxHashMap and wxString + * http://docs.wxwidgets.org/3.0/classwx_hash_map.html + */ + +#ifndef LRU_CACHE_H +#define LRU_CACHE_H + + +#include +#include +#include +#include +#include + + +/** + * Template LRU_WXSTR_CACHE + * template for a wxString key based LRU cache + * + * @code + * LRU_WXSTR_CACHE< int > cache; + * + * cache.Resize( 3 ); + * + * cache.Insert( "First", 1 ); + * cache.Insert( "Second", 2 ); + * + * printf(" cache.Size() %d \n", cache.Size() ); // size == 2 + * printf(" cache.MaxSize() %d \n", cache.MaxSize() ); // max size == 3 + * @endcode + */ +template< typename t_value > +class LRU_WXSTR_CACHE +{ + +private: + + /** + * Declares KEY_VALUE_PAIR + * Declares a pair with the key (wxString) and a value + */ + typedef std::pair< wxString, t_value > KEY_VALUE_PAIR; + + /** + * Declares LIST_ITERATOR + * Declares a iterator type for a list of KEY_VALUE_PAIR + */ + typedef std::list< KEY_VALUE_PAIR > CACHED_LIST; + + + /** + * Declares LIST_ITERATOR + * Declares a iterator type for a list of KEY_VALUE_PAIR + */ + typedef typename CACHED_LIST::iterator LIST_ITERATOR; + + + /** + * Declares WXSTR_HASH_MAP + * Declares a map type of LIST_ITERATOR based on a wxString key + */ + typedef std::map< wxString, LIST_ITERATOR > WXSTR_HASH_MAP; + + /** + * Declares MAP_ITERATOR + * Declares a iterator for the map + */ + typedef typename WXSTR_HASH_MAP::iterator MAP_ITERATOR; + + + /** + * list of cached items + */ + CACHED_LIST m_cached_list; + + /** + * Cache map with iterators of the list + */ + WXSTR_HASH_MAP m_map_iterators; + + + /** + * Max capacity of the cache + */ + size_t m_maxSize; + +public: + + + /** + * Constructor LRU_WXSTR_CACHE + * @param aMaxSize - initial max number of items of the cache + */ + LRU_WXSTR_CACHE( size_t aMaxSize = 1 ) : m_maxSize( aMaxSize ) {} + + + /** + * Function Insert + * @param aKey = the string key that is the reference of this entry + * @param aValue = the value to add + */ + void Insert( const wxString &aKey, const t_value &aValue ) + { + MAP_ITERATOR it = m_map_iterators.find( aKey ); + + if( it != m_map_iterators.end() ) + { + // It already exists, so must remove it from list and form the map + // it->second have a iterator from the list m_cached_list + m_cached_list.erase( it->second ); + m_map_iterators.erase( it ); + } + + // Inserts a new element at the beginning of the list, a pair of + m_cached_list.push_front( KEY_VALUE_PAIR( aKey, aValue) ); + + // Insert a new key and the added list iterator to the map + m_map_iterators[aKey] = m_cached_list.begin(); + + // Manage the size of the list + if( m_cached_list.size() > m_maxSize ) + { + // Get an iterator to the end of the list + LIST_ITERATOR last_it = m_cached_list.end(); + + // This gets the real iterator that is the latest one + last_it--; + + // Remove the key from the map + m_map_iterators.erase( last_it->first ); + + // Removes the last element in the list + m_cached_list.pop_back(); + } + } + + + /** + * Function Get + * Returns an existent value from the given key. + * The key must exists, if not it will throw an error. + * Use function Exists to check first if you can get that key + * @param aKey = an existent key + * @return t_value + */ + const t_value& Get( const wxString &aKey ) + { + MAP_ITERATOR map_it = m_map_iterators.find( aKey ); + + if( map_it == m_map_iterators.end() ) + { + throw std::range_error( "Requested a key that dont exists" ); + } + + // This will update the list and put in the beginning the iterator that we are getting + m_cached_list.splice( m_cached_list.begin(), m_cached_list, map_it->second ); + + // Return the t_value from the pair that was in the list + return map_it->second->second; + } + + + /** + * Function Exists + * @param aKey key to look for + * @return true if the aKey exists + */ + bool Exists( const wxString &aKey ) const + { + return ( m_map_iterators.find( aKey ) != m_map_iterators.end() ); + } + + + /** + * Function Resize + * If aNewSize is smaller than the current maxSize then the items back in the list are discarded + * This function can be used to empty the cache, setting the new size to 0 + * @param aNewSize - resize the store capability of the list to aNewSize + */ + void Resize( size_t aNewSize ) + { + m_maxSize = aNewSize; + + while( m_map_iterators.size() > m_maxSize ) + { + // Remove the key from the map + m_map_iterators.erase( m_cached_list.back().first ); + + // Remove the back of the list + m_cached_list.pop_back(); + } + } + + + /** + * Function Size + * @return size_t current size of the cache + */ + size_t Size() const + { + return m_map_iterators.size(); + } + + + /** + * Function MaxSize + * @return size_t current max size of the cache + */ + size_t MaxSize() const + { + return m_maxSize; + } + + +}; + +#endif // LRU_CACHE_H diff --git a/include/plugins/3dapi/xv3d_types.h b/include/plugins/3dapi/xv3d_types.h index 43222d674d..6fdd567d66 100644 --- a/include/plugins/3dapi/xv3d_types.h +++ b/include/plugins/3dapi/xv3d_types.h @@ -38,15 +38,17 @@ #include #include -typedef glm::uvec2 SFVEC2UI; -typedef glm::ivec2 SFVEC2I; -typedef glm::vec2 SFVEC2F; -typedef glm::dvec2 SFVEC2D; -typedef glm::vec3 SFVEC3F; -typedef glm::dvec3 SFVEC3D; -typedef glm::vec4 SFVEC4F; -typedef glm::uvec3 SFVEC3UI; -typedef glm::dvec3 SFVEC3D; +typedef glm::uvec2 SFVEC2UI; +typedef glm::ivec2 SFVEC2I; +typedef glm::u64vec2 SFVEC2UI64; +typedef glm::i64vec2 SFVEC2I64; +typedef glm::vec2 SFVEC2F; +typedef glm::dvec2 SFVEC2D; +typedef glm::vec3 SFVEC3F; +typedef glm::dvec3 SFVEC3D; +typedef glm::vec4 SFVEC4F; +typedef glm::uvec3 SFVEC3UI; +typedef glm::dvec3 SFVEC3D; #define CLASS_ALIGNMENT 16 diff --git a/include/wxBasePcbFrame.h b/include/wxBasePcbFrame.h index 81f592e44e..84038252a0 100644 --- a/include/wxBasePcbFrame.h +++ b/include/wxBasePcbFrame.h @@ -43,7 +43,6 @@ #include #include - /* Forward declarations of classes. */ class BOARD; class BOARD_CONNECTED_ITEM; @@ -51,7 +50,7 @@ class MODULE; class TRACK; class D_PAD; class TEXTE_MODULE; -class EDA_3D_FRAME; +class EDA_3D_VIEWER; class GENERAL_COLLECTOR; class GENERAL_COLLECTORS_GUIDE; class BOARD_DESIGN_SETTINGS; @@ -109,7 +108,7 @@ public: /** * @return a reference to the 3D viewer frame, when exists, or NULL */ - EDA_3D_FRAME* Get3DViewerFrame(); + EDA_3D_VIEWER* Get3DViewerFrame(); /** * Function LoadFootprint diff --git a/include/wxPcbStruct.h b/include/wxPcbStruct.h index 010f6ec159..b1b2a0eaf5 100644 --- a/include/wxPcbStruct.h +++ b/include/wxPcbStruct.h @@ -991,10 +991,24 @@ public: double aXRef, double aYRef ); /** - * Function ExportToIDF3 + * Function OnExportIDF3 * will export the current BOARD to a IDFv3 board and lib files. */ - void ExportToIDF3( wxCommandEvent& event ); + void OnExportIDF3( wxCommandEvent& event ); + + /** + * Function Export_IDF3 + * Creates an IDF3 compliant BOARD (*.emn) and LIBRARY (*.emp) file. + * + * @param aPcb = a pointer to the board to be exported to IDF + * @param aFullFileName = the full filename of the export file + * @param aUseThou = set to true if the desired IDF unit is thou (mil) + * @param aXRef = the board Reference Point in mm, X value + * @param aYRef = the board Reference Point in mm, Y value + * @return true if OK + */ + bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, + bool aUseThou, double aXRef, double aYRef ); /** * Function ExporttoSPECCTRA diff --git a/pcbnew/basepcbframe.cpp b/pcbnew/basepcbframe.cpp index e95d2c68de..0dbaa33ae1 100644 --- a/pcbnew/basepcbframe.cpp +++ b/pcbnew/basepcbframe.cpp @@ -39,7 +39,7 @@ #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> // To include VIEWER3D_FRAMENAME #include #include @@ -121,10 +121,10 @@ PCB_BASE_FRAME::~PCB_BASE_FRAME() } -EDA_3D_FRAME* PCB_BASE_FRAME::Get3DViewerFrame() +EDA_3D_VIEWER* PCB_BASE_FRAME::Get3DViewerFrame() { // return the 3D viewer frame, when exists, or NULL - return dynamic_cast + return dynamic_cast ( wxWindow::FindWindowByName( VIEWER3D_FRAMENAME ) ); } diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 2e9570fef6..c1bab07900 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -131,7 +131,7 @@ void MODULE::TransformPadsShapesWithClearanceToPolygon( LAYER_ID aLayer, int aInflateValue, int aCircleToSegmentsCount, double aCorrectionFactor, - bool aSkipNPTHPadsWihNoCopper ) + bool aSkipNPTHPadsWihNoCopper ) const { D_PAD* pad = Pads(); @@ -206,7 +206,7 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet( int aInflateValue, int aCircleToSegmentsCount, double aCorrectionFactor, - int aCircleToSegmentsCountForTexts ) + int aCircleToSegmentsCountForTexts ) const { std::vector texts; // List of TEXTE_MODULE to convert EDGE_MODULE* outline; @@ -271,6 +271,75 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet( true, addTextSegmToPoly ); } +} + + +// Same as function TransformGraphicShapesWithClearanceToPolygonSet but +// this only render text +void MODULE::TransformGraphicTextWithClearanceToPolygonSet( + LAYER_ID aLayer, + SHAPE_POLY_SET& aCornerBuffer, + int aInflateValue, + int aCircleToSegmentsCount, + double aCorrectionFactor, + int aCircleToSegmentsCountForTexts ) const +{ + std::vector texts; // List of TEXTE_MODULE to convert + + for( EDA_ITEM* item = GraphicalItems(); item != NULL; item = item->Next() ) + { + switch( item->Type() ) + { + case PCB_MODULE_TEXT_T: + { + TEXTE_MODULE* text = static_cast( item ); + + if( text->GetLayer() == aLayer && text->IsVisible() ) + texts.push_back( text ); + + break; + } + + case PCB_MODULE_EDGE_T: + // This function does not render this + break; + + default: + break; + } + } + + // Convert texts sur modules + if( Reference().GetLayer() == aLayer && Reference().IsVisible() ) + texts.push_back( &Reference() ); + + if( Value().GetLayer() == aLayer && Value().IsVisible() ) + texts.push_back( &Value() ); + + s_cornerBuffer = &aCornerBuffer; + + // To allow optimization of circles approximated by segments, + // aCircleToSegmentsCountForTexts, when not 0, is used. + // if 0 (default value) the aCircleToSegmentsCount is used + s_textCircle2SegmentCount = aCircleToSegmentsCountForTexts ? + aCircleToSegmentsCountForTexts : aCircleToSegmentsCount; + + for( unsigned ii = 0; ii < texts.size(); ii++ ) + { + TEXTE_MODULE *textmod = texts[ii]; + s_textWidth = textmod->GetThickness() + ( 2 * aInflateValue ); + wxSize size = textmod->GetSize(); + + if( textmod->IsMirrored() ) + size.x = -size.x; + + DrawGraphicText( NULL, NULL, textmod->GetTextPosition(), BLACK, + textmod->GetShownText(), textmod->GetDrawRotation(), size, + textmod->GetHorizJustify(), textmod->GetVertJustify(), + textmod->GetThickness(), textmod->IsItalic(), + true, addTextSegmToPoly ); + } + } /* Function TransformSolidAreasShapesToPolygonSet @@ -286,7 +355,7 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet( void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet( SHAPE_POLY_SET& aCornerBuffer, int aCircleToSegmentsCount, - double aCorrectionFactor ) + double aCorrectionFactor ) const { if( GetFilledPolysList().IsEmpty() ) return; diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index 456aa0086c..97cc3b33fa 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -42,7 +42,6 @@ #include #include #include -#include <3d_struct.h> #include #include @@ -75,8 +74,7 @@ MODULE::MODULE( BOARD* parent ) : m_Reference = new TEXTE_MODULE( this, TEXTE_MODULE::TEXT_is_REFERENCE ); m_Value = new TEXTE_MODULE( this, TEXTE_MODULE::TEXT_is_VALUE ); - // Reserve one void 3D entry, to avoid problems with void list - m_3D_Drawings.PushBack( new S3D_MASTER( this ) ); + m_3D_Drawings.clear(); } @@ -141,19 +139,7 @@ MODULE::MODULE( const MODULE& aModule ) : } // Copy auxiliary data: 3D_Drawings info - for( S3D_MASTER* item = aModule.m_3D_Drawings; item; item = item->Next() ) - { - if( item->GetShape3DName().IsEmpty() ) // do not copy empty shapes. - continue; - - S3D_MASTER* t3d = new S3D_MASTER( this ); - t3d->Copy( item ); - m_3D_Drawings.PushBack( t3d ); - } - - // Ensure there is at least one item in m_3D_Drawings. - if( m_3D_Drawings.GetCount() == 0 ) - m_3D_Drawings.PushBack( new S3D_MASTER( this ) ); // push a void item + m_3D_Drawings = aModule.m_3D_Drawings; m_Doc = aModule.m_Doc; m_KeyWord = aModule.m_KeyWord; @@ -278,32 +264,10 @@ void MODULE::Copy( MODULE* aModule ) } // Copy auxiliary data: 3D_Drawings info - m_3D_Drawings.DeleteAll(); - - // Ensure there is one (or more) item in m_3D_Drawings - m_3D_Drawings.PushBack( new S3D_MASTER( this ) ); // push a void item - - for( S3D_MASTER* item = aModule->m_3D_Drawings; item; item = item->Next() ) - { - if( item->GetShape3DName().IsEmpty() ) // do not copy empty shapes. - continue; - - S3D_MASTER* t3d = m_3D_Drawings; - - if( t3d && t3d->GetShape3DName().IsEmpty() ) // The first entry can - { // exist, but is empty : use it. - t3d->Copy( item ); - } - else - { - t3d = new S3D_MASTER( this ); - t3d->Copy( item ); - m_3D_Drawings.PushBack( t3d ); - } - } - - m_Doc = aModule->m_Doc; - m_KeyWord = aModule->m_KeyWord; + m_3D_Drawings.clear(); + m_3D_Drawings = aModule->m_3D_Drawings; + m_Doc = aModule->m_Doc; + m_KeyWord = aModule->m_KeyWord; // Ensure auxiliary data is up to date CalculateBoundingBox(); @@ -628,16 +592,12 @@ void MODULE::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) aList.push_back( MSG_PANEL_ITEM( _( "Attributes" ), msg, BROWN ) ); aList.push_back( MSG_PANEL_ITEM( _( "Footprint" ), FROM_UTF8( m_fpid.Format().c_str() ), BLUE ) ); - msg = _( "No 3D shape" ); + if( m_3D_Drawings.empty() ) + msg = _( "No 3D shape" ); + else + msg = m_3D_Drawings.front().m_Filename; + // Search the first active 3D shape in list - for( S3D_MASTER* struct3D = m_3D_Drawings; struct3D; struct3D = struct3D->Next() ) - { - if( !struct3D->GetShape3DName().IsEmpty() ) - { - msg = struct3D->GetShape3DName(); - break; - } - } aList.push_back( MSG_PANEL_ITEM( _( "3D-Shape" ), msg, RED ) ); @@ -753,10 +713,15 @@ unsigned MODULE::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const } -void MODULE::Add3DModel( S3D_MASTER* a3DModel ) +void MODULE::Add3DModel( S3D_INFO* a3DModel ) { - a3DModel->SetParent( this ); - m_3D_Drawings.PushBack( a3DModel ); + if( NULL == a3DModel ) + return; + + if( !a3DModel->m_Filename.empty() ) + m_3D_Drawings.push_back( *a3DModel ); + + delete a3DModel; } diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index 255af845fc..edbedd6afc 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -32,6 +32,7 @@ #define MODULE_H_ +#include #include #include // ALL_LAYERS definition. #include @@ -40,12 +41,12 @@ #include #include #include "zones.h" +#include <3d_cache/3d_info.h> #include class LINE_READER; class EDA_3D_CANVAS; -class S3D_MASTER; class EDA_DRAW_PANEL; class D_PAD; class BOARD; @@ -154,8 +155,8 @@ public: DLIST& GraphicalItems() { return m_Drawings; } const DLIST& GraphicalItems() const { return m_Drawings; } - DLIST& Models() { return m_3D_Drawings; } - const DLIST& Models() const { return m_3D_Drawings; } + std::list& Models() { return m_3D_Drawings; } + const std::list& Models() const { return m_3D_Drawings; } void SetPosition( const wxPoint& aPos ); // was overload const wxPoint& GetPosition() const { return m_Pos; } // was overload @@ -343,7 +344,7 @@ public: int aInflateValue, int aCircleToSegmentsCount, double aCorrectionFactor, - bool aSkipNPTHPadsWihNoCopper = false ); + bool aSkipNPTHPadsWihNoCopper = false ) const; /** * function TransformGraphicShapesWithClearanceToPolygonSet @@ -371,7 +372,26 @@ public: int aInflateValue, int aCircleToSegmentsCount, double aCorrectionFactor, - int aCircleToSegmentsCountForTexts = 0 ); + int aCircleToSegmentsCountForTexts = 0 ) const; + + /** + * @brief TransformGraphicTextWithClearanceToPolygonSet + * This function is the same as TransformGraphicShapesWithClearanceToPolygonSet + * but only generate text + * @param aLayer + * @param aCornerBuffer + * @param aInflateValue + * @param aCircleToSegmentsCount + * @param aCorrectionFactor + * @param aCircleToSegmentsCountForTexts + */ + void TransformGraphicTextWithClearanceToPolygonSet( + LAYER_ID aLayer, + SHAPE_POLY_SET& aCornerBuffer, + int aInflateValue, + int aCircleToSegmentsCount, + double aCorrectionFactor, + int aCircleToSegmentsCountForTexts = 0 ) const; /** * Function DrawEdgesOnly @@ -527,9 +547,9 @@ public: * Function Add3DModel * adds \a a3DModel definition to the end of the 3D model list. * - * @param a3DModel A pointer to a #S3D_MASTER to add to the list. + * @param a3DModel A pointer to a #S3D_INFO to add to the list. */ - void Add3DModel( S3D_MASTER* a3DModel ); + void Add3DModel( S3D_INFO* a3DModel ); SEARCH_RESULT Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] ) override; @@ -640,7 +660,7 @@ public: private: DLIST m_Pads; ///< Linked list of pads. DLIST m_Drawings; ///< Linked list of graphical items. - DLIST m_3D_Drawings; ///< Linked list of 3D models. + std::list m_3D_Drawings; ///< Linked list of 3D models. double m_Orient; ///< Orientation in tenths of a degree, 900=90.0 degrees. wxPoint m_Pos; ///< Position of module on the board in internal units. TEXTE_MODULE* m_Reference; ///< Component reference designator value (U34, R18..) diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index 85e2bbd0e9..798e3f2499 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -261,7 +261,7 @@ public: */ void TransformSolidAreasShapesToPolygonSet( SHAPE_POLY_SET& aCornerBuffer, int aCircleToSegmentsCount, - double aCorrectionFactor ); + double aCorrectionFactor ) const; /** * Function BuildFilledSolidAreasPolygons * Build the filled solid areas data from real outlines (stored in m_Poly) diff --git a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp index bf1b07af09..8d58e55e29 100644 --- a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp +++ b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp @@ -5,6 +5,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * + * Copyright (C) 2016 Mario Luzeiro * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2015 Dick Hollenbeck, dick@softplc.com * Copyright (C) 2004-2016 KiCad Developers, see change_log.txt for contributors. @@ -36,7 +37,6 @@ #include #include #include -#include <3d_struct.h> #include <3d_viewer.h> #include #include @@ -55,6 +55,9 @@ size_t DIALOG_MODULE_BOARD_EDITOR::m_page = 0; // remember the last open page during session +wxBEGIN_EVENT_TABLE( DIALOG_MODULE_BOARD_EDITOR, wxDialog ) + EVT_CLOSE( DIALOG_MODULE_BOARD_EDITOR::OnCloseWindow ) +wxEND_EVENT_TABLE() DIALOG_MODULE_BOARD_EDITOR::DIALOG_MODULE_BOARD_EDITOR( PCB_EDIT_FRAME* aParent, MODULE* aModule, @@ -62,10 +65,15 @@ DIALOG_MODULE_BOARD_EDITOR::DIALOG_MODULE_BOARD_EDITOR( PCB_EDIT_FRAME* aParent DIALOG_MODULE_BOARD_EDITOR_BASE( aParent ), m_OrientValidator( 1, &m_OrientValue ) { + wxASSERT( aParent != NULL ); + wxASSERT( aModule != NULL ); + m_Parent = aParent; m_DC = aDC; m_CurrentModule = aModule; + m_currentModuleCopy = new MODULE( *aModule ); + // Give an icon wxIcon icon; icon.CopyFromBitmap( KiBitmap( icon_modedit_xpm ) ); @@ -76,7 +84,12 @@ DIALOG_MODULE_BOARD_EDITOR::DIALOG_MODULE_BOARD_EDITOR( PCB_EDIT_FRAME* aParent m_OrientValidator.SetWindow( m_OrientValueCtrl ); aParent->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() ); - m_PreviewPane = new PANEL_PREV_3D( m_Panel3D, aParent->Prj().Get3DCacheManager() ); + + m_PreviewPane = new PANEL_PREV_3D( m_Panel3D, + aParent->Prj().Get3DCacheManager(), + m_currentModuleCopy, + &m_shapes3D_list ); + bLowerSizer3D->Add( m_PreviewPane, 1, wxEXPAND, 5 ); m_NoteBook->SetSelection( m_page ); @@ -96,10 +109,7 @@ DIALOG_MODULE_BOARD_EDITOR::DIALOG_MODULE_BOARD_EDITOR( PCB_EDIT_FRAME* aParent DIALOG_MODULE_BOARD_EDITOR::~DIALOG_MODULE_BOARD_EDITOR() { - for( unsigned ii = 0; ii < m_Shapes3D_list.size(); ii++ ) - delete m_Shapes3D_list[ii]; - - m_Shapes3D_list.clear(); + m_shapes3D_list.clear(); // free the memory used by all models, otherwise models which were // browsed but not used would consume memory @@ -108,11 +118,28 @@ DIALOG_MODULE_BOARD_EDITOR::~DIALOG_MODULE_BOARD_EDITOR() // the GL canvas has to be visible before it is destroyed m_page = m_NoteBook->GetSelection(); m_NoteBook->SetSelection( 1 ); + + delete m_ReferenceCopy; + m_ReferenceCopy = NULL; + + delete m_ValueCopy; + m_ValueCopy = NULL; + delete m_PreviewPane; m_PreviewPane = NULL; // just in case, to avoid double-free - delete m_ReferenceCopy; - delete m_ValueCopy; + // this is already deleted by the board used on preview pane so + // no need to delete here + // delete m_currentModuleCopy; + // m_currentModuleCopy = NULL; +} + + +void DIALOG_MODULE_BOARD_EDITOR::OnCloseWindow( wxCloseEvent &event ) +{ + m_PreviewPane->Close(); + + event.Skip(); } @@ -272,32 +299,30 @@ void DIALOG_MODULE_BOARD_EDITOR::InitModeditProperties() // Init 3D shape list m_3D_ShapeNameListBox->Clear(); - S3D_MASTER* draw3D = m_CurrentModule->Models(); + std::list::iterator sM = m_CurrentModule->Models().begin(); + std::list::iterator eM = m_CurrentModule->Models().end(); + m_shapes3D_list.clear(); + wxString origPath; wxString alias; wxString shortPath; S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); - while( draw3D ) + while( sM != eM ) { - if( !draw3D->GetShape3DName().IsEmpty() ) + m_shapes3D_list.push_back( *sM ); + origPath = sM->m_Filename; + + if( res && res->SplitAlias( origPath, alias, shortPath ) ) { - S3D_MASTER* draw3DCopy = new S3D_MASTER( NULL ); - draw3DCopy->Copy( draw3D ); - m_Shapes3D_list.push_back( draw3DCopy ); - origPath = draw3DCopy->GetShape3DName(); - - if( res && res->SplitAlias( origPath, alias, shortPath ) ) - { - origPath = alias; - origPath.append( wxT( ":" ) ); - origPath.append( shortPath ); - } - - m_3D_ShapeNameListBox->Append( origPath ); + origPath = alias; + origPath.append( wxT( ":" ) ); + origPath.append( shortPath ); } - draw3D = (S3D_MASTER*) draw3D->Next(); + m_3D_ShapeNameListBox->Append( origPath ); + ++sM; + } m_ReferenceCopy = new TEXTE_MODULE( NULL ); @@ -363,15 +388,14 @@ void DIALOG_MODULE_BOARD_EDITOR::InitModeditProperties() { m_LastSelected3DShapeIndex = 0; m_3D_ShapeNameListBox->SetSelection( m_LastSelected3DShapeIndex ); - Transfert3DValuesToDisplay( m_Shapes3D_list[m_LastSelected3DShapeIndex] ); + + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_LastSelected3DShapeIndex, true ); } else { - S3D_INFO params; - params.scale.x = 1.0; - params.scale.y = 1.0; - params.scale.z = 1.0; - m_PreviewPane->SetModelData( ¶ms ); + if( m_PreviewPane ) + m_PreviewPane->ResetModelData( true ); } // We have modified the UI, so call Fit() for m_Panel3D @@ -380,113 +404,53 @@ void DIALOG_MODULE_BOARD_EDITOR::InitModeditProperties() } -/* Initialize 3D info displayed in dialog box from values in aStruct3DSource - */ -void DIALOG_MODULE_BOARD_EDITOR::Transfert3DValuesToDisplay( - S3D_MASTER* aStruct3DSource ) -{ - S3D_INFO params; - - if( aStruct3DSource ) - { - params.filename = aStruct3DSource->GetShape3DName(); - params.scale.x = aStruct3DSource->m_MatScale.x; - params.scale.y = aStruct3DSource->m_MatScale.y; - params.scale.z = aStruct3DSource->m_MatScale.z; - - params.offset.x = aStruct3DSource->m_MatPosition.x; - params.offset.y = aStruct3DSource->m_MatPosition.y; - params.offset.z = aStruct3DSource->m_MatPosition.z; - - params.rotation.x = aStruct3DSource->m_MatRotation.x; - params.rotation.y = aStruct3DSource->m_MatRotation.y; - params.rotation.z = aStruct3DSource->m_MatRotation.z; - } - else - { - params.scale.x = 1.0; - params.scale.y = 1.0; - params.scale.z = 1.0; - - params.offset.x = 0.0; - params.offset.y = 0.0; - params.offset.z = 0.0; - - params.rotation = params.offset; - } - - m_PreviewPane->SetModelData( ¶ms ); - return; -} - - -/** Copy 3D info displayed in dialog box to values in a item in m_Shapes3D_list - * @param aIndexSelection = item index in m_Shapes3D_list - */ -void DIALOG_MODULE_BOARD_EDITOR::TransfertDisplayTo3DValues( - int aIndexSelection ) -{ - if( aIndexSelection >= (int) m_Shapes3D_list.size() ) - return; - - S3D_MASTER* struct3DDest = m_Shapes3D_list[aIndexSelection]; - S3D_INFO params; - m_PreviewPane->GetModelData( ¶ms ); - - struct3DDest->m_MatScale.x = params.scale.x; - struct3DDest->m_MatScale.y = params.scale.y; - struct3DDest->m_MatScale.z = params.scale.z; - - struct3DDest->m_MatRotation.x = params.rotation.x; - struct3DDest->m_MatRotation.y = params.rotation.y; - struct3DDest->m_MatRotation.z = params.rotation.z; - - struct3DDest->m_MatPosition.x = params.offset.x; - struct3DDest->m_MatPosition.y = params.offset.y; - struct3DDest->m_MatPosition.z = params.offset.z; - - return; -} - - void DIALOG_MODULE_BOARD_EDITOR::On3DShapeNameSelected( wxCommandEvent& event ) { - if( m_LastSelected3DShapeIndex >= 0 ) - TransfertDisplayTo3DValues( m_LastSelected3DShapeIndex ); m_LastSelected3DShapeIndex = m_3D_ShapeNameListBox->GetSelection(); if( m_LastSelected3DShapeIndex < 0 ) // happens under wxGTK when // deleting an item in // m_3D_ShapeNameListBox wxListBox - return; - - if( m_LastSelected3DShapeIndex >= (int) m_Shapes3D_list.size() ) { - wxMessageBox( wxT( "On3DShapeNameSelected() error" ) ); - m_LastSelected3DShapeIndex = -1; + if( m_PreviewPane ) + m_PreviewPane->ResetModelData(); + return; } - Transfert3DValuesToDisplay( m_Shapes3D_list[m_LastSelected3DShapeIndex] ); + if( m_LastSelected3DShapeIndex >= (int) m_shapes3D_list.size() ) + { + wxMessageBox( wxT( "On3DShapeNameSelected() error" ) ); + m_LastSelected3DShapeIndex = -1; + + if( m_PreviewPane ) + m_PreviewPane->ResetModelData(); + + return; + } + + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_LastSelected3DShapeIndex ); } void DIALOG_MODULE_BOARD_EDITOR::Remove3DShape( wxCommandEvent& event ) { - if( m_LastSelected3DShapeIndex >= 0 ) - TransfertDisplayTo3DValues( m_LastSelected3DShapeIndex ); - int ii = m_3D_ShapeNameListBox->GetSelection(); - if( ii < 0 ) - return; - m_Shapes3D_list.erase( m_Shapes3D_list.begin() + ii ); + if( ii < 0 ) + { + if( m_PreviewPane ) + m_PreviewPane->ResetModelData( true ); + + return; + } + + m_shapes3D_list.erase( m_shapes3D_list.begin() + ii ); m_3D_ShapeNameListBox->Delete( ii ); - if( m_3D_ShapeNameListBox->GetCount() == 0 ) - Transfert3DValuesToDisplay( NULL ); - else + if( m_3D_ShapeNameListBox->GetCount() > 0 ) { if( ii > 0 ) m_LastSelected3DShapeIndex = ii - 1; @@ -494,8 +458,14 @@ void DIALOG_MODULE_BOARD_EDITOR::Remove3DShape( wxCommandEvent& event ) m_LastSelected3DShapeIndex = 0; m_3D_ShapeNameListBox->SetSelection( m_LastSelected3DShapeIndex ); - Transfert3DValuesToDisplay( - m_Shapes3D_list[m_LastSelected3DShapeIndex] ); + + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_LastSelected3DShapeIndex, true ); + } + else + { + if( m_PreviewPane ) + m_PreviewPane->ResetModelData( true ); } return; @@ -509,9 +479,6 @@ void DIALOG_MODULE_BOARD_EDITOR::Edit3DShapeFileName() if( idx < 0 ) return; - // ensure any updated parameters are not discarded - TransfertDisplayTo3DValues( idx ); - // Edit filename wxString filename = m_3D_ShapeNameListBox->GetStringSelection(); wxTextEntryDialog dlg( this, wxEmptyString, wxEmptyString, filename ); @@ -547,15 +514,11 @@ void DIALOG_MODULE_BOARD_EDITOR::Edit3DShapeFileName() filename.Replace( wxT( "\\" ), wxT( "/" ) ); #endif - S3D_MASTER* new3DShape = new S3D_MASTER( NULL ); - new3DShape->SetShape3DName( filename ); - new3DShape->m_MatPosition = m_Shapes3D_list[idx]->m_MatPosition; - new3DShape->m_MatRotation = m_Shapes3D_list[idx]->m_MatRotation; - new3DShape->m_MatScale = m_Shapes3D_list[idx]->m_MatScale; - delete m_Shapes3D_list[idx]; - m_Shapes3D_list[idx] = new3DShape; + m_shapes3D_list[idx].m_Filename = filename; - Transfert3DValuesToDisplay( m_Shapes3D_list[idx] ); + // This assumes that the index didn't change and will just update the filename + if( m_PreviewPane ) + m_PreviewPane->UpdateModelName( filename ); return; } @@ -580,7 +543,7 @@ void DIALOG_MODULE_BOARD_EDITOR::BrowseAndAdd3DShapeFile() } if( !S3D::Select3DModel( this, Prj().Get3DCacheManager(), - initialpath, filter, &model ) || model.filename.empty() ) + initialpath, filter, &model ) || model.m_Filename.empty() ) { return; } @@ -588,41 +551,33 @@ void DIALOG_MODULE_BOARD_EDITOR::BrowseAndAdd3DShapeFile() prj.SetRString( PROJECT::VIEWER_3D_PATH, initialpath ); sidx = wxString::Format( wxT( "%i" ), filter ); prj.SetRString( PROJECT::VIEWER_3D_FILTER_INDEX, sidx ); - wxString origPath = model.filename; + S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); wxString alias; wxString shortPath; - S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); + wxString filename = model.m_Filename; - if( res && res->SplitAlias( origPath, alias, shortPath ) ) + if( res && res->SplitAlias( filename, alias, shortPath ) ) { - origPath = alias; - origPath.append( wxT( ":" ) ); - origPath.append( shortPath ); + alias.Append( wxT( ":" ) ); + alias.Append( shortPath ); + m_3D_ShapeNameListBox->Append( alias ); + } + else + { + m_3D_ShapeNameListBox->Append( filename ); } - - m_3D_ShapeNameListBox->Append( origPath ); #ifdef __WINDOWS__ // In Kicad files, filenames and paths are stored using Unix notation - model.filename.Replace( wxT( "\\" ), wxT( "/" ) ); + model.m_Filename.Replace( wxT( "\\" ), wxT( "/" ) ); #endif - S3D_MASTER* new3DShape = new S3D_MASTER( NULL ); - new3DShape->SetShape3DName( model.filename ); - new3DShape->m_MatScale.x = model.scale.x; - new3DShape->m_MatScale.y = model.scale.y; - new3DShape->m_MatScale.z = model.scale.z; - new3DShape->m_MatRotation.x = model.rotation.x; - new3DShape->m_MatRotation.y = model.rotation.y; - new3DShape->m_MatRotation.z = model.rotation.z; - new3DShape->m_MatPosition.x = model.offset.x; - new3DShape->m_MatPosition.y = model.offset.y; - new3DShape->m_MatPosition.z = model.offset.z; - - m_Shapes3D_list.push_back( new3DShape ); + m_shapes3D_list.push_back( model ); m_LastSelected3DShapeIndex = m_3D_ShapeNameListBox->GetCount() - 1; m_3D_ShapeNameListBox->SetSelection( m_LastSelected3DShapeIndex ); - Transfert3DValuesToDisplay( m_Shapes3D_list[m_LastSelected3DShapeIndex] ); + + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_LastSelected3DShapeIndex, true ) ; return; } @@ -759,48 +714,10 @@ bool DIALOG_MODULE_BOARD_EDITOR::TransferDataFromWindow() if( change_layer ) m_CurrentModule->Flip( m_CurrentModule->GetPosition() ); - // Update 3D shape list - int idx = m_3D_ShapeNameListBox->GetSelection(); - - if( idx >= 0 ) - TransfertDisplayTo3DValues( idx ); - - S3D_MASTER* draw3D = m_CurrentModule->Models(); - - for( unsigned ii = 0; ii < m_Shapes3D_list.size(); ii++ ) - { - S3D_MASTER* draw3DCopy = m_Shapes3D_list[ii]; - - if( draw3DCopy->GetShape3DName().IsEmpty() ) - continue; - - if( draw3D == NULL ) - { - draw3D = new S3D_MASTER( draw3D ); - m_CurrentModule->Models().Append( draw3D ); - } - - draw3D->SetShape3DName( draw3DCopy->GetShape3DName() ); - draw3D->m_MatScale = draw3DCopy->m_MatScale; - draw3D->m_MatRotation = draw3DCopy->m_MatRotation; - draw3D->m_MatPosition = draw3DCopy->m_MatPosition; - - draw3D = draw3D->Next(); - } - - // Remove old extra 3D shapes - S3D_MASTER* nextdraw3D; - - for( ; draw3D != NULL; draw3D = nextdraw3D ) - { - nextdraw3D = (S3D_MASTER*) draw3D->Next(); - delete m_CurrentModule->Models().Remove( draw3D ); - } - - // Fill shape list with one void entry, if no entry - if( m_CurrentModule->Models() == NULL ) - m_CurrentModule->Models().PushBack( new S3D_MASTER( m_CurrentModule ) ); - + // This will update the S3D_INFO list into the current module + std::list* draw3D = &m_CurrentModule->Models(); + draw3D->clear(); + draw3D->insert( draw3D->end(), m_shapes3D_list.begin(), m_shapes3D_list.end() ); m_CurrentModule->CalculateBoundingBox(); @@ -844,5 +761,8 @@ void DIALOG_MODULE_BOARD_EDITOR::OnEditValue( wxCommandEvent& event ) void DIALOG_MODULE_BOARD_EDITOR::Cfg3DPath( wxCommandEvent& event ) { - S3D::Configure3DPaths( this, Prj().Get3DCacheManager()->GetResolver() ); + if( S3D::Configure3DPaths( this, Prj().Get3DCacheManager()->GetResolver() ) ) + if( m_LastSelected3DShapeIndex >= 0 ) + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_LastSelected3DShapeIndex, true ); } diff --git a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp.orig b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp.orig new file mode 100644 index 0000000000..bf1b07af09 --- /dev/null +++ b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.cpp.orig @@ -0,0 +1,848 @@ +/** + * Module editor: Dialog for editing module properties in the pcb editor. + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2015 Dick Hollenbeck, dick@softplc.com + * Copyright (C) 2004-2016 KiCad Developers, see change_log.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include <3d_struct.h> +#include <3d_viewer.h> +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "3d_cache/dialogs/panel_prev_model.h" +#include "3d_cache/dialogs/3d_cache_dialogs.h" +#include "3d_cache/3d_cache.h" +#include "3d_cache/3d_filename_resolver.h" + +size_t DIALOG_MODULE_BOARD_EDITOR::m_page = 0; // remember the last open page during session + + +DIALOG_MODULE_BOARD_EDITOR::DIALOG_MODULE_BOARD_EDITOR( PCB_EDIT_FRAME* aParent, + MODULE* aModule, + wxDC* aDC ) : + DIALOG_MODULE_BOARD_EDITOR_BASE( aParent ), + m_OrientValidator( 1, &m_OrientValue ) +{ + m_Parent = aParent; + m_DC = aDC; + m_CurrentModule = aModule; + + // Give an icon + wxIcon icon; + icon.CopyFromBitmap( KiBitmap( icon_modedit_xpm ) ); + SetIcon( icon ); + + m_OrientValidator.SetRange( -360.0, 360.0 ); + m_OrientValueCtrl->SetValidator( m_OrientValidator ); + m_OrientValidator.SetWindow( m_OrientValueCtrl ); + + aParent->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() ); + m_PreviewPane = new PANEL_PREV_3D( m_Panel3D, aParent->Prj().Get3DCacheManager() ); + bLowerSizer3D->Add( m_PreviewPane, 1, wxEXPAND, 5 ); + + m_NoteBook->SetSelection( m_page ); + m_sdbSizerStdButtonsOK->SetDefault(); + + m_ReferenceCopy = NULL; + m_ValueCopy = NULL; + m_LastSelected3DShapeIndex = 0; + m_OrientValue = 0; + + Layout(); + + FixOSXCancelButtonIssue(); +} + + + +DIALOG_MODULE_BOARD_EDITOR::~DIALOG_MODULE_BOARD_EDITOR() +{ + for( unsigned ii = 0; ii < m_Shapes3D_list.size(); ii++ ) + delete m_Shapes3D_list[ii]; + + m_Shapes3D_list.clear(); + + // free the memory used by all models, otherwise models which were + // browsed but not used would consume memory + Prj().Get3DCacheManager()->FlushCache( false ); + + // the GL canvas has to be visible before it is destroyed + m_page = m_NoteBook->GetSelection(); + m_NoteBook->SetSelection( 1 ); + delete m_PreviewPane; + m_PreviewPane = NULL; // just in case, to avoid double-free + + delete m_ReferenceCopy; + delete m_ValueCopy; +} + + +// Creation of the panel properties of the module editor. +void DIALOG_MODULE_BOARD_EDITOR::InitBoardProperties() +{ + PutValueInLocalUnits( *m_ModPositionX, m_CurrentModule->GetPosition().x ); + m_XPosUnit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + + PutValueInLocalUnits( *m_ModPositionY, m_CurrentModule->GetPosition().y ); + m_YPosUnit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + + m_LayerCtrl->SetSelection( + (m_CurrentModule->GetLayer() == B_Cu) ? 1 : 0 ); + + bool custom_orientation = false; + switch( int( m_CurrentModule->GetOrientation() ) ) + { + case 0: + m_OrientCtrl->SetSelection( 0 ); + break; + + case 900: + case -2700: + m_OrientCtrl->SetSelection( 1 ); + break; + + case -900: + case 2700: + m_OrientCtrl->SetSelection( 2 ); + break; + + case -1800: + case 1800: + m_OrientCtrl->SetSelection( 3 ); + break; + + default: + m_OrientCtrl->SetSelection( 4 ); + custom_orientation = true; + break; + } + + m_OrientValueCtrl->Enable( custom_orientation ); + m_OrientValue = m_CurrentModule->GetOrientation() / 10.0; + m_OrientValidator.TransferToWindow(); + + // Initialize dialog relative to masks clearances + m_NetClearanceUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + m_SolderMaskMarginUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + m_SolderPasteMarginUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + + PutValueInLocalUnits( *m_NetClearanceValueCtrl, m_CurrentModule->GetLocalClearance() ); + PutValueInLocalUnits( *m_SolderMaskMarginCtrl, m_CurrentModule->GetLocalSolderMaskMargin() ); + + // These 2 parameters are usually < 0, so prepare entering a negative + // value, if current is 0 + PutValueInLocalUnits( *m_SolderPasteMarginCtrl, m_CurrentModule->GetLocalSolderPasteMargin() ); + + if( m_CurrentModule->GetLocalSolderPasteMargin() == 0 ) + m_SolderPasteMarginCtrl->SetValue( wxT( "-" ) + + m_SolderPasteMarginCtrl->GetValue() ); + + // Add solder paste margin ration in per cent + // for the usual default value 0.0, display -0.0 (or -0,0 in some countries) + wxString msg; + msg.Printf( wxT( "%f" ), + m_CurrentModule->GetLocalSolderPasteMarginRatio() * 100.0 ); + + if( m_CurrentModule->GetLocalSolderPasteMarginRatio() == 0.0 && + msg[0] == '0') // Sometimes Printf adds a sign if the value is very small (0.0) + m_SolderPasteMarginRatioCtrl->SetValue( wxT("-") + msg ); + else + m_SolderPasteMarginRatioCtrl->SetValue( msg ); + + switch( m_CurrentModule->GetZoneConnection() ) + { + default: + case PAD_ZONE_CONN_INHERITED: + m_ZoneConnectionChoice->SetSelection( 0 ); + break; + + case PAD_ZONE_CONN_FULL: + m_ZoneConnectionChoice->SetSelection( 1 ); + break; + + case PAD_ZONE_CONN_THERMAL: + m_ZoneConnectionChoice->SetSelection( 2 ); + break; + + case PAD_ZONE_CONN_NONE: + m_ZoneConnectionChoice->SetSelection( 3 ); + break; + } +} + + +void DIALOG_MODULE_BOARD_EDITOR::GotoModuleEditor( wxCommandEvent& event ) +{ + if( m_CurrentModule->GetTimeStamp() == 0 ) // Module Editor needs a non null timestamp + { + m_CurrentModule->SetTimeStamp( GetNewTimeStamp() ); + m_Parent->OnModify(); + } + + EndModal( PRM_EDITOR_WANT_MODEDIT ); +} + + +void DIALOG_MODULE_BOARD_EDITOR::ExchangeModule( wxCommandEvent& event ) +{ + EndModal( PRM_EDITOR_WANT_EXCHANGE_FP ); +} + + +void DIALOG_MODULE_BOARD_EDITOR::ModuleOrientEvent( wxCommandEvent& event ) +{ + bool custom_orientation = false; + + switch( m_OrientCtrl->GetSelection() ) + { + case 0: + m_OrientValue = 0.0; + break; + + case 1: + m_OrientValue = 90.0; + break; + + case 2: + m_OrientValue = 270.0; + break; + + case 3: + m_OrientValue = 180.0; + break; + + default: + custom_orientation = true; + break; + } + + m_OrientValidator.TransferToWindow(); + m_OrientValueCtrl->Enable( custom_orientation ); +} + + +void DIALOG_MODULE_BOARD_EDITOR::InitModeditProperties() +{ + wxString default_path; + wxGetEnv( KISYS3DMOD, &default_path ); +#ifdef __WINDOWS__ + default_path.Replace( wxT( "/" ), wxT( "\\" ) ); +#endif + + m_LastSelected3DShapeIndex = -1; + + // Init 3D shape list + m_3D_ShapeNameListBox->Clear(); + S3D_MASTER* draw3D = m_CurrentModule->Models(); + wxString origPath; + wxString alias; + wxString shortPath; + S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); + + while( draw3D ) + { + if( !draw3D->GetShape3DName().IsEmpty() ) + { + S3D_MASTER* draw3DCopy = new S3D_MASTER( NULL ); + draw3DCopy->Copy( draw3D ); + m_Shapes3D_list.push_back( draw3DCopy ); + origPath = draw3DCopy->GetShape3DName(); + + if( res && res->SplitAlias( origPath, alias, shortPath ) ) + { + origPath = alias; + origPath.append( wxT( ":" ) ); + origPath.append( shortPath ); + } + + m_3D_ShapeNameListBox->Append( origPath ); + } + + draw3D = (S3D_MASTER*) draw3D->Next(); + } + + m_ReferenceCopy = new TEXTE_MODULE( NULL ); + m_ValueCopy = new TEXTE_MODULE( NULL ); + m_ReferenceCopy->Copy( &m_CurrentModule->Reference() ); + m_ValueCopy->Copy( &m_CurrentModule->Value() ); + m_ReferenceCtrl->SetValue( m_ReferenceCopy->GetText() ); + m_ValueCtrl->SetValue( m_ValueCopy->GetText() ); + + // Shows the footprint's schematic path. + m_textCtrlSheetPath->SetValue( m_CurrentModule->GetPath() ); + + m_AttributsCtrl->SetItemToolTip( 0, + _( "Use this attribute for most non SMD components\n" + "Components with this option are not put in the footprint position list file" ) ); + m_AttributsCtrl->SetItemToolTip( 1, + _( "Use this attribute for SMD components.\n" + "Only components with this option are put in the footprint position list file" ) ); + m_AttributsCtrl->SetItemToolTip( 2, + _( "Use this attribute for \"virtual\" components drawn on board\n" + "(like a old ISA PC bus connector)" ) ); + + // Controls on right side of the dialog + switch( m_CurrentModule->GetAttributes() & 255 ) + { + case 0: + m_AttributsCtrl->SetSelection( 0 ); + break; + + case MOD_CMS: + m_AttributsCtrl->SetSelection( 1 ); + break; + + case MOD_VIRTUAL: + m_AttributsCtrl->SetSelection( 2 ); + break; + + default: + m_AttributsCtrl->SetSelection( 0 ); + break; + } + + if( m_CurrentModule->IsLocked() ) + m_AutoPlaceCtrl->SetSelection( 2 ); + else if( m_CurrentModule->PadsLocked() ) + m_AutoPlaceCtrl->SetSelection( 1 ); + else + m_AutoPlaceCtrl->SetSelection( 0 ); + + m_AutoPlaceCtrl->SetItemToolTip( 0, + _( "Component can be freely moved and auto placed. User can arbitrarily select and edit component's pads." ) ); + m_AutoPlaceCtrl->SetItemToolTip( 1, + _( "Component can be freely moved and auto placed, but its pads cannot be selected or edited." ) ); + m_AutoPlaceCtrl->SetItemToolTip( 2, + _( "Component is locked: it cannot be freely moved or auto placed." ) ); + + m_CostRot90Ctrl->SetValue( m_CurrentModule->GetPlacementCost90() ); + + m_CostRot180Ctrl->SetValue( m_CurrentModule->GetPlacementCost180() ); + + // if m_3D_ShapeNameListBox is not empty, preselect first 3D shape + if( m_3D_ShapeNameListBox->GetCount() > 0 ) + { + m_LastSelected3DShapeIndex = 0; + m_3D_ShapeNameListBox->SetSelection( m_LastSelected3DShapeIndex ); + Transfert3DValuesToDisplay( m_Shapes3D_list[m_LastSelected3DShapeIndex] ); + } + else + { + S3D_INFO params; + params.scale.x = 1.0; + params.scale.y = 1.0; + params.scale.z = 1.0; + m_PreviewPane->SetModelData( ¶ms ); + } + + // We have modified the UI, so call Fit() for m_Panel3D + // to be sure the m_Panel3D sizers are initiliazed before opening the dialog + m_Panel3D->GetSizer()->Fit( m_Panel3D ); +} + + +/* Initialize 3D info displayed in dialog box from values in aStruct3DSource + */ +void DIALOG_MODULE_BOARD_EDITOR::Transfert3DValuesToDisplay( + S3D_MASTER* aStruct3DSource ) +{ + S3D_INFO params; + + if( aStruct3DSource ) + { + params.filename = aStruct3DSource->GetShape3DName(); + params.scale.x = aStruct3DSource->m_MatScale.x; + params.scale.y = aStruct3DSource->m_MatScale.y; + params.scale.z = aStruct3DSource->m_MatScale.z; + + params.offset.x = aStruct3DSource->m_MatPosition.x; + params.offset.y = aStruct3DSource->m_MatPosition.y; + params.offset.z = aStruct3DSource->m_MatPosition.z; + + params.rotation.x = aStruct3DSource->m_MatRotation.x; + params.rotation.y = aStruct3DSource->m_MatRotation.y; + params.rotation.z = aStruct3DSource->m_MatRotation.z; + } + else + { + params.scale.x = 1.0; + params.scale.y = 1.0; + params.scale.z = 1.0; + + params.offset.x = 0.0; + params.offset.y = 0.0; + params.offset.z = 0.0; + + params.rotation = params.offset; + } + + m_PreviewPane->SetModelData( ¶ms ); + return; +} + + +/** Copy 3D info displayed in dialog box to values in a item in m_Shapes3D_list + * @param aIndexSelection = item index in m_Shapes3D_list + */ +void DIALOG_MODULE_BOARD_EDITOR::TransfertDisplayTo3DValues( + int aIndexSelection ) +{ + if( aIndexSelection >= (int) m_Shapes3D_list.size() ) + return; + + S3D_MASTER* struct3DDest = m_Shapes3D_list[aIndexSelection]; + S3D_INFO params; + m_PreviewPane->GetModelData( ¶ms ); + + struct3DDest->m_MatScale.x = params.scale.x; + struct3DDest->m_MatScale.y = params.scale.y; + struct3DDest->m_MatScale.z = params.scale.z; + + struct3DDest->m_MatRotation.x = params.rotation.x; + struct3DDest->m_MatRotation.y = params.rotation.y; + struct3DDest->m_MatRotation.z = params.rotation.z; + + struct3DDest->m_MatPosition.x = params.offset.x; + struct3DDest->m_MatPosition.y = params.offset.y; + struct3DDest->m_MatPosition.z = params.offset.z; + + return; +} + + +void DIALOG_MODULE_BOARD_EDITOR::On3DShapeNameSelected( wxCommandEvent& event ) +{ + if( m_LastSelected3DShapeIndex >= 0 ) + TransfertDisplayTo3DValues( m_LastSelected3DShapeIndex ); + m_LastSelected3DShapeIndex = m_3D_ShapeNameListBox->GetSelection(); + + if( m_LastSelected3DShapeIndex < 0 ) // happens under wxGTK when + // deleting an item in + // m_3D_ShapeNameListBox wxListBox + return; + + if( m_LastSelected3DShapeIndex >= (int) m_Shapes3D_list.size() ) + { + wxMessageBox( wxT( "On3DShapeNameSelected() error" ) ); + m_LastSelected3DShapeIndex = -1; + return; + } + + Transfert3DValuesToDisplay( m_Shapes3D_list[m_LastSelected3DShapeIndex] ); +} + + + +void DIALOG_MODULE_BOARD_EDITOR::Remove3DShape( wxCommandEvent& event ) +{ + if( m_LastSelected3DShapeIndex >= 0 ) + TransfertDisplayTo3DValues( m_LastSelected3DShapeIndex ); + + int ii = m_3D_ShapeNameListBox->GetSelection(); + if( ii < 0 ) + return; + + m_Shapes3D_list.erase( m_Shapes3D_list.begin() + ii ); + m_3D_ShapeNameListBox->Delete( ii ); + + if( m_3D_ShapeNameListBox->GetCount() == 0 ) + Transfert3DValuesToDisplay( NULL ); + else + { + if( ii > 0 ) + m_LastSelected3DShapeIndex = ii - 1; + else + m_LastSelected3DShapeIndex = 0; + + m_3D_ShapeNameListBox->SetSelection( m_LastSelected3DShapeIndex ); + Transfert3DValuesToDisplay( + m_Shapes3D_list[m_LastSelected3DShapeIndex] ); + } + + return; +} + + +void DIALOG_MODULE_BOARD_EDITOR::Edit3DShapeFileName() +{ + int idx = m_3D_ShapeNameListBox->GetSelection(); + + if( idx < 0 ) + return; + + // ensure any updated parameters are not discarded + TransfertDisplayTo3DValues( idx ); + + // Edit filename + wxString filename = m_3D_ShapeNameListBox->GetStringSelection(); + wxTextEntryDialog dlg( this, wxEmptyString, wxEmptyString, filename ); + + bool hasAlias; + S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); + + if( dlg.ShowModal() != wxID_OK ) + return; + + filename = dlg.GetValue(); + + if( filename.empty() ) + return; + + if( !res->ValidateFileName( filename, hasAlias ) ) + { + wxString msg = _( "Invalid filename: " ); + msg.append( filename ); + wxMessageBox( msg, _T( "Edit 3D file name" ) ); + + return; + } + + m_3D_ShapeNameListBox->SetString( idx, filename ); + + // if the user has specified an alias in the name then prepend ':' + if( hasAlias ) + filename.insert( 0, wxT( ":" ) ); + + #ifdef __WINDOWS__ + // In Kicad files, filenames and paths are stored using Unix notation + filename.Replace( wxT( "\\" ), wxT( "/" ) ); + #endif + + S3D_MASTER* new3DShape = new S3D_MASTER( NULL ); + new3DShape->SetShape3DName( filename ); + new3DShape->m_MatPosition = m_Shapes3D_list[idx]->m_MatPosition; + new3DShape->m_MatRotation = m_Shapes3D_list[idx]->m_MatRotation; + new3DShape->m_MatScale = m_Shapes3D_list[idx]->m_MatScale; + delete m_Shapes3D_list[idx]; + m_Shapes3D_list[idx] = new3DShape; + + Transfert3DValuesToDisplay( m_Shapes3D_list[idx] ); + + return; +} + + +void DIALOG_MODULE_BOARD_EDITOR::BrowseAndAdd3DShapeFile() +{ + PROJECT& prj = Prj(); + S3D_INFO model; + + wxString initialpath = prj.GetRString( PROJECT::VIEWER_3D_PATH ); + wxString sidx = prj.GetRString( PROJECT::VIEWER_3D_FILTER_INDEX ); + int filter = 0; + + if( !sidx.empty() ) + { + long tmp; + sidx.ToLong( &tmp ); + + if( tmp > 0 && tmp <= 0x7FFFFFFF ) + filter = (int) tmp; + } + + if( !S3D::Select3DModel( this, Prj().Get3DCacheManager(), + initialpath, filter, &model ) || model.filename.empty() ) + { + return; + } + + prj.SetRString( PROJECT::VIEWER_3D_PATH, initialpath ); + sidx = wxString::Format( wxT( "%i" ), filter ); + prj.SetRString( PROJECT::VIEWER_3D_FILTER_INDEX, sidx ); + wxString origPath = model.filename; + wxString alias; + wxString shortPath; + S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); + + if( res && res->SplitAlias( origPath, alias, shortPath ) ) + { + origPath = alias; + origPath.append( wxT( ":" ) ); + origPath.append( shortPath ); + } + + m_3D_ShapeNameListBox->Append( origPath ); + +#ifdef __WINDOWS__ + // In Kicad files, filenames and paths are stored using Unix notation + model.filename.Replace( wxT( "\\" ), wxT( "/" ) ); +#endif + + S3D_MASTER* new3DShape = new S3D_MASTER( NULL ); + new3DShape->SetShape3DName( model.filename ); + new3DShape->m_MatScale.x = model.scale.x; + new3DShape->m_MatScale.y = model.scale.y; + new3DShape->m_MatScale.z = model.scale.z; + new3DShape->m_MatRotation.x = model.rotation.x; + new3DShape->m_MatRotation.y = model.rotation.y; + new3DShape->m_MatRotation.z = model.rotation.z; + new3DShape->m_MatPosition.x = model.offset.x; + new3DShape->m_MatPosition.y = model.offset.y; + new3DShape->m_MatPosition.z = model.offset.z; + + m_Shapes3D_list.push_back( new3DShape ); + m_LastSelected3DShapeIndex = m_3D_ShapeNameListBox->GetCount() - 1; + m_3D_ShapeNameListBox->SetSelection( m_LastSelected3DShapeIndex ); + Transfert3DValuesToDisplay( m_Shapes3D_list[m_LastSelected3DShapeIndex] ); + + return; +} + + +bool DIALOG_MODULE_BOARD_EDITOR::TransferDataToWindow() +{ + if( !wxDialog::TransferDataToWindow() ) + return false; + + if( !m_PanelProperties->TransferDataToWindow() ) + return false; + if( !m_Panel3D->TransferDataToWindow() ) + return false; + + InitModeditProperties(); + InitBoardProperties(); + + return true; +} + + +bool DIALOG_MODULE_BOARD_EDITOR::TransferDataFromWindow() +{ + wxPoint modpos; + wxString msg; + + if( !Validate() || !DIALOG_MODULE_BOARD_EDITOR_BASE::TransferDataFromWindow() ) + return false; + + if( !m_PanelProperties->TransferDataFromWindow() ) + return false; + if( !m_Panel3D->TransferDataFromWindow() ) + return false; + + if( m_CurrentModule->GetFlags() == 0 ) // this is a simple edition, we + // must create an undo entry + m_Parent->SaveCopyInUndoList( m_CurrentModule, UR_CHANGED ); + + if( m_DC ) + { + m_Parent->GetCanvas()->CrossHairOff( m_DC ); + m_CurrentModule->Draw( m_Parent->GetCanvas(), m_DC, GR_XOR ); + } + + // Init Fields (should be first, because they can be moved or/and flipped later): + m_CurrentModule->Reference().Copy( m_ReferenceCopy ); + m_CurrentModule->Value().Copy( m_ValueCopy ); + + // Initialize masks clearances + m_CurrentModule->SetLocalClearance( ValueFromTextCtrl( *m_NetClearanceValueCtrl ) ); + m_CurrentModule->SetLocalSolderMaskMargin( ValueFromTextCtrl( *m_SolderMaskMarginCtrl ) ); + m_CurrentModule->SetLocalSolderPasteMargin( ValueFromTextCtrl( *m_SolderPasteMarginCtrl ) ); + + double dtmp = 0.0; + msg = m_SolderPasteMarginRatioCtrl->GetValue(); + msg.ToDouble( &dtmp ); + + // A -50% margin ratio means no paste on a pad, the ratio must be >= -50% + if( dtmp < -50.0 ) + dtmp = -50.0; + // A margin ratio is always <= 0 + // 0 means use full pad copper area + if( dtmp > 0.0 ) + dtmp = 0.0; + + m_CurrentModule->SetLocalSolderPasteMarginRatio( dtmp / 100 ); + + switch( m_ZoneConnectionChoice->GetSelection() ) + { + default: + case 0: + m_CurrentModule->SetZoneConnection( PAD_ZONE_CONN_INHERITED ); + break; + + case 1: + m_CurrentModule->SetZoneConnection( PAD_ZONE_CONN_FULL ); + break; + + case 2: + m_CurrentModule->SetZoneConnection( PAD_ZONE_CONN_THERMAL ); + break; + + case 3: + m_CurrentModule->SetZoneConnection( PAD_ZONE_CONN_NONE ); + break; + } + + // Set Module Position + modpos.x = ValueFromTextCtrl( *m_ModPositionX ); + modpos.y = ValueFromTextCtrl( *m_ModPositionY ); + m_CurrentModule->SetPosition( modpos ); + m_CurrentModule->SetLocked( m_AutoPlaceCtrl->GetSelection() == 2 ); + m_CurrentModule->SetPadsLocked( m_AutoPlaceCtrl->GetSelection() == 1 ); + + switch( m_AttributsCtrl->GetSelection() ) + { + case 0: + m_CurrentModule->SetAttributes( 0 ); + break; + + case 1: + m_CurrentModule->SetAttributes( MOD_CMS ); + break; + + case 2: + m_CurrentModule->SetAttributes( MOD_VIRTUAL ); + break; + } + + m_CurrentModule->SetPlacementCost90( m_CostRot90Ctrl->GetValue() ); + m_CurrentModule->SetPlacementCost180( m_CostRot180Ctrl->GetValue() ); + + /* Now, set orientation. must be made after others changes, + * because rotation changes fields positions on board according to the new orientation + * (relative positions are not modified) + */ + int orient = KiROUND( m_OrientValue * 10.0 ); + + if( m_CurrentModule->GetOrientation() != orient ) + m_CurrentModule->Rotate( m_CurrentModule->GetPosition(), + orient - m_CurrentModule->GetOrientation() ); + + // Set component side, that also have effect on the fields positions on board + bool change_layer = false; + if( m_LayerCtrl->GetSelection() == 0 ) // layer req = COMPONENT + { + if( m_CurrentModule->GetLayer() == B_Cu ) + change_layer = true; + } + else if( m_CurrentModule->GetLayer() == F_Cu ) + change_layer = true; + + if( change_layer ) + m_CurrentModule->Flip( m_CurrentModule->GetPosition() ); + + // Update 3D shape list + int idx = m_3D_ShapeNameListBox->GetSelection(); + + if( idx >= 0 ) + TransfertDisplayTo3DValues( idx ); + + S3D_MASTER* draw3D = m_CurrentModule->Models(); + + for( unsigned ii = 0; ii < m_Shapes3D_list.size(); ii++ ) + { + S3D_MASTER* draw3DCopy = m_Shapes3D_list[ii]; + + if( draw3DCopy->GetShape3DName().IsEmpty() ) + continue; + + if( draw3D == NULL ) + { + draw3D = new S3D_MASTER( draw3D ); + m_CurrentModule->Models().Append( draw3D ); + } + + draw3D->SetShape3DName( draw3DCopy->GetShape3DName() ); + draw3D->m_MatScale = draw3DCopy->m_MatScale; + draw3D->m_MatRotation = draw3DCopy->m_MatRotation; + draw3D->m_MatPosition = draw3DCopy->m_MatPosition; + + draw3D = draw3D->Next(); + } + + // Remove old extra 3D shapes + S3D_MASTER* nextdraw3D; + + for( ; draw3D != NULL; draw3D = nextdraw3D ) + { + nextdraw3D = (S3D_MASTER*) draw3D->Next(); + delete m_CurrentModule->Models().Remove( draw3D ); + } + + // Fill shape list with one void entry, if no entry + if( m_CurrentModule->Models() == NULL ) + m_CurrentModule->Models().PushBack( new S3D_MASTER( m_CurrentModule ) ); + + + m_CurrentModule->CalculateBoundingBox(); + + m_Parent->OnModify(); + + SetReturnCode( PRM_EDITOR_EDIT_OK ); + + if( m_DC ) + { + m_CurrentModule->Draw( m_Parent->GetCanvas(), m_DC, GR_OR ); + m_Parent->GetCanvas()->CrossHairOn( m_DC ); + } + + return true; +} + + +void DIALOG_MODULE_BOARD_EDITOR::OnEditReference( wxCommandEvent& event ) +{ + wxPoint tmp = m_Parent->GetCrossHairPosition(); + + m_Parent->SetCrossHairPosition( m_ReferenceCopy->GetTextPosition() ); + m_ReferenceCopy->SetParent( m_CurrentModule ); + m_Parent->InstallTextModOptionsFrame( m_ReferenceCopy, NULL ); + m_Parent->SetCrossHairPosition( tmp ); + m_ReferenceCtrl->SetValue( m_ReferenceCopy->GetText() ); +} + + +void DIALOG_MODULE_BOARD_EDITOR::OnEditValue( wxCommandEvent& event ) +{ + wxPoint tmp = m_Parent->GetCrossHairPosition(); + + m_Parent->SetCrossHairPosition( m_ValueCopy->GetTextPosition() ); + m_ValueCopy->SetParent( m_CurrentModule ); + m_Parent->InstallTextModOptionsFrame( m_ValueCopy, NULL ); + m_Parent->SetCrossHairPosition( tmp ); + m_ValueCtrl->SetValue( m_ValueCopy->GetText() ); +} + + +void DIALOG_MODULE_BOARD_EDITOR::Cfg3DPath( wxCommandEvent& event ) +{ + S3D::Configure3DPaths( this, Prj().Get3DCacheManager()->GetResolver() ); +} diff --git a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.h b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.h index 5f10d41ec4..de03ccd91d 100644 --- a/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.h +++ b/pcbnew/dialogs/dialog_edit_module_for_BoardEditor.h @@ -28,7 +28,6 @@ #include -#include <3d_struct.h> #include class PANEL_PREV_3D; @@ -41,10 +40,11 @@ private: MODULE* m_CurrentModule; TEXTE_MODULE* m_ReferenceCopy; TEXTE_MODULE* m_ValueCopy; - std::vector m_Shapes3D_list; + std::vector m_shapes3D_list; int m_LastSelected3DShapeIndex; static size_t m_page; // remember the last open page during session PANEL_PREV_3D* m_PreviewPane; + MODULE* m_currentModuleCopy; wxFloatingPointValidator m_OrientValidator; double m_OrientValue; @@ -69,8 +69,6 @@ private: void BrowseAndAdd3DShapeFile(); void InitBoardProperties(); void InitModeditProperties(); - void Transfert3DValuesToDisplay( S3D_MASTER * aStruct3DSource ); - void TransfertDisplayTo3DValues( int aIndexSelection ); void Edit3DShapeFileName(); // virtual event functions @@ -103,6 +101,14 @@ private: bool TransferDataToWindow(); bool TransferDataFromWindow(); + + /** + * @brief OnCloseWindow - called when the frame is closed + * @param event + */ + void OnCloseWindow( wxCloseEvent &event ); + + DECLARE_EVENT_TABLE(); }; diff --git a/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp b/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp index f5bd166e57..90716cb26c 100644 --- a/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp +++ b/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp @@ -37,7 +37,6 @@ #include #include #include -#include <3d_struct.h> #include <3d_viewer.h> #include #include @@ -72,7 +71,14 @@ DIALOG_MODULE_MODULE_EDITOR::DIALOG_MODULE_MODULE_EDITOR( FOOTPRINT_EDIT_FRAME* SetIcon( icon ); aParent->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() ); - m_PreviewPane = new PANEL_PREV_3D( m_Panel3D, aParent->Prj().Get3DCacheManager() ); + + m_currentModuleCopy = new MODULE( *aModule ); + + m_PreviewPane = new PANEL_PREV_3D( m_Panel3D, + aParent->Prj().Get3DCacheManager(), + m_currentModuleCopy, + &m_shapes3D_list ); + bLowerSizer3D->Add( m_PreviewPane, 1, wxEXPAND, 5 ); m_FootprintNameCtrl->SetValidator( FILE_NAME_CHAR_VALIDATOR() ); @@ -90,9 +96,6 @@ DIALOG_MODULE_MODULE_EDITOR::DIALOG_MODULE_MODULE_EDITOR( FOOTPRINT_EDIT_FRAME* DIALOG_MODULE_MODULE_EDITOR::~DIALOG_MODULE_MODULE_EDITOR() { - for( unsigned ii = 0; ii < m_shapes3D_list.size(); ii++ ) - delete m_shapes3D_list[ii]; - m_shapes3D_list.clear(); // free the memory used by all models, otherwise models which were @@ -102,11 +105,20 @@ DIALOG_MODULE_MODULE_EDITOR::~DIALOG_MODULE_MODULE_EDITOR() // the GL canvas has to be visible before it is destroyed m_page = m_NoteBook->GetSelection(); m_NoteBook->SetSelection( 1 ); + + delete m_referenceCopy; + m_referenceCopy = NULL; // just in case, to avoid double-free + + delete m_valueCopy; + m_valueCopy = NULL; + delete m_PreviewPane; m_PreviewPane = NULL; // just in case, to avoid double-free - delete m_referenceCopy; - delete m_valueCopy; + // this is already deleted by the board used on preview pane so + // no need to delete here + // delete m_currentModuleCopy; + // m_currentModuleCopy = NULL; } @@ -125,33 +137,30 @@ void DIALOG_MODULE_MODULE_EDITOR::initModeditProperties() // Init 3D shape list m_3D_ShapeNameListBox->Clear(); - S3D_MASTER* draw3D = m_currentModule->Models(); + std::list::iterator sM = m_currentModule->Models().begin(); + std::list::iterator eM = m_currentModule->Models().end(); + m_shapes3D_list.clear(); + + wxString origPath; wxString alias; wxString shortPath; S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); - while( draw3D ) + while( sM != eM ) { - if( !draw3D->GetShape3DName().IsEmpty() ) + m_shapes3D_list.push_back( *sM ); + origPath = sM->m_Filename; + + if( res && res->SplitAlias( origPath, alias, shortPath ) ) { - S3D_MASTER* draw3DCopy = new S3D_MASTER(NULL); - draw3DCopy->Copy( draw3D ); - m_shapes3D_list.push_back( draw3DCopy ); - - origPath = draw3DCopy->GetShape3DName(); - - if( res && res->SplitAlias( origPath, alias, shortPath ) ) - { - origPath = alias; - origPath.append( wxT( ":" ) ); - origPath.append( shortPath ); - } - - m_3D_ShapeNameListBox->Append( origPath ); + origPath = alias; + origPath.append( wxT( ":" ) ); + origPath.append( shortPath ); } - draw3D = (S3D_MASTER*) draw3D->Next(); + m_3D_ShapeNameListBox->Append( origPath ); + ++sM; } m_DocCtrl->SetValue( m_currentModule->GetDescription() ); @@ -234,15 +243,13 @@ void DIALOG_MODULE_MODULE_EDITOR::initModeditProperties() { m_lastSelected3DShapeIndex = 0; m_3D_ShapeNameListBox->SetSelection( m_lastSelected3DShapeIndex ); - Transfert3DValuesToDisplay( m_shapes3D_list[m_lastSelected3DShapeIndex] ); + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_lastSelected3DShapeIndex, true ); } else { - S3D_INFO params; - params.scale.x = 1.0; - params.scale.y = 1.0; - params.scale.z = 1.0; - m_PreviewPane->SetModelData( ¶ms ); + if( m_PreviewPane ) + m_PreviewPane->ResetModelData( true ); } // We have modified the UI, so call Fit() for m_Panel3D @@ -251,109 +258,48 @@ void DIALOG_MODULE_MODULE_EDITOR::initModeditProperties() } -// Initialize 3D info displayed in dialog box from values in aStruct3DSource -void DIALOG_MODULE_MODULE_EDITOR::Transfert3DValuesToDisplay( S3D_MASTER * aStruct3DSource ) -{ - S3D_INFO params; - - if( aStruct3DSource ) - { - params.filename = aStruct3DSource->GetShape3DName(); - - params.scale.x = aStruct3DSource->m_MatScale.x; - params.scale.y = aStruct3DSource->m_MatScale.y; - params.scale.z = aStruct3DSource->m_MatScale.z; - - params.offset.x = aStruct3DSource->m_MatPosition.x; - params.offset.y = aStruct3DSource->m_MatPosition.y; - params.offset.z = aStruct3DSource->m_MatPosition.z; - - params.rotation.x = aStruct3DSource->m_MatRotation.x; - params.rotation.y = aStruct3DSource->m_MatRotation.y; - params.rotation.z = aStruct3DSource->m_MatRotation.z; - } - else - { - params.scale.x = 1.0; - params.scale.y = 1.0; - params.scale.z = 1.0; - - params.offset.x = 0.0; - params.offset.y = 0.0; - params.offset.z = 0.0; - - params.rotation = params.offset; - } - - m_PreviewPane->SetModelData( ¶ms ); - return; -} - - -/** Copy 3D info displayed in dialog box to values in a item in m_shapes3D_list - * @param aIndexSelection = item index in m_shapes3D_list - */ -void DIALOG_MODULE_MODULE_EDITOR::TransfertDisplayTo3DValues( int aIndexSelection ) -{ - if( aIndexSelection >= (int) m_shapes3D_list.size() ) - return; - - S3D_MASTER* struct3DDest = m_shapes3D_list[aIndexSelection]; - S3D_INFO params; - m_PreviewPane->GetModelData( ¶ms ); - - struct3DDest->m_MatScale.x = params.scale.x; - struct3DDest->m_MatScale.y = params.scale.y; - struct3DDest->m_MatScale.z = params.scale.z; - - struct3DDest->m_MatRotation.x = params.rotation.x; - struct3DDest->m_MatRotation.y = params.rotation.y; - struct3DDest->m_MatRotation.z = params.rotation.z; - - struct3DDest->m_MatPosition.x = params.offset.x; - struct3DDest->m_MatPosition.y = params.offset.y; - struct3DDest->m_MatPosition.z = params.offset.z; - - return; -} - - void DIALOG_MODULE_MODULE_EDITOR::On3DShapeNameSelected(wxCommandEvent& event) { - if( m_lastSelected3DShapeIndex >= 0 ) - TransfertDisplayTo3DValues( m_lastSelected3DShapeIndex ); - m_lastSelected3DShapeIndex = m_3D_ShapeNameListBox->GetSelection(); if( m_lastSelected3DShapeIndex < 0 ) // happens under wxGTK when deleting an item in m_3D_ShapeNameListBox wxListBox - return; + { + if( m_PreviewPane ) + m_PreviewPane->ResetModelData(); + return; + } if( m_lastSelected3DShapeIndex >= (int)m_shapes3D_list.size() ) { wxMessageBox( wxT( "On3DShapeNameSelected() error" ) ); m_lastSelected3DShapeIndex = -1; + + if( m_PreviewPane ) + m_PreviewPane->ResetModelData(); + return; } - Transfert3DValuesToDisplay( m_shapes3D_list[m_lastSelected3DShapeIndex] ); + m_PreviewPane->SetModelDataIdx( m_lastSelected3DShapeIndex ); } void DIALOG_MODULE_MODULE_EDITOR::Remove3DShape(wxCommandEvent& event) { - if( m_lastSelected3DShapeIndex >= 0 ) - TransfertDisplayTo3DValues( m_lastSelected3DShapeIndex ); - int ii = m_3D_ShapeNameListBox->GetSelection(); + if( ii < 0 ) + { + if( m_PreviewPane ) + m_PreviewPane->ResetModelData( true ); + return; + } m_shapes3D_list.erase( m_shapes3D_list.begin() + ii ); m_3D_ShapeNameListBox->Delete( ii ); - if( m_3D_ShapeNameListBox->GetCount() == 0 ) - Transfert3DValuesToDisplay( NULL ); - else + if( m_3D_ShapeNameListBox->GetCount() > 0 ) { if( ii > 0 ) m_lastSelected3DShapeIndex = ii - 1; @@ -361,8 +307,14 @@ void DIALOG_MODULE_MODULE_EDITOR::Remove3DShape(wxCommandEvent& event) m_lastSelected3DShapeIndex = 0; m_3D_ShapeNameListBox->SetSelection( m_lastSelected3DShapeIndex ); - Transfert3DValuesToDisplay( - m_shapes3D_list[m_lastSelected3DShapeIndex] ); + + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_lastSelected3DShapeIndex, true ); + } + else + { + if( m_PreviewPane ) + m_PreviewPane->ResetModelData( true ); } return; @@ -376,9 +328,6 @@ void DIALOG_MODULE_MODULE_EDITOR::Edit3DShapeFileName() if( idx < 0 ) return; - // ensure any updated parameters are not discarded - TransfertDisplayTo3DValues( idx ); - // Edit filename wxString filename = m_3D_ShapeNameListBox->GetStringSelection(); wxTextEntryDialog dlg( this, wxEmptyString, wxEmptyString, filename ); @@ -414,15 +363,11 @@ void DIALOG_MODULE_MODULE_EDITOR::Edit3DShapeFileName() filename.Replace( wxT( "\\" ), wxT( "/" ) ); #endif - S3D_MASTER* new3DShape = new S3D_MASTER( NULL ); - new3DShape->SetShape3DName( filename ); - new3DShape->m_MatPosition = m_shapes3D_list[idx]->m_MatPosition; - new3DShape->m_MatRotation = m_shapes3D_list[idx]->m_MatRotation; - new3DShape->m_MatScale = m_shapes3D_list[idx]->m_MatScale; - delete m_shapes3D_list[idx]; - m_shapes3D_list[idx] = new3DShape; + m_shapes3D_list[idx].m_Filename = filename; - Transfert3DValuesToDisplay( m_shapes3D_list[idx] ); + // This assumes that the index didn't change and will just update the filename + if( m_PreviewPane ) + m_PreviewPane->UpdateModelName( filename ); return; } @@ -447,7 +392,7 @@ void DIALOG_MODULE_MODULE_EDITOR::BrowseAndAdd3DShapeFile() } if( !S3D::Select3DModel( m_PreviewPane, Prj().Get3DCacheManager(), - initialpath, filter, &model ) || model.filename.empty() ) + initialpath, filter, &model ) || model.m_Filename.empty() ) { return; } @@ -455,7 +400,7 @@ void DIALOG_MODULE_MODULE_EDITOR::BrowseAndAdd3DShapeFile() prj.SetRString( PROJECT::VIEWER_3D_PATH, initialpath ); sidx = wxString::Format( wxT( "%i" ), filter ); prj.SetRString( PROJECT::VIEWER_3D_FILTER_INDEX, sidx ); - wxString origPath = model.filename; + wxString origPath = model.m_Filename; wxString alias; wxString shortPath; S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); @@ -471,25 +416,15 @@ void DIALOG_MODULE_MODULE_EDITOR::BrowseAndAdd3DShapeFile() #ifdef __WINDOWS__ // In Kicad files, filenames and paths are stored using Unix notation - model.filename.Replace( wxT( "\\" ), wxT( "/" ) ); + model.m_Filename.Replace( wxT( "\\" ), wxT( "/" ) ); #endif - S3D_MASTER* new3DShape = new S3D_MASTER( NULL ); - new3DShape->SetShape3DName( model.filename ); - new3DShape->m_MatScale.x = model.scale.x; - new3DShape->m_MatScale.y = model.scale.y; - new3DShape->m_MatScale.z = model.scale.z; - new3DShape->m_MatRotation.x = model.rotation.x; - new3DShape->m_MatRotation.y = model.rotation.y; - new3DShape->m_MatRotation.z = model.rotation.z; - new3DShape->m_MatPosition.x = model.offset.x; - new3DShape->m_MatPosition.y = model.offset.y; - new3DShape->m_MatPosition.z = model.offset.z; - - m_shapes3D_list.push_back( new3DShape ); + m_shapes3D_list.push_back( model ); m_lastSelected3DShapeIndex = m_3D_ShapeNameListBox->GetCount() - 1; m_3D_ShapeNameListBox->SetSelection( m_lastSelected3DShapeIndex ); - Transfert3DValuesToDisplay( m_shapes3D_list[m_lastSelected3DShapeIndex] ); + + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_lastSelected3DShapeIndex, true ); return; } @@ -569,48 +504,9 @@ void DIALOG_MODULE_MODULE_EDITOR::OnOkClick( wxCommandEvent& event ) m_currentModule->SetLocalSolderPasteMarginRatio( dtmp / 100 ); - // Update 3D shape list - int idx = m_3D_ShapeNameListBox->GetSelection(); - - if ( idx >= 0 ) - TransfertDisplayTo3DValues( idx ); - - S3D_MASTER* draw3D = m_currentModule->Models(); - - for( unsigned ii = 0; ii < m_shapes3D_list.size(); ii++ ) - { - S3D_MASTER* draw3DCopy = m_shapes3D_list[ii]; - - if( draw3DCopy->GetShape3DName().IsEmpty() ) - continue; - - if( draw3D == NULL ) - { - draw3D = new S3D_MASTER( draw3D ); - m_currentModule->Models().Append( draw3D ); - } - - draw3D->SetShape3DName( draw3DCopy->GetShape3DName() ); - draw3D->m_MatScale = draw3DCopy->m_MatScale; - draw3D->m_MatRotation = draw3DCopy->m_MatRotation; - draw3D->m_MatPosition = draw3DCopy->m_MatPosition; - - draw3D = draw3D->Next(); - } - - // Remove old extra 3D shapes - S3D_MASTER* nextdraw3D; - - for( ; draw3D != NULL; draw3D = nextdraw3D ) - { - nextdraw3D = (S3D_MASTER*) draw3D->Next(); - delete m_currentModule->Models().Remove( draw3D ); - } - - // Fill shape list with one void entry, if no entry - if( m_currentModule->Models() == NULL ) - m_currentModule->Models().PushBack( new S3D_MASTER( m_currentModule ) ); - + std::list* draw3D = &m_currentModule->Models(); + draw3D->clear(); + draw3D->insert( draw3D->end(), m_shapes3D_list.begin(), m_shapes3D_list.end() ); m_currentModule->CalculateBoundingBox(); @@ -642,5 +538,8 @@ void DIALOG_MODULE_MODULE_EDITOR::OnEditValue(wxCommandEvent& event) void DIALOG_MODULE_MODULE_EDITOR::Cfg3DPath( wxCommandEvent& event ) { - S3D::Configure3DPaths( this, Prj().Get3DCacheManager()->GetResolver() ); + if( S3D::Configure3DPaths( this, Prj().Get3DCacheManager()->GetResolver() ) ) + if( m_lastSelected3DShapeIndex >= 0 ) + if( m_PreviewPane ) + m_PreviewPane->SetModelDataIdx( m_lastSelected3DShapeIndex, true ); } diff --git a/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp.orig b/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp.orig new file mode 100644 index 0000000000..f5bd166e57 --- /dev/null +++ b/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp.orig @@ -0,0 +1,646 @@ +/** + * @file dialog_edit_module_for_Modedit.cpp + * + * @brief Dialog for editing a module properties in module editor (modedit) + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2015 Dick Hollenbeck, dick@softplc.com + * Copyright (C) 2008-2015 Wayne Stambaugh + * Copyright (C) 2004-2015 KiCad Developers, see change_log.txt for contributors. + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include +#include +#include +#include +#include +#include +#include <3d_struct.h> +#include <3d_viewer.h> +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "3d_cache/dialogs/panel_prev_model.h" +#include "3d_cache/dialogs/3d_cache_dialogs.h" +#include "3d_cache/3d_cache.h" +#include "3d_cache/3d_filename_resolver.h" + +size_t DIALOG_MODULE_MODULE_EDITOR::m_page = 0; // remember the last open page during session + + +DIALOG_MODULE_MODULE_EDITOR::DIALOG_MODULE_MODULE_EDITOR( FOOTPRINT_EDIT_FRAME* aParent, + MODULE* aModule ) : + DIALOG_MODULE_MODULE_EDITOR_BASE( aParent ) +{ + m_parent = aParent; + m_currentModule = aModule; + + // Give an icon + wxIcon icon; + icon.CopyFromBitmap( KiBitmap( icon_modedit_xpm ) ); + SetIcon( icon ); + + aParent->Prj().Get3DCacheManager()->GetResolver()->SetProgramBase( &Pgm() ); + m_PreviewPane = new PANEL_PREV_3D( m_Panel3D, aParent->Prj().Get3DCacheManager() ); + bLowerSizer3D->Add( m_PreviewPane, 1, wxEXPAND, 5 ); + + m_FootprintNameCtrl->SetValidator( FILE_NAME_CHAR_VALIDATOR() ); + initModeditProperties(); + + m_NoteBook->SetSelection( m_page ); + + m_sdbSizerStdButtonsOK->SetDefault(); + + Layout(); + + FixOSXCancelButtonIssue(); +} + + +DIALOG_MODULE_MODULE_EDITOR::~DIALOG_MODULE_MODULE_EDITOR() +{ + for( unsigned ii = 0; ii < m_shapes3D_list.size(); ii++ ) + delete m_shapes3D_list[ii]; + + m_shapes3D_list.clear(); + + // free the memory used by all models, otherwise models which were + // browsed but not used would consume memory + Prj().Get3DCacheManager()->FlushCache( false ); + + // the GL canvas has to be visible before it is destroyed + m_page = m_NoteBook->GetSelection(); + m_NoteBook->SetSelection( 1 ); + delete m_PreviewPane; + m_PreviewPane = NULL; // just in case, to avoid double-free + + delete m_referenceCopy; + delete m_valueCopy; +} + + +void DIALOG_MODULE_MODULE_EDITOR::initModeditProperties() +{ + SetFocus(); + + // Display the default path, given by environment variable KISYS3DMOD + wxString default_path; + wxGetEnv( KISYS3DMOD, &default_path ); +#ifdef __WINDOWS__ + default_path.Replace( wxT( "/" ), wxT( "\\" ) ); +#endif + + m_lastSelected3DShapeIndex = -1; + + // Init 3D shape list + m_3D_ShapeNameListBox->Clear(); + S3D_MASTER* draw3D = m_currentModule->Models(); + wxString origPath; + wxString alias; + wxString shortPath; + S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); + + while( draw3D ) + { + if( !draw3D->GetShape3DName().IsEmpty() ) + { + S3D_MASTER* draw3DCopy = new S3D_MASTER(NULL); + draw3DCopy->Copy( draw3D ); + m_shapes3D_list.push_back( draw3DCopy ); + + origPath = draw3DCopy->GetShape3DName(); + + if( res && res->SplitAlias( origPath, alias, shortPath ) ) + { + origPath = alias; + origPath.append( wxT( ":" ) ); + origPath.append( shortPath ); + } + + m_3D_ShapeNameListBox->Append( origPath ); + } + + draw3D = (S3D_MASTER*) draw3D->Next(); + } + + m_DocCtrl->SetValue( m_currentModule->GetDescription() ); + m_KeywordCtrl->SetValue( m_currentModule->GetKeywords() ); + m_referenceCopy = new TEXTE_MODULE( NULL ); + m_valueCopy = new TEXTE_MODULE( NULL ); + m_referenceCopy->Copy( &m_currentModule->Reference() ); + m_valueCopy->Copy( &m_currentModule->Value() ); + m_ReferenceCtrl->SetValue( m_referenceCopy->GetText() ); + m_ValueCtrl->SetValue( m_valueCopy->GetText() ); + m_FootprintNameCtrl->SetValue( m_currentModule->GetFPID().Format() ); + + m_AttributsCtrl->SetItemToolTip( 0, _( "Use this attribute for most non SMD components" ) ); + m_AttributsCtrl->SetItemToolTip( 1, + _( "Use this attribute for SMD components.\nOnly components with this option are put in the footprint position list file" ) ); + m_AttributsCtrl->SetItemToolTip( 2, + _( "Use this attribute for \"virtual\" components drawn on board (like a old ISA PC bus connector)" ) ); + + // Controls on right side of the dialog + switch( m_currentModule->GetAttributes() & 255 ) + { + case 0: + m_AttributsCtrl->SetSelection( 0 ); + break; + + case MOD_CMS: + m_AttributsCtrl->SetSelection( 1 ); + break; + + case MOD_VIRTUAL: + m_AttributsCtrl->SetSelection( 2 ); + break; + + default: + m_AttributsCtrl->SetSelection( 0 ); + break; + } + + m_AutoPlaceCtrl->SetSelection( (m_currentModule->IsLocked()) ? 1 : 0 ); + m_AutoPlaceCtrl->SetItemToolTip( 0, _( "Enable hotkey move commands and Auto Placement" ) ); + m_AutoPlaceCtrl->SetItemToolTip( 1, _( "Disable hotkey move commands and Auto Placement" ) ); + + m_CostRot90Ctrl->SetValue( m_currentModule->GetPlacementCost90() ); + m_CostRot180Ctrl->SetValue( m_currentModule->GetPlacementCost180() ); + + // Initialize dialog relative to masks clearances + m_NetClearanceUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + m_SolderMaskMarginUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + m_SolderPasteMarginUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + + wxString msg; + PutValueInLocalUnits( *m_NetClearanceValueCtrl, m_currentModule->GetLocalClearance() ); + PutValueInLocalUnits( *m_SolderMaskMarginCtrl, m_currentModule->GetLocalSolderMaskMargin() ); + + // These 2 parameters are usually < 0, so prepare entering a negative value, if current is 0 + PutValueInLocalUnits( *m_SolderPasteMarginCtrl, m_currentModule->GetLocalSolderPasteMargin() ); + + if( m_currentModule->GetLocalSolderPasteMargin() == 0 ) + m_SolderPasteMarginCtrl->SetValue( wxT( "-" ) + m_SolderPasteMarginCtrl->GetValue() ); + + if( m_currentModule->GetLocalSolderPasteMarginRatio() == 0.0 ) + msg.Printf( wxT( "-%f" ), m_currentModule->GetLocalSolderPasteMarginRatio() * 100.0 ); + else + msg.Printf( wxT( "%f" ), m_currentModule->GetLocalSolderPasteMarginRatio() * 100.0 ); + + m_SolderPasteMarginRatioCtrl->SetValue( msg ); + + // Add solder paste margin ration in per cent + // for the usual default value 0.0, display -0.0 (or -0,0 in some countries) + msg.Printf( wxT( "%f" ), m_currentModule->GetLocalSolderPasteMarginRatio() * 100.0 ); + + if( m_currentModule->GetLocalSolderPasteMarginRatio() == 0.0 && + msg[0] == '0') // Sometimes Printf adds a sign if the value is very small (0.0) + m_SolderPasteMarginRatioCtrl->SetValue( wxT( "-" ) + msg ); + else + m_SolderPasteMarginRatioCtrl->SetValue( msg ); + + // if m_3D_ShapeNameListBox is not empty, preselect first 3D shape + if( m_3D_ShapeNameListBox->GetCount() > 0 ) + { + m_lastSelected3DShapeIndex = 0; + m_3D_ShapeNameListBox->SetSelection( m_lastSelected3DShapeIndex ); + Transfert3DValuesToDisplay( m_shapes3D_list[m_lastSelected3DShapeIndex] ); + } + else + { + S3D_INFO params; + params.scale.x = 1.0; + params.scale.y = 1.0; + params.scale.z = 1.0; + m_PreviewPane->SetModelData( ¶ms ); + } + + // We have modified the UI, so call Fit() for m_Panel3D + // to be sure the m_Panel3D sizers are initialized before opening the dialog + m_Panel3D->GetSizer()->Fit( m_Panel3D ); +} + + +// Initialize 3D info displayed in dialog box from values in aStruct3DSource +void DIALOG_MODULE_MODULE_EDITOR::Transfert3DValuesToDisplay( S3D_MASTER * aStruct3DSource ) +{ + S3D_INFO params; + + if( aStruct3DSource ) + { + params.filename = aStruct3DSource->GetShape3DName(); + + params.scale.x = aStruct3DSource->m_MatScale.x; + params.scale.y = aStruct3DSource->m_MatScale.y; + params.scale.z = aStruct3DSource->m_MatScale.z; + + params.offset.x = aStruct3DSource->m_MatPosition.x; + params.offset.y = aStruct3DSource->m_MatPosition.y; + params.offset.z = aStruct3DSource->m_MatPosition.z; + + params.rotation.x = aStruct3DSource->m_MatRotation.x; + params.rotation.y = aStruct3DSource->m_MatRotation.y; + params.rotation.z = aStruct3DSource->m_MatRotation.z; + } + else + { + params.scale.x = 1.0; + params.scale.y = 1.0; + params.scale.z = 1.0; + + params.offset.x = 0.0; + params.offset.y = 0.0; + params.offset.z = 0.0; + + params.rotation = params.offset; + } + + m_PreviewPane->SetModelData( ¶ms ); + return; +} + + +/** Copy 3D info displayed in dialog box to values in a item in m_shapes3D_list + * @param aIndexSelection = item index in m_shapes3D_list + */ +void DIALOG_MODULE_MODULE_EDITOR::TransfertDisplayTo3DValues( int aIndexSelection ) +{ + if( aIndexSelection >= (int) m_shapes3D_list.size() ) + return; + + S3D_MASTER* struct3DDest = m_shapes3D_list[aIndexSelection]; + S3D_INFO params; + m_PreviewPane->GetModelData( ¶ms ); + + struct3DDest->m_MatScale.x = params.scale.x; + struct3DDest->m_MatScale.y = params.scale.y; + struct3DDest->m_MatScale.z = params.scale.z; + + struct3DDest->m_MatRotation.x = params.rotation.x; + struct3DDest->m_MatRotation.y = params.rotation.y; + struct3DDest->m_MatRotation.z = params.rotation.z; + + struct3DDest->m_MatPosition.x = params.offset.x; + struct3DDest->m_MatPosition.y = params.offset.y; + struct3DDest->m_MatPosition.z = params.offset.z; + + return; +} + + +void DIALOG_MODULE_MODULE_EDITOR::On3DShapeNameSelected(wxCommandEvent& event) +{ + if( m_lastSelected3DShapeIndex >= 0 ) + TransfertDisplayTo3DValues( m_lastSelected3DShapeIndex ); + + m_lastSelected3DShapeIndex = m_3D_ShapeNameListBox->GetSelection(); + + if( m_lastSelected3DShapeIndex < 0 ) // happens under wxGTK when deleting an item in m_3D_ShapeNameListBox wxListBox + return; + + if( m_lastSelected3DShapeIndex >= (int)m_shapes3D_list.size() ) + { + wxMessageBox( wxT( "On3DShapeNameSelected() error" ) ); + m_lastSelected3DShapeIndex = -1; + return; + } + + Transfert3DValuesToDisplay( m_shapes3D_list[m_lastSelected3DShapeIndex] ); +} + + +void DIALOG_MODULE_MODULE_EDITOR::Remove3DShape(wxCommandEvent& event) +{ + if( m_lastSelected3DShapeIndex >= 0 ) + TransfertDisplayTo3DValues( m_lastSelected3DShapeIndex ); + + int ii = m_3D_ShapeNameListBox->GetSelection(); + if( ii < 0 ) + return; + + m_shapes3D_list.erase( m_shapes3D_list.begin() + ii ); + m_3D_ShapeNameListBox->Delete( ii ); + + if( m_3D_ShapeNameListBox->GetCount() == 0 ) + Transfert3DValuesToDisplay( NULL ); + else + { + if( ii > 0 ) + m_lastSelected3DShapeIndex = ii - 1; + else + m_lastSelected3DShapeIndex = 0; + + m_3D_ShapeNameListBox->SetSelection( m_lastSelected3DShapeIndex ); + Transfert3DValuesToDisplay( + m_shapes3D_list[m_lastSelected3DShapeIndex] ); + } + + return; +} + + +void DIALOG_MODULE_MODULE_EDITOR::Edit3DShapeFileName() +{ + int idx = m_3D_ShapeNameListBox->GetSelection(); + + if( idx < 0 ) + return; + + // ensure any updated parameters are not discarded + TransfertDisplayTo3DValues( idx ); + + // Edit filename + wxString filename = m_3D_ShapeNameListBox->GetStringSelection(); + wxTextEntryDialog dlg( this, wxEmptyString, wxEmptyString, filename ); + + bool hasAlias; + S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); + + if( dlg.ShowModal() != wxID_OK ) + return; + + filename = dlg.GetValue(); + + if( filename.empty() ) + return; + + if( !res->ValidateFileName( filename, hasAlias ) ) + { + wxString msg = _( "Invalid filename: " ); + msg.append( filename ); + wxMessageBox( msg, _T( "Edit 3D file name" ) ); + + return; + } + + m_3D_ShapeNameListBox->SetString( idx, filename ); + + // if the user has specified an alias in the name then prepend ':' + if( hasAlias ) + filename.insert( 0, wxT( ":" ) ); + + #ifdef __WINDOWS__ + // In Kicad files, filenames and paths are stored using Unix notation + filename.Replace( wxT( "\\" ), wxT( "/" ) ); + #endif + + S3D_MASTER* new3DShape = new S3D_MASTER( NULL ); + new3DShape->SetShape3DName( filename ); + new3DShape->m_MatPosition = m_shapes3D_list[idx]->m_MatPosition; + new3DShape->m_MatRotation = m_shapes3D_list[idx]->m_MatRotation; + new3DShape->m_MatScale = m_shapes3D_list[idx]->m_MatScale; + delete m_shapes3D_list[idx]; + m_shapes3D_list[idx] = new3DShape; + + Transfert3DValuesToDisplay( m_shapes3D_list[idx] ); + + return; +} + + +void DIALOG_MODULE_MODULE_EDITOR::BrowseAndAdd3DShapeFile() +{ + PROJECT& prj = Prj(); + S3D_INFO model; + + wxString initialpath = prj.GetRString( PROJECT::VIEWER_3D_PATH ); + wxString sidx = prj.GetRString( PROJECT::VIEWER_3D_FILTER_INDEX ); + int filter = 0; + + if( !sidx.empty() ) + { + long tmp; + sidx.ToLong( &tmp ); + + if( tmp > 0 && tmp <= 0x7FFFFFFF ) + filter = (int) tmp; + } + + if( !S3D::Select3DModel( m_PreviewPane, Prj().Get3DCacheManager(), + initialpath, filter, &model ) || model.filename.empty() ) + { + return; + } + + prj.SetRString( PROJECT::VIEWER_3D_PATH, initialpath ); + sidx = wxString::Format( wxT( "%i" ), filter ); + prj.SetRString( PROJECT::VIEWER_3D_FILTER_INDEX, sidx ); + wxString origPath = model.filename; + wxString alias; + wxString shortPath; + S3D_FILENAME_RESOLVER* res = Prj().Get3DCacheManager()->GetResolver(); + + if( res && res->SplitAlias( origPath, alias, shortPath ) ) + { + origPath = alias; + origPath.append( wxT( ":" ) ); + origPath.append( shortPath ); + } + + m_3D_ShapeNameListBox->Append( origPath ); + + #ifdef __WINDOWS__ + // In Kicad files, filenames and paths are stored using Unix notation + model.filename.Replace( wxT( "\\" ), wxT( "/" ) ); + #endif + + S3D_MASTER* new3DShape = new S3D_MASTER( NULL ); + new3DShape->SetShape3DName( model.filename ); + new3DShape->m_MatScale.x = model.scale.x; + new3DShape->m_MatScale.y = model.scale.y; + new3DShape->m_MatScale.z = model.scale.z; + new3DShape->m_MatRotation.x = model.rotation.x; + new3DShape->m_MatRotation.y = model.rotation.y; + new3DShape->m_MatRotation.z = model.rotation.z; + new3DShape->m_MatPosition.x = model.offset.x; + new3DShape->m_MatPosition.y = model.offset.y; + new3DShape->m_MatPosition.z = model.offset.z; + + m_shapes3D_list.push_back( new3DShape ); + m_lastSelected3DShapeIndex = m_3D_ShapeNameListBox->GetCount() - 1; + m_3D_ShapeNameListBox->SetSelection( m_lastSelected3DShapeIndex ); + Transfert3DValuesToDisplay( m_shapes3D_list[m_lastSelected3DShapeIndex] ); + + return; +} + + +void DIALOG_MODULE_MODULE_EDITOR::OnCancelClick( wxCommandEvent& event ) +{ + EndModal( -1 ); +} + + +void DIALOG_MODULE_MODULE_EDITOR::OnOkClick( wxCommandEvent& event ) +{ + // First, test for invalid chars in module name + wxString footprintName = m_FootprintNameCtrl->GetValue(); + + if( ! footprintName.IsEmpty() ) + { + if( ! MODULE::IsLibNameValid( footprintName ) ) + { + wxString msg; + msg.Printf( _( "Error:\none of invalid chars <%s> found\nin <%s>" ), + MODULE::StringLibNameInvalidChars( true ), + GetChars( footprintName ) ); + + DisplayError( NULL, msg ); + return; + } + } + + m_parent->SaveCopyInUndoList( m_currentModule, UR_MODEDIT ); + m_currentModule->SetLocked( m_AutoPlaceCtrl->GetSelection() == 1 ); + + switch( m_AttributsCtrl->GetSelection() ) + { + case 0: + m_currentModule->SetAttributes( 0 ); + break; + + case 1: + m_currentModule->SetAttributes( MOD_CMS ); + break; + + case 2: + m_currentModule->SetAttributes( MOD_VIRTUAL ); + break; + } + + m_currentModule->SetPlacementCost90( m_CostRot90Ctrl->GetValue() ); + m_currentModule->SetPlacementCost180( m_CostRot180Ctrl->GetValue() ); + m_currentModule->SetDescription( m_DocCtrl->GetValue() ); + m_currentModule->SetKeywords( m_KeywordCtrl->GetValue() ); + + // Init footprint name in library + if( ! footprintName.IsEmpty() ) + m_currentModule->SetFPID( FPID( footprintName ) ); + + // Init Fields: + m_currentModule->Reference().Copy( m_referenceCopy ); + m_currentModule->Value().Copy( m_valueCopy ); + + // Initialize masks clearances + m_currentModule->SetLocalClearance( ValueFromTextCtrl( *m_NetClearanceValueCtrl ) ); + m_currentModule->SetLocalSolderMaskMargin( ValueFromTextCtrl( *m_SolderMaskMarginCtrl ) ); + m_currentModule->SetLocalSolderPasteMargin( ValueFromTextCtrl( *m_SolderPasteMarginCtrl ) ); + double dtmp; + wxString msg = m_SolderPasteMarginRatioCtrl->GetValue(); + msg.ToDouble( &dtmp ); + + // A -50% margin ratio means no paste on a pad, the ratio must be >= -50 % + if( dtmp < -50.0 ) + dtmp = -50.0; + + // A margin ratio is always <= 0 + if( dtmp > 0.0 ) + dtmp = 0.0; + + m_currentModule->SetLocalSolderPasteMarginRatio( dtmp / 100 ); + + // Update 3D shape list + int idx = m_3D_ShapeNameListBox->GetSelection(); + + if ( idx >= 0 ) + TransfertDisplayTo3DValues( idx ); + + S3D_MASTER* draw3D = m_currentModule->Models(); + + for( unsigned ii = 0; ii < m_shapes3D_list.size(); ii++ ) + { + S3D_MASTER* draw3DCopy = m_shapes3D_list[ii]; + + if( draw3DCopy->GetShape3DName().IsEmpty() ) + continue; + + if( draw3D == NULL ) + { + draw3D = new S3D_MASTER( draw3D ); + m_currentModule->Models().Append( draw3D ); + } + + draw3D->SetShape3DName( draw3DCopy->GetShape3DName() ); + draw3D->m_MatScale = draw3DCopy->m_MatScale; + draw3D->m_MatRotation = draw3DCopy->m_MatRotation; + draw3D->m_MatPosition = draw3DCopy->m_MatPosition; + + draw3D = draw3D->Next(); + } + + // Remove old extra 3D shapes + S3D_MASTER* nextdraw3D; + + for( ; draw3D != NULL; draw3D = nextdraw3D ) + { + nextdraw3D = (S3D_MASTER*) draw3D->Next(); + delete m_currentModule->Models().Remove( draw3D ); + } + + // Fill shape list with one void entry, if no entry + if( m_currentModule->Models() == NULL ) + m_currentModule->Models().PushBack( new S3D_MASTER( m_currentModule ) ); + + + m_currentModule->CalculateBoundingBox(); + + m_parent->OnModify(); + + EndModal( 1 ); +} + + +void DIALOG_MODULE_MODULE_EDITOR::OnEditReference(wxCommandEvent& event) +{ + wxPoint tmp = m_parent->GetCrossHairPosition(); + m_parent->SetCrossHairPosition( m_referenceCopy->GetTextPosition() ); + m_parent->InstallTextModOptionsFrame( m_referenceCopy, NULL ); + m_parent->SetCrossHairPosition( tmp ); + m_ReferenceCtrl->SetValue( m_referenceCopy->GetText() ); +} + + +void DIALOG_MODULE_MODULE_EDITOR::OnEditValue(wxCommandEvent& event) +{ + wxPoint tmp = m_parent->GetCrossHairPosition(); + m_parent->SetCrossHairPosition( m_valueCopy->GetTextPosition() ); + m_parent->InstallTextModOptionsFrame( m_valueCopy, NULL ); + m_parent->SetCrossHairPosition( tmp ); + m_ValueCtrl->SetValue( m_valueCopy->GetText() ); +} + + +void DIALOG_MODULE_MODULE_EDITOR::Cfg3DPath( wxCommandEvent& event ) +{ + S3D::Configure3DPaths( this, Prj().Get3DCacheManager()->GetResolver() ); +} diff --git a/pcbnew/dialogs/dialog_edit_module_for_Modedit.h b/pcbnew/dialogs/dialog_edit_module_for_Modedit.h index b28fefd11d..846eaa0e30 100644 --- a/pcbnew/dialogs/dialog_edit_module_for_Modedit.h +++ b/pcbnew/dialogs/dialog_edit_module_for_Modedit.h @@ -28,9 +28,9 @@ // Include the wxFormBuider header base: #include #include -#include <3d_struct.h> class PANEL_PREV_3D; +class MODULE; class DIALOG_MODULE_MODULE_EDITOR : public DIALOG_MODULE_MODULE_EDITOR_BASE { @@ -40,10 +40,11 @@ private: MODULE* m_currentModule; TEXTE_MODULE* m_referenceCopy; TEXTE_MODULE* m_valueCopy; - std::vector m_shapes3D_list; + std::vector m_shapes3D_list; int m_lastSelected3DShapeIndex; static size_t m_page; // remember the last open page during session PANEL_PREV_3D* m_PreviewPane; + MODULE* m_currentModuleCopy; public: @@ -54,8 +55,6 @@ public: private: void BrowseAndAdd3DShapeFile(); void initModeditProperties(); - void Transfert3DValuesToDisplay( S3D_MASTER * aStruct3DSource ); - void TransfertDisplayTo3DValues( int aIndexSelection ); void Edit3DShapeFileName(); // virtual event functions diff --git a/pcbnew/dialogs/dialog_export_idf.cpp b/pcbnew/dialogs/dialog_export_idf.cpp index b38a8bb827..ffaf684a7e 100644 --- a/pcbnew/dialogs/dialog_export_idf.cpp +++ b/pcbnew/dialogs/dialog_export_idf.cpp @@ -33,6 +33,7 @@ // IDF export header generated by wxFormBuilder #include +#include #define OPTKEY_IDF_THOU wxT( "IDFExportThou" ) #define OPTKEY_IDF_REF_AUTOADJ wxT( "IDFRefAutoAdj" ) @@ -41,21 +42,6 @@ #define OPTKEY_IDF_REF_Y wxT( "IDFRefY" ) -/** - * Function Export_IDF3 - * Creates an IDF3 compliant BOARD (*.emn) and LIBRARY (*.emp) file. - * - * @param aPcb = a pointer to the board to be exported to IDF - * @param aFullFileName = the full filename of the export file - * @param aUseThou = set to true if the desired IDF unit is thou (mil) - * @param aXRef = the board Reference Point in mm, X value - * @param aYRef = the board Reference Point in mm, Y value - * @return true if OK - */ -bool Export_IDF3( BOARD *aPcb, const wxString & aFullFileName, bool aUseThou, - double aXRef, double aYRef ); - - class DIALOG_EXPORT_IDF3: public DIALOG_EXPORT_IDF3_BASE { private: @@ -179,7 +165,7 @@ public: * Function OnExportIDF3 * will export the current BOARD to IDF board and lib files. */ -void PCB_EDIT_FRAME::ExportToIDF3( wxCommandEvent& event ) +void PCB_EDIT_FRAME::OnExportIDF3( wxCommandEvent& event ) { wxFileName fn; diff --git a/pcbnew/dialogs/dialog_export_idf.cpp.orig b/pcbnew/dialogs/dialog_export_idf.cpp.orig new file mode 100644 index 0000000000..b38a8bb827 --- /dev/null +++ b/pcbnew/dialogs/dialog_export_idf.cpp.orig @@ -0,0 +1,231 @@ +/** + * @file dialog_export_idf.cpp + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013-2015 Cirilo Bernardo + * + * 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 2 + * 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, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include + +// IDF export header generated by wxFormBuilder +#include + +#define OPTKEY_IDF_THOU wxT( "IDFExportThou" ) +#define OPTKEY_IDF_REF_AUTOADJ wxT( "IDFRefAutoAdj" ) +#define OPTKEY_IDF_REF_UNITS wxT( "IDFRefUnits" ) +#define OPTKEY_IDF_REF_X wxT( "IDFRefX" ) +#define OPTKEY_IDF_REF_Y wxT( "IDFRefY" ) + + +/** + * Function Export_IDF3 + * Creates an IDF3 compliant BOARD (*.emn) and LIBRARY (*.emp) file. + * + * @param aPcb = a pointer to the board to be exported to IDF + * @param aFullFileName = the full filename of the export file + * @param aUseThou = set to true if the desired IDF unit is thou (mil) + * @param aXRef = the board Reference Point in mm, X value + * @param aYRef = the board Reference Point in mm, Y value + * @return true if OK + */ +bool Export_IDF3( BOARD *aPcb, const wxString & aFullFileName, bool aUseThou, + double aXRef, double aYRef ); + + +class DIALOG_EXPORT_IDF3: public DIALOG_EXPORT_IDF3_BASE +{ +private: + PCB_EDIT_FRAME* m_parent; + wxConfigBase* m_config; + bool m_idfThouOpt; // remember last preference for units in THOU + bool m_AutoAdjust; // remember last Reference Point AutoAdjust setting + int m_RefUnits; // remember last units for Reference Point + double m_XRef; // remember last X Reference Point + double m_YRef; // remember last Y Reference Point + +public: + DIALOG_EXPORT_IDF3( PCB_EDIT_FRAME* parent ) : + DIALOG_EXPORT_IDF3_BASE( parent ) + { + m_parent = parent; + m_config = Kiface().KifaceSettings(); + SetFocus(); + m_idfThouOpt = false; + m_config->Read( OPTKEY_IDF_THOU, &m_idfThouOpt ); + m_rbUnitSelection->SetSelection( m_idfThouOpt ? 1 : 0 ); + m_config->Read( OPTKEY_IDF_REF_AUTOADJ, &m_AutoAdjust, false ); + m_config->Read( OPTKEY_IDF_REF_UNITS, &m_RefUnits, 0 ); + m_config->Read( OPTKEY_IDF_REF_X, &m_XRef, 0.0 ); + m_config->Read( OPTKEY_IDF_REF_Y, &m_YRef, 0.0 ); + + m_cbAutoAdjustOffset->SetValue( m_AutoAdjust ); + m_cbAutoAdjustOffset->Bind( wxEVT_CHECKBOX, &DIALOG_EXPORT_IDF3::OnAutoAdjustOffset, this ); + + m_IDF_RefUnitChoice->SetSelection( m_RefUnits ); + wxString tmpStr; + tmpStr << m_XRef; + m_IDF_Xref->SetValue( tmpStr ); + tmpStr = wxT( "" ); + tmpStr << m_YRef; + m_IDF_Yref->SetValue( tmpStr ); + + if( m_AutoAdjust ) + { + m_IDF_RefUnitChoice->Enable( false ); + m_IDF_Xref->Enable( false ); + m_IDF_Yref->Enable( false ); + } + else + { + m_IDF_RefUnitChoice->Enable( true ); + m_IDF_Xref->Enable( true ); + m_IDF_Yref->Enable( true ); + } + + m_sdbSizerOK->SetDefault(); + + FixOSXCancelButtonIssue(); + + // Now all widgets have the size fixed, call FinishDialogSettings + FinishDialogSettings(); + } + + ~DIALOG_EXPORT_IDF3() + { + m_idfThouOpt = m_rbUnitSelection->GetSelection() == 1; + m_config->Write( OPTKEY_IDF_THOU, m_idfThouOpt ); + m_config->Write( OPTKEY_IDF_REF_AUTOADJ, GetAutoAdjustOffset() ); + m_config->Write( OPTKEY_IDF_REF_UNITS, m_IDF_RefUnitChoice->GetSelection() ); + m_config->Write( OPTKEY_IDF_REF_X, m_IDF_Xref->GetValue() ); + m_config->Write( OPTKEY_IDF_REF_Y, m_IDF_Yref->GetValue() ); + } + + bool GetThouOption() + { + return m_rbUnitSelection->GetSelection() == 1; + } + + wxFilePickerCtrl* FilePicker() + { + return m_filePickerIDF; + } + + int GetRefUnitsChoice() + { + return m_IDF_RefUnitChoice->GetSelection(); + } + + double GetXRef() + { + return DoubleValueFromString( UNSCALED_UNITS, m_IDF_Xref->GetValue() ); + } + + double GetYRef() + { + return DoubleValueFromString( UNSCALED_UNITS, m_IDF_Yref->GetValue() ); + } + + bool GetAutoAdjustOffset() + { + return m_cbAutoAdjustOffset->GetValue(); + } + + void OnAutoAdjustOffset( wxCommandEvent& event ) + { + if( GetAutoAdjustOffset() ) + { + m_IDF_RefUnitChoice->Enable( false ); + m_IDF_Xref->Enable( false ); + m_IDF_Yref->Enable( false ); + } + else + { + m_IDF_RefUnitChoice->Enable( true ); + m_IDF_Xref->Enable( true ); + m_IDF_Yref->Enable( true ); + } + + event.Skip(); + } + +}; + + +/** + * Function OnExportIDF3 + * will export the current BOARD to IDF board and lib files. + */ +void PCB_EDIT_FRAME::ExportToIDF3( wxCommandEvent& event ) +{ + wxFileName fn; + + // Build default file name + fn = GetBoard()->GetFileName(); + fn.SetExt( wxT( "emn" ) ); + + DIALOG_EXPORT_IDF3 dlg( this ); + dlg.FilePicker()->SetPath( fn.GetFullPath() ); + + if ( dlg.ShowModal() != wxID_OK ) + return; + + bool thou = dlg.GetThouOption(); + double aXRef; + double aYRef; + + if( dlg.GetAutoAdjustOffset() ) + { + EDA_RECT bbox = GetBoard()->ComputeBoundingBox( true ); + + aXRef = bbox.Centre().x * MM_PER_IU; + aYRef = bbox.Centre().y * MM_PER_IU; + } + else + { + aXRef = dlg.GetXRef(); + aYRef = dlg.GetYRef(); + + if( dlg.GetRefUnitsChoice() == 1 ) + { + // selected reference unit is in inches + aXRef *= 25.4; + aYRef *= 25.4; + } + + } + + wxBusyCursor dummy; + + wxString fullFilename = dlg.FilePicker()->GetPath(); + + if( !Export_IDF3( GetBoard(), fullFilename, thou, aXRef, aYRef ) ) + { + wxString msg = _( "Unable to create " ) + fullFilename; + wxMessageBox( msg ); + return; + } +} diff --git a/pcbnew/dialogs/wizard_3DShape_Libs_downloader.cpp b/pcbnew/dialogs/wizard_3DShape_Libs_downloader.cpp index 08e61ffc15..6523ed3f32 100644 --- a/pcbnew/dialogs/wizard_3DShape_Libs_downloader.cpp +++ b/pcbnew/dialogs/wizard_3DShape_Libs_downloader.cpp @@ -36,12 +36,14 @@ #include #include #include +#include #include #include #include #include #include <3d_viewer.h> +#include #include <../github/github_getliblist.h> diff --git a/pcbnew/editmod.cpp b/pcbnew/editmod.cpp index b2c9dc0691..8f0dfbb735 100644 --- a/pcbnew/editmod.cpp +++ b/pcbnew/editmod.cpp @@ -34,7 +34,7 @@ #include #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> #include #include @@ -66,6 +66,8 @@ void PCB_EDIT_FRAME::InstallModuleOptionsFrame( MODULE* Module, wxDC* DC ) * FP_PRM_EDITOR_RETVALUE::PRM_EDITOR_EDIT_OK for normal edition * FP_PRM_EDITOR_RETVALUE::PRM_EDITOR_WANT_MODEDIT for a goto editor command */ + + dlg->Close(); dlg->Destroy(); #ifdef __WXMAC__ diff --git a/pcbnew/exporters/export_idf.cpp b/pcbnew/exporters/export_idf.cpp index cd3f421b59..da14472b36 100644 --- a/pcbnew/exporters/export_idf.cpp +++ b/pcbnew/exporters/export_idf.cpp @@ -34,8 +34,12 @@ #include #include #include -#include <3d_struct.h> +#include <3d_cache/3d_info.h> #include +#include "project.h" +#include "kiway.h" +#include "3d_cache/3d_cache.h" +#include "3d_cache/3d_filename_resolver.h" #ifndef PCBNEW #define PCBNEW // needed to define the right value of Millimeter2iu(x) @@ -45,6 +49,8 @@ // assumed default graphical line thickness: == 0.1mm #define LINE_WIDTH (Millimeter2iu( 0.1 )) +static S3D_FILENAME_RESOLVER* resolver; + /** * Function idf_export_outline * retrieves line segment information from the edge layer and compiles @@ -373,11 +379,21 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule, IDF3_COMPONENT* comp = NULL; - for( S3D_MASTER* modfile = aModule->Models(); modfile != 0; modfile = modfile->Next() ) + std::list::const_iterator sM = aModule->Models().begin(); + std::list::const_iterator eM = aModule->Models().end(); + wxFileName idfFile; + wxString idfExt; + + while( sM != eM ) { - if( !modfile->Is3DType( S3D_MASTER::FILE3D_IDF ) - || modfile->GetShape3DFullFilename().empty() ) + idfFile.Assign( resolver->ResolvePath( sM->m_Filename ) ); + idfExt = idfFile.GetExt(); + + if( idfExt.Cmp( wxT( "idf" ) ) && idfExt.Cmp( wxT( "IDF" ) ) ) + { + ++sM; continue; + } if( refdes.empty() ) { @@ -393,16 +409,16 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule, IDF3_COMP_OUTLINE* outline; - outline = aIDFBoard.GetComponentOutline( modfile->GetShape3DFullFilename() ); + outline = aIDFBoard.GetComponentOutline( idfFile.GetFullPath() ); if( !outline ) throw( std::runtime_error( aIDFBoard.GetError() ) ); double rotz = aModule->GetOrientation()/10.0; - double locx = modfile->m_MatPosition.x * 25.4; // part offsets are in inches - double locy = modfile->m_MatPosition.y * 25.4; - double locz = modfile->m_MatPosition.z * 25.4; - double lrot = modfile->m_MatRotation.z; + double locx = sM->m_Offset.x * 25.4; // part offsets are in inches + double locy = sM->m_Offset.y * 25.4; + double locz = sM->m_Offset.z * 25.4; + double lrot = sM->m_Rotation.z; bool top = ( aModule->GetLayer() == B_Cu ) ? false : true; @@ -522,6 +538,7 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule, data->SetOffsets( locx, locy, locz, lrot ); comp->AddOutlineData( data ); + ++sM; } return; @@ -533,14 +550,16 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule, * generates IDFv3 compliant board (*.emn) and library (*.emp) * files representing the user's PCB design. */ -bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, bool aUseThou, - double aXRef, double aYRef ) +bool PCB_EDIT_FRAME::Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, + bool aUseThou, double aXRef, double aYRef ) { IDF3_BOARD idfBoard( IDF3::CAD_ELEC ); // Switch the locale to standard C (needed to print floating point numbers) LOCALE_IO toggle; + resolver = Prj().Get3DCacheManager()->GetResolver(); + bool ok = true; double scale = MM_PER_IU; // we must scale internal units to mm for IDF IDF3::IDF_UNIT idfUnit; diff --git a/pcbnew/exporters/export_vrml.cpp b/pcbnew/exporters/export_vrml.cpp index aa6e96c19b..c77a1f3ea1 100644 --- a/pcbnew/exporters/export_vrml.cpp +++ b/pcbnew/exporters/export_vrml.cpp @@ -4,7 +4,7 @@ * Copyright (C) 2009-2013 Lorenzo Mercantonio * Copyright (C) 2014 Cirilo Bernado * Copyright (C) 2013 Jean-Pierre Charras jp.charras at wanadoo.fr - * Copyright (C) 2004-2016 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,16 +26,20 @@ #include +#include "3d_cache/3d_cache.h" +#include "3d_cache/3d_filename_resolver.h" #include #include #include #include #include -#include <3d_struct.h> +#include <3d_cache/3d_info.h> #include #include #include #include +#include +#include #include @@ -47,8 +51,6 @@ #include #include -#include "../3d-viewer/modelparsers.h" - #include #include #include @@ -59,6 +61,7 @@ // offset for art layers, mm (silk, paste, etc) #define ART_OFFSET 0.025 +static S3D_FILENAME_RESOLVER* resolver; struct VRML_COLOR { @@ -1198,16 +1201,27 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule bool isFlipped = aModule->GetLayer() == B_Cu; // Export the object VRML model(s) - for( S3D_MASTER* vrmlm = aModule->Models(); vrmlm; vrmlm = vrmlm->Next() ) - { - if( !vrmlm->Is3DType( S3D_MASTER::FILE3D_VRML ) ) - continue; + std::list::iterator sM = aModule->Models().begin(); + std::list::iterator eM = aModule->Models().end(); + wxString wrlExt; - wxFileName modelFileName = vrmlm->GetShape3DFullFilename(); - wxFileName destFileName( a3D_Subdir, modelFileName.GetName(), modelFileName.GetExt() ); + while( sM != eM ) + { + wxFileName modelFileName = resolver->ResolvePath( sM->m_Filename ); + wrlExt = modelFileName.GetExt(); + + if( wrlExt.Cmp( wxT( "wrl" ) ) && wrlExt.Cmp( wxT( "WRL" ) ) + && wrlExt.Cmp( wxT( "x3d" ) ) && wrlExt.Cmp( wxT( "X3D" ) ) ) + { + ++sM; + continue; + } + + wxFileName destFileName( a3D_Subdir, modelFileName.GetName(), wrlExt ); // Only copy VRML files. - if( modelFileName.FileExists() && modelFileName.GetExt() == wxT( "wrl" ) ) + if( modelFileName.FileExists() + && ( wrlExt == wxT( "wrl" ) || wrlExt == wxT( "WRL" ) ) ) { if( aExport3DFiles ) { @@ -1237,9 +1251,9 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule * for footprints that are flipped * When flipped, axis rotation is the horizontal axis (X axis) */ - double rotx = -vrmlm->m_MatRotation.x; - double roty = -vrmlm->m_MatRotation.y; - double rotz = -vrmlm->m_MatRotation.z; + double rotx = -sM->m_Rotation.x; + double roty = -sM->m_Rotation.y; + double rotz = -sM->m_Rotation.z; if( isFlipped ) { @@ -1273,9 +1287,9 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule // adjust 3D shape local offset position // they are given in inch, so they are converted in board IU. - double offsetx = vrmlm->m_MatPosition.x * IU_PER_MILS * 1000.0; - double offsety = vrmlm->m_MatPosition.y * IU_PER_MILS * 1000.0; - double offsetz = vrmlm->m_MatPosition.z * IU_PER_MILS * 1000.0; + double offsetx = sM->m_Offset.x * IU_PER_MILS * 1000.0; + double offsety = sM->m_Offset.y * IU_PER_MILS * 1000.0; + double offsetz = sM->m_Offset.z * IU_PER_MILS * 1000.0; if( isFlipped ) offsetz = -offsetz; @@ -1292,9 +1306,9 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule aOutputFile << ( (offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) ) << "\n"; aOutputFile << " scale "; - aOutputFile << ( vrmlm->m_MatScale.x * aVRMLModelsToBiu ) << " "; - aOutputFile << ( vrmlm->m_MatScale.y * aVRMLModelsToBiu ) << " "; - aOutputFile << ( vrmlm->m_MatScale.z * aVRMLModelsToBiu ) << "\n"; + aOutputFile << ( sM->m_Scale.x * aVRMLModelsToBiu ) << " "; + aOutputFile << ( sM->m_Scale.y * aVRMLModelsToBiu ) << " "; + aOutputFile << ( sM->m_Scale.z * aVRMLModelsToBiu ) << "\n"; aOutputFile << " children [\n Inline {\n url \""; if( aUseRelativePaths ) @@ -1311,6 +1325,8 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule aOutputFile << TO_UTF8( fn ) << "\"\n } ]\n"; aOutputFile << " }\n"; } + + ++sM; } } @@ -1323,6 +1339,8 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, double aMMt BOARD* pcb = GetBoard(); bool ok = true; + resolver = Prj().Get3DCacheManager()->GetResolver(); + MODEL_VRML model3d; model3d.plainPCB = aUsePlainPCB; diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp index f3affba1c0..5ee6471877 100644 --- a/pcbnew/files.cpp +++ b/pcbnew/files.cpp @@ -35,7 +35,7 @@ #include #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> #include #include #include @@ -325,6 +325,7 @@ void PCB_EDIT_FRAME::Files_io_from_id( int id ) GetBoard()->SetFileName( fn.GetFullPath() ); UpdateTitle(); ReCreateLayerBox(); + OnModify(); break; } @@ -603,7 +604,7 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in SetMsgPanel( GetBoard() ); // Refresh the 3D view, if any - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->NewDisplay(); diff --git a/pcbnew/footprint_wizard.cpp b/pcbnew/footprint_wizard.cpp index f63f8e555b..6e9b17ec79 100644 --- a/pcbnew/footprint_wizard.cpp +++ b/pcbnew/footprint_wizard.cpp @@ -7,7 +7,7 @@ #include #include #include -#include <3d_viewer.h> +//#include <3d_viewer/eda_3d_viewer.h> #include #include diff --git a/pcbnew/footprint_wizard_frame.cpp b/pcbnew/footprint_wizard_frame.cpp index adc54b5ab2..6112fa6018 100644 --- a/pcbnew/footprint_wizard_frame.cpp +++ b/pcbnew/footprint_wizard_frame.cpp @@ -32,7 +32,7 @@ #include #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> #include #include @@ -221,7 +221,7 @@ FOOTPRINT_WIZARD_FRAME::FOOTPRINT_WIZARD_FRAME( KIWAY* aKiway, FOOTPRINT_WIZARD_FRAME::~FOOTPRINT_WIZARD_FRAME() { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->Destroy(); @@ -526,7 +526,7 @@ bool FOOTPRINT_WIZARD_FRAME::GeneralControl( wxDC* aDC, const wxPoint& aPosition void FOOTPRINT_WIZARD_FRAME::Show3D_Frame( wxCommandEvent& event ) { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) { @@ -544,7 +544,7 @@ void FOOTPRINT_WIZARD_FRAME::Show3D_Frame( wxCommandEvent& event ) return; } - draw3DFrame = new EDA_3D_FRAME( &Kiway(), this, wxEmptyString ); + draw3DFrame = new EDA_3D_VIEWER( &Kiway(), this, wxEmptyString ); Update3D_Frame( false ); draw3DFrame->Raise(); // Needed with some Window Managers draw3DFrame->Show( true ); @@ -558,7 +558,7 @@ void FOOTPRINT_WIZARD_FRAME::Show3D_Frame( wxCommandEvent& event ) */ void FOOTPRINT_WIZARD_FRAME::Update3D_Frame( bool aForceReloadFootprint ) { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame == NULL ) return; diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index b3c15409ab..d73e10783c 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -27,7 +27,6 @@ #include #include // LEGACY_BOARD_FILE_VERSION #include -#include <3d_struct.h> #include #include @@ -1099,30 +1098,34 @@ void PCB_IO::format( MODULE* aModule, int aNestLevel ) const format( pad, aNestLevel+1 ); // Save 3D info. - for( S3D_MASTER* t3D = aModule->Models(); t3D; t3D = t3D->Next() ) + std::list::const_iterator bs3D = aModule->Models().begin(); + std::list::const_iterator es3D = aModule->Models().end(); + + while( bs3D != es3D ) { - if( !t3D->GetShape3DName().IsEmpty() ) + if( !bs3D->m_Filename.IsEmpty() ) { m_out->Print( aNestLevel+1, "(model %s\n", - m_out->Quotew( t3D->GetShape3DName() ).c_str() ); + m_out->Quotew( bs3D->m_Filename ).c_str() ); m_out->Print( aNestLevel+2, "(at (xyz %s %s %s))\n", - Double2Str( t3D->m_MatPosition.x ).c_str(), - Double2Str( t3D->m_MatPosition.y ).c_str(), - Double2Str( t3D->m_MatPosition.z ).c_str() ); + Double2Str( bs3D->m_Offset.x ).c_str(), + Double2Str( bs3D->m_Offset.y ).c_str(), + Double2Str( bs3D->m_Offset.z ).c_str() ); m_out->Print( aNestLevel+2, "(scale (xyz %s %s %s))\n", - Double2Str( t3D->m_MatScale.x ).c_str(), - Double2Str( t3D->m_MatScale.y ).c_str(), - Double2Str( t3D->m_MatScale.z ).c_str() ); + Double2Str( bs3D->m_Scale.x ).c_str(), + Double2Str( bs3D->m_Scale.y ).c_str(), + Double2Str( bs3D->m_Scale.z ).c_str() ); m_out->Print( aNestLevel+2, "(rotate (xyz %s %s %s))\n", - Double2Str( t3D->m_MatRotation.x ).c_str(), - Double2Str( t3D->m_MatRotation.y ).c_str(), - Double2Str( t3D->m_MatRotation.z ).c_str() ); + Double2Str( bs3D->m_Rotation.x ).c_str(), + Double2Str( bs3D->m_Rotation.y ).c_str(), + Double2Str( bs3D->m_Rotation.z ).c_str() ); m_out->Print( aNestLevel+1, ")\n" ); } + ++bs3D; } m_out->Print( aNestLevel, ")\n" ); diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index 385d380923..41442d32a4 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -63,7 +63,7 @@ #include #include #include - +#include #include // implement this here #include @@ -80,7 +80,7 @@ #include #include #include -#include <3d_struct.h> +#include <3d_cache/3d_info.h> #include #include #include @@ -1879,16 +1879,7 @@ void LEGACY_PLUGIN::loadMODULE_TEXT( TEXTE_MODULE* aText ) void LEGACY_PLUGIN::load3D( MODULE* aModule ) { - S3D_MASTER* t3D = aModule->Models(); - - if( !t3D->GetShape3DName().IsEmpty() ) - { - S3D_MASTER* n3D = new S3D_MASTER( aModule ); - - aModule->Models().PushBack( n3D ); - - t3D = n3D; - } + S3D_INFO t3D; char* line; while( ( line = READLINE( m_reader ) ) != NULL ) @@ -1897,35 +1888,38 @@ void LEGACY_PLUGIN::load3D( MODULE* aModule ) { char buf[512]; ReadDelimitedText( buf, line + SZ( "Na" ), sizeof(buf) ); - t3D->SetShape3DName( FROM_UTF8( buf ) ); + t3D.m_Filename = buf; } else if( TESTLINE( "Sc" ) ) // Scale { sscanf( line + SZ( "Sc" ), "%lf %lf %lf\n", - &t3D->m_MatScale.x, - &t3D->m_MatScale.y, - &t3D->m_MatScale.z ); + &t3D.m_Scale.x, + &t3D.m_Scale.y, + &t3D.m_Scale.z ); } else if( TESTLINE( "Of" ) ) // Offset { sscanf( line + SZ( "Of" ), "%lf %lf %lf\n", - &t3D->m_MatPosition.x, - &t3D->m_MatPosition.y, - &t3D->m_MatPosition.z ); + &t3D.m_Offset.x, + &t3D.m_Offset.y, + &t3D.m_Offset.z ); } else if( TESTLINE( "Ro" ) ) // Rotation { sscanf( line + SZ( "Ro" ), "%lf %lf %lf\n", - &t3D->m_MatRotation.x, - &t3D->m_MatRotation.y, - &t3D->m_MatRotation.z ); + &t3D.m_Rotation.x, + &t3D.m_Rotation.y, + &t3D.m_Rotation.z ); } else if( TESTLINE( "$EndSHAPE3D" ) ) + { + aModule->Models().push_back( t3D ); return; // preferred exit + } } THROW_IO_ERROR( "Missing '$EndSHAPE3D'" ); @@ -3084,49 +3078,59 @@ void LEGACY_PLUGIN::init( const PROPERTIES* aProperties ) void LEGACY_PLUGIN::SaveModule3D( const MODULE* me ) const { - for( S3D_MASTER* t3D = me->Models(); t3D; t3D = t3D->Next() ) + std::list::const_iterator sM = me->Models().begin(); + std::list::const_iterator eM = me->Models().end(); + + while( sM != eM ) { - if( !t3D->GetShape3DName().IsEmpty() ) + if( sM->m_Filename.empty() ) { - fprintf( m_fp, "$SHAPE3D\n" ); - - fprintf( m_fp, "Na %s\n", EscapedUTF8( t3D->GetShape3DName() ).c_str() ); - - fprintf(m_fp, -#if defined(DEBUG) - // use old formats for testing, just to verify compatibility - // using "diff", then switch to more concise form for release builds. - "Sc %lf %lf %lf\n", -#else - "Sc %.10g %.10g %.10g\n", -#endif - t3D->m_MatScale.x, - t3D->m_MatScale.y, - t3D->m_MatScale.z ); - - fprintf(m_fp, -#if defined(DEBUG) - "Of %lf %lf %lf\n", -#else - "Of %.10g %.10g %.10g\n", -#endif - t3D->m_MatPosition.x, - t3D->m_MatPosition.y, - t3D->m_MatPosition.z ); - - fprintf(m_fp, -#if defined(DEBUG) - "Ro %lf %lf %lf\n", -#else - "Ro %.10g %.10g %.10g\n", -#endif - t3D->m_MatRotation.x, - t3D->m_MatRotation.y, - t3D->m_MatRotation.z ); - - fprintf( m_fp, "$EndSHAPE3D\n" ); + ++sM; + continue; } + + fprintf( m_fp, "$SHAPE3D\n" ); + + fprintf( m_fp, "Na %s\n", EscapedUTF8( sM->m_Filename ).c_str() ); + + fprintf(m_fp, +#if defined(DEBUG) + // use old formats for testing, just to verify compatibility + // using "diff", then switch to more concise form for release builds. + "Sc %lf %lf %lf\n", +#else + "Sc %.10g %.10g %.10g\n", +#endif + sM->m_Scale.x, + sM->m_Scale.y, + sM->m_Scale.z ); + + fprintf(m_fp, +#if defined(DEBUG) + "Of %lf %lf %lf\n", +#else + "Of %.10g %.10g %.10g\n", +#endif + sM->m_Offset.x, + sM->m_Offset.y, + sM->m_Offset.z ); + + fprintf(m_fp, +#if defined(DEBUG) + "Ro %lf %lf %lf\n", +#else + "Ro %.10g %.10g %.10g\n", +#endif + sM->m_Rotation.x, + sM->m_Rotation.y, + sM->m_Rotation.z ); + + fprintf( m_fp, "$EndSHAPE3D\n" ); + + ++sM; } + + return; } diff --git a/pcbnew/modedit.cpp b/pcbnew/modedit.cpp index fa02c907dc..bd84590725 100644 --- a/pcbnew/modedit.cpp +++ b/pcbnew/modedit.cpp @@ -34,7 +34,7 @@ #include #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> #include #include #include @@ -193,7 +193,7 @@ void FOOTPRINT_EDIT_FRAME::LoadModuleFromBoard( wxCommandEvent& event ) GetScreen()->ClearUndoRedoList(); GetScreen()->ClrModify(); - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->NewDisplay(); @@ -373,7 +373,7 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) updateView(); m_canvas->Refresh(); - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->NewDisplay(); @@ -521,7 +521,7 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) Zoom_Automatique( false ); m_canvas->Refresh(); { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->NewDisplay(); @@ -583,7 +583,7 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) Zoom_Automatique( false ); { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->NewDisplay(); diff --git a/pcbnew/modedit_onclick.cpp b/pcbnew/modedit_onclick.cpp index 8c2af77fc5..6df77a19e9 100644 --- a/pcbnew/modedit_onclick.cpp +++ b/pcbnew/modedit_onclick.cpp @@ -29,7 +29,6 @@ #include #include #include -#include <3d_viewer.h> #include #include diff --git a/pcbnew/moduleframe.cpp b/pcbnew/moduleframe.cpp index 0de7921578..c9eb59279f 100644 --- a/pcbnew/moduleframe.cpp +++ b/pcbnew/moduleframe.cpp @@ -39,7 +39,7 @@ #include #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> #include #include @@ -675,7 +675,7 @@ void FOOTPRINT_EDIT_FRAME::OnUpdateSelectCurrentLib( wxUpdateUIEvent& aEvent ) void FOOTPRINT_EDIT_FRAME::Show3D_Frame( wxCommandEvent& event ) { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) { @@ -693,7 +693,7 @@ void FOOTPRINT_EDIT_FRAME::Show3D_Frame( wxCommandEvent& event ) return; } - draw3DFrame = new EDA_3D_FRAME( &Kiway(), this, _( "3D Viewer" ) ); + draw3DFrame = new EDA_3D_VIEWER( &Kiway(), this, _( "3D Viewer" ) ); draw3DFrame->Raise(); // Needed with some Window Managers draw3DFrame->Show( true ); } @@ -740,7 +740,7 @@ void FOOTPRINT_EDIT_FRAME::OnModify() { PCB_BASE_FRAME::OnModify(); - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->ReloadRequest(); diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp index 379f1128d1..4347b08d5c 100644 --- a/pcbnew/modview_frame.cpp +++ b/pcbnew/modview_frame.cpp @@ -33,7 +33,7 @@ #include #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> #include #include #include @@ -291,7 +291,7 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME() { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->Destroy(); @@ -628,7 +628,7 @@ bool FOOTPRINT_VIEWER_FRAME::GeneralControl( wxDC* aDC, const wxPoint& aPosition void FOOTPRINT_VIEWER_FRAME::Show3D_Frame( wxCommandEvent& event ) { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) { @@ -646,7 +646,7 @@ void FOOTPRINT_VIEWER_FRAME::Show3D_Frame( wxCommandEvent& event ) return; } - draw3DFrame = new EDA_3D_FRAME( &Kiway(), this, wxEmptyString ); + draw3DFrame = new EDA_3D_VIEWER( &Kiway(), this, wxEmptyString ); Update3D_Frame( false ); draw3DFrame->Raise(); // Needed with some Window Managers draw3DFrame->Show( true ); @@ -655,7 +655,7 @@ void FOOTPRINT_VIEWER_FRAME::Show3D_Frame( wxCommandEvent& event ) void FOOTPRINT_VIEWER_FRAME::Update3D_Frame( bool aForceReloadFootprint ) { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame == NULL ) return; diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 6478f7ea49..239f8e4f3e 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -32,7 +32,6 @@ #include #include #include -#include <3d_struct.h> #include #include @@ -336,17 +335,16 @@ void PCB_PARSER::parseEDA_TEXT( EDA_TEXT* aText ) throw( PARSE_ERROR, IO_ERROR ) } -S3D_MASTER* PCB_PARSER::parse3DModel() throw( PARSE_ERROR, IO_ERROR ) +S3D_INFO* PCB_PARSER::parse3DModel() throw( PARSE_ERROR, IO_ERROR ) { wxCHECK_MSG( CurTok() == T_model, NULL, - wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as S3D_MASTER." ) ); + wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as S3D_INFO." ) ); T token; - std::unique_ptr< S3D_MASTER > n3D( new S3D_MASTER( NULL ) ); - + S3D_INFO* n3D = new S3D_INFO; NeedSYMBOLorNUMBER(); - n3D->SetShape3DName( FromUTF8() ); + n3D->m_Filename = FromUTF8().ToUTF8(); for( token = NextTok(); token != T_RIGHT; token = NextTok() ) { @@ -364,9 +362,9 @@ S3D_MASTER* PCB_PARSER::parse3DModel() throw( PARSE_ERROR, IO_ERROR ) if( token != T_xyz ) Expecting( T_xyz ); - n3D->m_MatPosition.x = parseDouble( "x value" ); - n3D->m_MatPosition.y = parseDouble( "y value" ); - n3D->m_MatPosition.z = parseDouble( "z value" ); + n3D->m_Offset.x = parseDouble( "x value" ); + n3D->m_Offset.y = parseDouble( "y value" ); + n3D->m_Offset.z = parseDouble( "z value" ); NeedRIGHT(); break; @@ -377,9 +375,9 @@ S3D_MASTER* PCB_PARSER::parse3DModel() throw( PARSE_ERROR, IO_ERROR ) if( token != T_xyz ) Expecting( T_xyz ); - n3D->m_MatScale.x = parseDouble( "x value" ); - n3D->m_MatScale.y = parseDouble( "y value" ); - n3D->m_MatScale.z = parseDouble( "z value" ); + n3D->m_Scale.x = parseDouble( "x value" ); + n3D->m_Scale.y = parseDouble( "y value" ); + n3D->m_Scale.z = parseDouble( "z value" ); NeedRIGHT(); break; @@ -390,9 +388,9 @@ S3D_MASTER* PCB_PARSER::parse3DModel() throw( PARSE_ERROR, IO_ERROR ) if( token != T_xyz ) Expecting( T_xyz ); - n3D->m_MatRotation.x = parseDouble( "x value" ); - n3D->m_MatRotation.y = parseDouble( "y value" ); - n3D->m_MatRotation.z = parseDouble( "z value" ); + n3D->m_Rotation.x = parseDouble( "x value" ); + n3D->m_Rotation.y = parseDouble( "y value" ); + n3D->m_Rotation.z = parseDouble( "z value" ); NeedRIGHT(); break; @@ -403,7 +401,7 @@ S3D_MASTER* PCB_PARSER::parse3DModel() throw( PARSE_ERROR, IO_ERROR ) NeedRIGHT(); } - return n3D.release(); + return n3D; } diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h index cd815ad22f..98c48d9ed8 100644 --- a/pcbnew/pcb_parser.h +++ b/pcbnew/pcb_parser.h @@ -35,6 +35,7 @@ #include // LAYER_ID #include // KiROUND #include // IU_PER_MM +#include <3d_cache/3d_info.h> class BOARD; @@ -50,7 +51,6 @@ class TRACK; class MODULE; class PCB_TARGET; class VIA; -class S3D_MASTER; class ZONE_CONTAINER; struct LAYER; @@ -203,7 +203,7 @@ class PCB_PARSER : public PCB_LEXER */ void parseEDA_TEXT( EDA_TEXT* aText ) throw( PARSE_ERROR, IO_ERROR ); - S3D_MASTER* parse3DModel() throw( PARSE_ERROR, IO_ERROR ); + S3D_INFO* parse3DModel() throw( PARSE_ERROR, IO_ERROR ); /** * Function parseDouble diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index 896d1d1576..967737732b 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -38,7 +38,7 @@ #include #include #include -#include <3d_viewer.h> +#include <3d_viewer/eda_3d_viewer.h> #include #include @@ -127,7 +127,7 @@ BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME ) EVT_MENU( ID_GEN_EXPORT_FILE_GENCADFORMAT, PCB_EDIT_FRAME::ExportToGenCAD ) EVT_MENU( ID_GEN_EXPORT_FILE_MODULE_REPORT, PCB_EDIT_FRAME::GenFootprintsReport ) EVT_MENU( ID_GEN_EXPORT_FILE_VRML, PCB_EDIT_FRAME::OnExportVRML ) - EVT_MENU( ID_GEN_EXPORT_FILE_IDF3, PCB_EDIT_FRAME::ExportToIDF3 ) + EVT_MENU( ID_GEN_EXPORT_FILE_IDF3, PCB_EDIT_FRAME::OnExportIDF3 ) EVT_MENU( ID_GEN_IMPORT_SPECCTRA_SESSION,PCB_EDIT_FRAME::ImportSpecctraSession ) EVT_MENU( ID_GEN_IMPORT_SPECCTRA_DESIGN, PCB_EDIT_FRAME::ImportSpecctraDesign ) @@ -633,7 +633,7 @@ void PCB_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) void PCB_EDIT_FRAME::Show3D_Frame( wxCommandEvent& event ) { - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) { @@ -651,7 +651,7 @@ void PCB_EDIT_FRAME::Show3D_Frame( wxCommandEvent& event ) return; } - draw3DFrame = new EDA_3D_FRAME( &Kiway(), this, _( "3D Viewer" ) ); + draw3DFrame = new EDA_3D_VIEWER( &Kiway(), this, _( "3D Viewer" ) ); draw3DFrame->SetDefaultFileName( GetBoard()->GetFileName() ); draw3DFrame->Raise(); // Needed with some Window Managers draw3DFrame->Show( true ); @@ -947,7 +947,7 @@ void PCB_EDIT_FRAME::OnModify( ) { PCB_BASE_FRAME::OnModify(); - EDA_3D_FRAME* draw3DFrame = Get3DViewerFrame(); + EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame(); if( draw3DFrame ) draw3DFrame->ReloadRequest(); diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp index e461d2c6a5..dc6eb63862 100644 --- a/pcbnew/pcbnew.cpp +++ b/pcbnew/pcbnew.cpp @@ -58,7 +58,6 @@ #include #include #include - extern bool IsWxPythonLoaded(); // Colors for layers and items @@ -94,7 +93,6 @@ wxString g_DocModulesFileName = wxT( "footprints_doc/footprints.pdf" ); */ DLIST g_CurrentTrackList; -KIWAY* TheKiway = NULL; namespace PCB { @@ -118,7 +116,6 @@ static struct IFACE : public KIFACE_I { case FRAME_PCB: frame = dynamic_cast< wxWindow* >( new PCB_EDIT_FRAME( aKiway, aParent ) ); - TheKiway = aKiway; #if defined( KICAD_SCRIPTING ) // give the scripting helpers access to our frame diff --git a/pcbnew/router/CMakeLists.txt b/pcbnew/router/CMakeLists.txt index 2f324def01..4de04686bc 100644 --- a/pcbnew/router/CMakeLists.txt +++ b/pcbnew/router/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories( ./ ../ ../../include + ../../3d-viewer ../../pcbnew ../../polygon ${INC_AFTER} diff --git a/pcbnew/toolbars_update_user_interface.cpp b/pcbnew/toolbars_update_user_interface.cpp index 79fd084284..95b38a2791 100644 --- a/pcbnew/toolbars_update_user_interface.cpp +++ b/pcbnew/toolbars_update_user_interface.cpp @@ -33,7 +33,6 @@ #include #include #include -#include <3d_viewer.h> #include #include #include