static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule, std::ofstream& aOutputFile, double aVRMLModelsToBiu, bool aExport3DFiles, const wxString& a3D_Subdir ) { // Reference and value if( aModule->Reference().IsVisible() ) export_vrml_text_module( &aModule->Reference() ); if( aModule->Value().IsVisible() ) export_vrml_text_module( &aModule->Value() ); // Export module edges for( EDA_ITEM* item = aModule->GraphicalItems(); item; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: export_vrml_text_module( static_cast<TEXTE_MODULE*>( item ) ); break; case PCB_MODULE_EDGE_T: export_vrml_edge_module( aModel, static_cast<EDGE_MODULE*>( item ), aModule->GetOrientation() ); break; default: break; } } // Export pads for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) export_vrml_pad( aModel, aPcb, pad ); 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; wxString fname = vrmlm->GetShape3DFullFilename(); fname.Replace( wxT( "\\" ), wxT( "/" ) ); wxString source_fname = fname; if( aExport3DFiles ) { // Change illegal characters in filenames ChangeIllegalCharacters( fname, true ); fname = a3D_Subdir + wxT( "/" ) + fname; if( !wxFileExists( fname ) ) wxCopyFile( source_fname, fname ); } /* Calculate 3D shape rotation: * this is the rotation parameters, with an additional 180 deg rotation * 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; if( isFlipped ) { rotx += 180.0; NEGATE( roty ); NEGATE( rotz ); } // Do some quaternion munching double q1[4], q2[4], rot[4]; build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 ); build_quat( 0, 1, 0, DEG2RAD( roty ), q2 ); compose_quat( q1, q2, q1 ); build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 ); compose_quat( q1, q2, q1 ); // Note here aModule->GetOrientation() is in 0.1 degrees, // so module rotation has to be converted to radians build_quat( 0, 0, 1, DECIDEG2RAD( aModule->GetOrientation() ), q2 ); compose_quat( q1, q2, q1 ); from_quat( q1, rot ); aOutputFile << "Transform {\n"; // A null rotation would fail the acos! if( rot[3] != 0.0 ) { aOutputFile << " rotation " << std::setprecision( 3 ); aOutputFile << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n"; } // 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; if( isFlipped ) NEGATE( offsetz ); else // In normal mode, Y axis is reversed in Pcbnew. NEGATE( offsety ); RotatePoint( &offsetx, &offsety, aModule->GetOrientation() ); aOutputFile << " translation " << std::setprecision( aModel.precision ); aOutputFile << (( offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx ) << " "; aOutputFile << ( -(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty ) << " "; 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"; if( fname.EndsWith( wxT( "x3d" ) ) ) { X3D_MODEL_PARSER* parser = new X3D_MODEL_PARSER( vrmlm ); if( parser ) { // embed x3d model in vrml format double vrml_to_x3d = aVRMLModelsToBiu; parser->Load( fname, vrml_to_x3d ); try { aOutputFile << " children [\n "; aOutputFile << TO_UTF8( parser->VRML2_representation() ) << " ]\n"; aOutputFile << " }\n"; } catch( const std::exception& e ) { delete parser; throw; } } } else { aOutputFile << " children [\n Inline {\n url \""; aOutputFile << TO_UTF8( fname ) << "\"\n } ]\n"; aOutputFile << " }\n"; } } }
/** * Function idf_export_module * retrieves information from all board modules, adds drill holes to * the DRILLED_HOLES or BOARD_OUTLINE section as appropriate, * compiles data for the PLACEMENT section and compiles data for * the library ELECTRICAL section. */ static void idf_export_module( BOARD* aPcb, MODULE* aModule, IDF3_BOARD& aIDFBoard ) { // Reference Designator std::string crefdes = TO_UTF8( aModule->GetReference() ); if( crefdes.empty() || !crefdes.compare( "~" ) ) { std::string cvalue = TO_UTF8( aModule->GetValue() ); // if both the RefDes and Value are empty or set to '~' the board owns the part, // otherwise associated parts of the module must be marked NOREFDES. if( cvalue.empty() || !cvalue.compare( "~" ) ) crefdes = "BOARD"; else crefdes = "NOREFDES"; } // TODO: If module cutouts are supported we must add code here // for( EDA_ITEM* item = aModule->GraphicalItems(); item != NULL; item = item->Next() ) // { // if( ( item->Type() != PCB_MODULE_EDGE_T ) // || (item->GetLayer() != Edge_Cuts ) ) continue; // code to export cutouts // } // Export pads double drill, x, y; double scale = aIDFBoard.GetUserScale(); IDF3::KEY_PLATING kplate; std::string pintype; std::string tstr; double dx, dy; aIDFBoard.GetUserOffset( dx, dy ); for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) { drill = (double) pad->GetDrillSize().x * scale; x = pad->GetPosition().x * scale + dx; y = -pad->GetPosition().y * scale + dy; // Export the hole on the edge layer if( drill > 0.0 ) { // plating if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) kplate = IDF3::NPTH; else kplate = IDF3::PTH; // hole type tstr = TO_UTF8( pad->GetPadName() ); if( tstr.empty() || !tstr.compare( "0" ) || !tstr.compare( "~" ) || ( kplate == IDF3::NPTH ) ||( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ) ) pintype = "MTG"; else pintype = "PIN"; // fields: // 1. hole dia. : float // 2. X coord : float // 3. Y coord : float // 4. plating : PTH | NPTH // 5. Assoc. part : BOARD | NOREFDES | PANEL | {"refdes"} // 6. type : PIN | VIA | MTG | TOOL | { "other" } // 7. owner : MCAD | ECAD | UNOWNED if( ( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ) && ( pad->GetDrillSize().x != pad->GetDrillSize().y ) ) { // NOTE: IDF does not have direct support for slots; // slots are implemented as a board cutout and we // cannot represent plating or reference designators double dlength = pad->GetDrillSize().y * scale; // NOTE: The orientation of modules and pads have // the opposite sense due to KiCad drawing on a // screen with a LH coordinate system double angle = pad->GetOrientation() / 10.0; // NOTE: Since this code assumes the scenario where // GetDrillSize().y is the length but idf_parser.cpp // assumes a length along the X axis, the orientation // must be shifted +90 deg when GetDrillSize().y is // the major axis. if( dlength < drill ) { std::swap( drill, dlength ); } else { angle += 90.0; } // NOTE: KiCad measures a slot's length from end to end // rather than between the centers of the arcs dlength -= drill; aIDFBoard.AddSlot( drill, dlength, angle, x, y ); } else { IDF_DRILL_DATA *dp = new IDF_DRILL_DATA( drill, x, y, kplate, crefdes, pintype, IDF3::ECAD ); if( !aIDFBoard.AddDrill( dp ) ) { delete dp; std::ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__; ostr << "(): could not add drill"; throw std::runtime_error( ostr.str() ); } } } } // add any valid models to the library item list std::string refdes; IDF3_COMPONENT* comp = NULL; for( S3D_MASTER* modfile = aModule->Models(); modfile != 0; modfile = modfile->Next() ) { if( !modfile->Is3DType( S3D_MASTER::FILE3D_IDF ) || modfile->GetShape3DFullFilename().empty() ) continue; if( refdes.empty() ) { refdes = TO_UTF8( aModule->GetReference() ); // NOREFDES cannot be used or else the software gets confused // when writing out the placement data due to conflicting // placement and layer specifications; to work around this we // create a (hopefully) unique refdes for our exported part. if( refdes.empty() || !refdes.compare( "~" ) ) refdes = aIDFBoard.GetNewRefDes(); } IDF3_COMP_OUTLINE* outline; outline = aIDFBoard.GetComponentOutline( modfile->GetShape3DFullFilename() ); 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; bool top = ( aModule->GetLayer() == B_Cu ) ? false : true; if( top ) { locy = -locy; RotatePoint( &locx, &locy, aModule->GetOrientation() ); locy = -locy; } if( !top ) { lrot = -lrot; RotatePoint( &locx, &locy, aModule->GetOrientation() ); locy = -locy; rotz = 180.0 - rotz; if( rotz >= 360.0 ) while( rotz >= 360.0 ) rotz -= 360.0; if( rotz <= -360.0 ) while( rotz <= -360.0 ) rotz += 360.0; } if( comp == NULL ) comp = aIDFBoard.FindComponent( refdes ); if( comp == NULL ) { comp = new IDF3_COMPONENT( &aIDFBoard ); if( comp == NULL ) throw( std::runtime_error( aIDFBoard.GetError() ) ); comp->SetRefDes( refdes ); if( top ) comp->SetPosition( aModule->GetPosition().x * scale + dx, -aModule->GetPosition().y * scale + dy, rotz, IDF3::LYR_TOP ); else comp->SetPosition( aModule->GetPosition().x * scale + dx, -aModule->GetPosition().y * scale + dy, rotz, IDF3::LYR_BOTTOM ); comp->SetPlacement( IDF3::PS_ECAD ); aIDFBoard.AddComponent( comp ); } else { double refX, refY, refA; IDF3::IDF_LAYER side; if( ! comp->GetPosition( refX, refY, refA, side ) ) { // place the item if( top ) comp->SetPosition( aModule->GetPosition().x * scale + dx, -aModule->GetPosition().y * scale + dy, rotz, IDF3::LYR_TOP ); else comp->SetPosition( aModule->GetPosition().x * scale + dx, -aModule->GetPosition().y * scale + dy, rotz, IDF3::LYR_BOTTOM ); comp->SetPlacement( IDF3::PS_ECAD ); } else { // check that the retrieved component matches this one refX = refX - ( aModule->GetPosition().x * scale + dx ); refY = refY - ( -aModule->GetPosition().y * scale + dy ); refA = refA - rotz; refA *= refA; refX *= refX; refY *= refY; refX += refY; // conditions: same side, X,Y coordinates within 10 microns, // angle within 0.01 degree if( ( top && side == IDF3::LYR_BOTTOM ) || ( !top && side == IDF3::LYR_TOP ) || ( refA > 0.0001 ) || ( refX > 0.0001 ) ) { comp->GetPosition( refX, refY, refA, side ); std::ostringstream ostr; ostr << "* " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* conflicting Reference Designator '" << refdes << "'\n"; ostr << "* X loc: " << (aModule->GetPosition().x * scale + dx); ostr << " vs. " << refX << "\n"; ostr << "* Y loc: " << (-aModule->GetPosition().y * scale + dy); ostr << " vs. " << refY << "\n"; ostr << "* angle: " << rotz; ostr << " vs. " << refA << "\n"; if( top ) ostr << "* TOP vs. "; else ostr << "* BOTTOM vs. "; if( side == IDF3::LYR_TOP ) ostr << "TOP"; else ostr << "BOTTOM"; throw( std::runtime_error( ostr.str() ) ); } } } // create the local data ... IDF3_COMP_OUTLINE_DATA* data = new IDF3_COMP_OUTLINE_DATA( comp, outline ); data->SetOffsets( locx, locy, locz, lrot ); comp->AddOutlineData( data ); } return; }
static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule, std::ofstream& aOutputFile, double aVRMLModelsToBiu, bool aExport3DFiles, bool aUseRelativePaths, const wxString& a3D_Subdir ) { // Reference and value if( aModule->Reference().IsVisible() ) export_vrml_text_module( &aModule->Reference() ); if( aModule->Value().IsVisible() ) export_vrml_text_module( &aModule->Value() ); // Export module edges for( EDA_ITEM* item = aModule->GraphicalItems(); item; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: export_vrml_text_module( static_cast<TEXTE_MODULE*>( item ) ); break; case PCB_MODULE_EDGE_T: export_vrml_edge_module( aModel, static_cast<EDGE_MODULE*>( item ), aModule->GetOrientation() ); break; default: break; } } // Export pads for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) export_vrml_pad( aModel, aPcb, pad ); 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; wxFileName modelFileName = vrmlm->GetShape3DFullFilename(); wxFileName destFileName( a3D_Subdir, modelFileName.GetName(), modelFileName.GetExt() ); // Only copy VRML files. if( modelFileName.FileExists() && modelFileName.GetExt() == wxT( "wrl" ) ) { if( aExport3DFiles ) { wxDateTime srcModTime = modelFileName.GetModificationTime(); wxDateTime destModTime = srcModTime; destModTime.SetToCurrent(); if( destFileName.FileExists() ) destModTime = destFileName.GetModificationTime(); // Only copy the file if it doesn't exist or has been modified. This eliminates // the redundant file copies. if( srcModTime != destModTime ) { wxLogDebug( wxT( "Copying 3D model %s to %s." ), GetChars( modelFileName.GetFullPath() ), GetChars( destFileName.GetFullPath() ) ); if( !wxCopyFile( modelFileName.GetFullPath(), destFileName.GetFullPath() ) ) continue; } } /* Calculate 3D shape rotation: * this is the rotation parameters, with an additional 180 deg rotation * 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; if( isFlipped ) { rotx += 180.0; NEGATE( roty ); NEGATE( rotz ); } // Do some quaternion munching double q1[4], q2[4], rot[4]; build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 ); build_quat( 0, 1, 0, DEG2RAD( roty ), q2 ); compose_quat( q1, q2, q1 ); build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 ); compose_quat( q1, q2, q1 ); // Note here aModule->GetOrientation() is in 0.1 degrees, // so module rotation has to be converted to radians build_quat( 0, 0, 1, DECIDEG2RAD( aModule->GetOrientation() ), q2 ); compose_quat( q1, q2, q1 ); from_quat( q1, rot ); aOutputFile << "Transform {\n"; // A null rotation would fail the acos! if( rot[3] != 0.0 ) { aOutputFile << " rotation " << std::setprecision( 3 ); aOutputFile << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n"; } // 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; if( isFlipped ) NEGATE( offsetz ); else // In normal mode, Y axis is reversed in Pcbnew. NEGATE( offsety ); RotatePoint( &offsetx, &offsety, aModule->GetOrientation() ); aOutputFile << " translation " << std::setprecision( aModel.precision ); aOutputFile << ( ( offsetx + aModule->GetPosition().x ) * aModel.scale + aModel.tx ) << " "; aOutputFile << ( -(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty ) << " "; 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 << " children [\n Inline {\n url \""; if( aUseRelativePaths ) { wxFileName tmp = destFileName; tmp.SetExt( wxT( "" ) ); tmp.SetName( wxT( "" ) ); tmp.RemoveLastDir(); destFileName.MakeRelativeTo( tmp.GetPath() ); } wxString fn = destFileName.GetFullPath(); fn.Replace( wxT( "\\" ), wxT( "/" ) ); aOutputFile << TO_UTF8( fn ) << "\"\n } ]\n"; aOutputFile << " }\n"; } } }
MODULE::MODULE( const MODULE& aModule ) : BOARD_ITEM( aModule ) { m_Pos = aModule.m_Pos; m_fpid = aModule.m_fpid; m_Layer = aModule.m_Layer; m_Attributs = aModule.m_Attributs; m_ModuleStatus = aModule.m_ModuleStatus; m_Orient = aModule.m_Orient; m_BoundaryBox = aModule.m_BoundaryBox; m_CntRot90 = aModule.m_CntRot90; m_CntRot180 = aModule.m_CntRot180; m_LastEditTime = aModule.m_LastEditTime; m_Link = aModule.m_Link; m_Path = aModule.m_Path; //is this correct behavior? m_LocalClearance = aModule.m_LocalClearance; m_LocalSolderMaskMargin = aModule.m_LocalSolderMaskMargin; m_LocalSolderPasteMargin = aModule.m_LocalSolderPasteMargin; m_LocalSolderPasteMarginRatio = aModule.m_LocalSolderPasteMarginRatio; m_ZoneConnection = aModule.m_ZoneConnection; m_ThermalWidth = aModule.m_ThermalWidth; m_ThermalGap = aModule.m_ThermalGap; // Copy reference and value. m_Reference = new TEXTE_MODULE( *aModule.m_Reference ); m_Reference->SetParent( this ); m_Value = new TEXTE_MODULE( *aModule.m_Value ); m_Value->SetParent( this ); // Copy auxiliary data: Pads for( D_PAD* pad = aModule.m_Pads; pad; pad = pad->Next() ) { D_PAD* newpad = new D_PAD( *pad ); assert( newpad->GetNet() == pad->GetNet() ); newpad->SetParent( this ); m_Pads.PushBack( newpad ); } // Copy auxiliary data: Drawings for( BOARD_ITEM* item = aModule.m_Drawings; item; item = item->Next() ) { BOARD_ITEM* newItem; switch( item->Type() ) { case PCB_MODULE_TEXT_T: case PCB_MODULE_EDGE_T: newItem = static_cast<BOARD_ITEM*>( item->Clone() ); newItem->SetParent( this ); m_Drawings.PushBack( newItem ); break; default: wxLogMessage( wxT( "MODULE::Copy() Internal Err: unknown type" ) ); break; } } // 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_Doc = aModule.m_Doc; m_KeyWord = aModule.m_KeyWord; m_arflag = 0; // Ensure auxiliary data is up to date CalculateBoundingBox(); m_initial_comments = aModule.m_initial_comments ? new wxArrayString( *aModule.m_initial_comments ) : 0; }
void MODULE::Copy( MODULE* aModule ) { m_Pos = aModule->m_Pos; m_Layer = aModule->m_Layer; m_fpid = aModule->m_fpid; m_Attributs = aModule->m_Attributs; m_ModuleStatus = aModule->m_ModuleStatus; m_Orient = aModule->m_Orient; m_BoundaryBox = aModule->m_BoundaryBox; m_CntRot90 = aModule->m_CntRot90; m_CntRot180 = aModule->m_CntRot180; m_LastEditTime = aModule->m_LastEditTime; m_Link = aModule->m_Link; m_Path = aModule->m_Path; //is this correct behavior? SetTimeStamp( GetNewTimeStamp() ); m_LocalClearance = aModule->m_LocalClearance; m_LocalSolderMaskMargin = aModule->m_LocalSolderMaskMargin; m_LocalSolderPasteMargin = aModule->m_LocalSolderPasteMargin; m_LocalSolderPasteMarginRatio = aModule->m_LocalSolderPasteMarginRatio; m_ZoneConnection = aModule->m_ZoneConnection; m_ThermalWidth = aModule->m_ThermalWidth; m_ThermalGap = aModule->m_ThermalGap; // Copy reference and value. m_Reference->Copy( aModule->m_Reference ); m_Value->Copy( aModule->m_Value ); // Copy auxiliary data: Pads m_Pads.DeleteAll(); for( D_PAD* pad = aModule->m_Pads; pad; pad = pad->Next() ) { D_PAD* newpad = new D_PAD( this ); newpad->Copy( pad ); m_Pads.PushBack( newpad ); } // Copy auxiliary data: Drawings m_Drawings.DeleteAll(); for( BOARD_ITEM* item = aModule->m_Drawings; item; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: { TEXTE_MODULE* textm = new TEXTE_MODULE( this ); textm->Copy( static_cast<TEXTE_MODULE*>( item ) ); m_Drawings.PushBack( textm ); break; } case PCB_MODULE_EDGE_T: { EDGE_MODULE * edge; edge = new EDGE_MODULE( this ); edge->Copy( (EDGE_MODULE*) item ); m_Drawings.PushBack( edge ); break; } default: wxLogMessage( wxT( "MODULE::Copy() Internal Err: unknown type" ) ); break; } } // 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; // Ensure auxiliary data is up to date CalculateBoundingBox(); }
static void export_vrml_module( BOARD* aPcb, MODULE* aModule, FILE* aOutputFile, double aVRMLModelsToBiu, bool aExport3DFiles, const wxString & a3D_Subdir, double boardIU2WRML ) { // Reference and value export_vrml_text_module( aModule->m_Reference ); export_vrml_text_module( aModule->m_Value ); // Export module edges for( EDA_ITEM* item = aModule->m_Drawings; item != NULL; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: export_vrml_text_module( dynamic_cast<TEXTE_MODULE*>(item) ); break; case PCB_MODULE_EDGE_T: export_vrml_edge_module( dynamic_cast<EDGE_MODULE*>(item) ); break; default: break; } } // Export pads for( D_PAD* pad = aModule->m_Pads; pad; pad = pad->Next() ) export_vrml_pad( aPcb, pad ); bool isFlipped = aModule->GetLayer() == LAYER_N_BACK; // Export the object VRML model(s) for( S3D_MASTER* vrmlm = aModule->m_3D_Drawings; vrmlm != 0; vrmlm = vrmlm->Next() ) { wxString fname = vrmlm->m_Shape3DName; if( fname.IsEmpty() ) continue; if( ! wxFileName::FileExists( fname ) ) { wxFileName fn = fname; fname = wxGetApp().FindLibraryPath( fn ); if( fname.IsEmpty() ) // keep "short" name if full filemane not found fname = vrmlm->m_Shape3DName; } fname.Replace(wxT("\\"), wxT("/" ) ); wxString source_fname = fname; if( aExport3DFiles ) // Change illegal characters in short filename { ChangeIllegalCharacters( fname, true ); fname = a3D_Subdir + wxT("/") + fname; if( !wxFileExists( fname ) ) wxCopyFile( source_fname, fname ); } /* Calculate 3D shape rotation: * this is the rotation parameters, with an additional 180 deg rotation * 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; if ( isFlipped ) { rotx += 180.0; NEGATE(roty); NEGATE(rotz); } // Do some quaternion munching double q1[4], q2[4], rot[4]; build_quat( 1, 0, 0, rotx / 180.0 * M_PI, q1 ); build_quat( 0, 1, 0, roty / 180.0 * M_PI, q2 ); compose_quat( q1, q2, q1 ); build_quat( 0, 0, 1, rotz / 180.0 * M_PI, q2 ); compose_quat( q1, q2, q1 ); // Note here aModule->GetOrientation() is in 0.1 degrees, // so module rotation is aModule->GetOrientation() / 1800.0 build_quat( 0, 0, 1, aModule->GetOrientation() / 1800.0 * M_PI, q2 ); compose_quat( q1, q2, q1 ); from_quat( q1, rot ); fprintf( aOutputFile, "Transform {\n" ); // A null rotation would fail the acos! if( rot[3] != 0.0 ) { fprintf( aOutputFile, " rotation %g %g %g %g\n", rot[0], rot[1], rot[2], rot[3] ); } // adjust 3D shape offset position int offsetx = vrmlm->m_MatPosition.x; int offsety = vrmlm->m_MatPosition.y; double offsetz = vrmlm->m_MatPosition.z; if ( isFlipped ) NEGATE(offsetz); else // In normal mode, Y axis is reversed in Pcbnew. NEGATE(offsety); RotatePoint(&offsetx, &offsety, aModule->GetOrientation()); fprintf( aOutputFile, " translation %g %g %g\n", (double) (offsetx + aModule->m_Pos.x) * boardIU2WRML, - (double)(offsety + aModule->m_Pos.y) * boardIU2WRML, // Y axis is reversed in Pcbnew offsetz + layer_z[aModule->GetLayer()] * boardIU2WRML); fprintf( aOutputFile, " scale %g %g %g\n", vrmlm->m_MatScale.x * aVRMLModelsToBiu, vrmlm->m_MatScale.y * aVRMLModelsToBiu, vrmlm->m_MatScale.z * aVRMLModelsToBiu ); fprintf( aOutputFile, // " children [\n Inline {\n url \"file://%s\"\n } ]\n", " children [\n Inline {\n url \"%s\"\n } ]\n", TO_UTF8( fname ) ); fprintf( aOutputFile, " }\n" ); } }