altium: initial code to import STEP models

For now, they are extracted into the same directory as the project file.
Furthermore, there seems to be a problem with rotation / positioning of some models.
This commit is contained in:
Thomas Pointhuber 2020-04-21 15:34:06 +02:00 committed by Jon Evans
parent 76a100984a
commit 41a6791928
8 changed files with 246 additions and 1 deletions

View File

@ -81,8 +81,10 @@ BOARD* ALTIUM_CIRCUIT_MAKER_PLUGIN::Load(
{ ALTIUM_PCB_DIR::BOARDREGIONS, "E3A544335C30403A991912052C936F\\Data" },
{ ALTIUM_PCB_DIR::CLASSES6, "4F71DD45B09143988210841EA1C28D\\Data" },
{ ALTIUM_PCB_DIR::COMPONENTS6, "F9D060ACC7DD4A85BC73CB785BAC81\\Data" },
{ ALTIUM_PCB_DIR::COMPONENTBODIES6, "44D9487C98CE4F0EB46AB6E9CDAF40\\Data" }, // or: A0DB41FBCB0D49CE8C32A271AA7EF5 ?
{ ALTIUM_PCB_DIR::DIMENSIONS6, "068B9422DBB241258BA2DE9A6BA1A6\\Data" },
{ ALTIUM_PCB_DIR::FILLS6, "6FFE038462A940E9B422EFC8F5D85E\\Data" },
{ ALTIUM_PCB_DIR::MODELS, "0DB009C021D946C88F1B3A32DAE94B\\Data" },
{ ALTIUM_PCB_DIR::NETS6, "35D7CF51BB9B4875B3A138B32D80DC\\Data" },
{ ALTIUM_PCB_DIR::PADS6, "4F501041A9BC4A06BDBDAB67D3820E\\Data" },
{ ALTIUM_PCB_DIR::POLYGONS6, "A1931C8B0B084A61AA45146575FDD3\\Data" },

View File

@ -81,8 +81,10 @@ BOARD* ALTIUM_CIRCUIT_STUDIO_PLUGIN::Load(
{ ALTIUM_PCB_DIR::BOARDREGIONS, "8957CF30F167408D9D263D23FE7C89\\Data" },
{ ALTIUM_PCB_DIR::CLASSES6, "847EFBF87A5149B1AA326A52AD6357\\Data" },
{ ALTIUM_PCB_DIR::COMPONENTS6, "465416896A15486999A39C643935D2\\Data" },
{ ALTIUM_PCB_DIR::COMPONENTBODIES6, "1849D9B5512D452A93EABF4E40B122\\Data" }, // or B6AD30D75241498BA2536EBF001752 ?
{ ALTIUM_PCB_DIR::DIMENSIONS6, "16C81DBC13C447FF8B42A426677F3C\\Data" },
{ ALTIUM_PCB_DIR::FILLS6, "4E83BDC3253747F08E9006D7F57020\\Data" },
{ ALTIUM_PCB_DIR::MODELS, "C0F7599ECC6A4D648DF5BB557679AF\\Data" },
{ ALTIUM_PCB_DIR::NETS6, "D95A0DA2FE9047779A5194C127F30B\\Data" },
{ ALTIUM_PCB_DIR::PADS6, "47D69BC5107A4B8DB8DAA23E39C238\\Data" },
{ ALTIUM_PCB_DIR::POLYGONS6, "D7038392280E4E229B9D9B5426B295\\Data" },

View File

@ -81,8 +81,10 @@ BOARD* ALTIUM_DESIGNER_PLUGIN::Load(
{ ALTIUM_PCB_DIR::BOARDREGIONS, "BoardRegions\\Data" },
{ ALTIUM_PCB_DIR::CLASSES6, "Classes6\\Data" },
{ ALTIUM_PCB_DIR::COMPONENTS6, "Components6\\Data" },
{ ALTIUM_PCB_DIR::COMPONENTBODIES6, "ComponentBodies6\\Data" },
{ ALTIUM_PCB_DIR::DIMENSIONS6, "Dimensions6\\Data" },
{ ALTIUM_PCB_DIR::FILLS6, "Fills6\\Data" },
{ ALTIUM_PCB_DIR::MODELS, "Models\\Data" },
{ ALTIUM_PCB_DIR::NETS6, "Nets6\\Data" },
{ ALTIUM_PCB_DIR::PADS6, "Pads6\\Data" },
{ ALTIUM_PCB_DIR::POLYGONS6, "Polygons6\\Data" },

View File

@ -34,6 +34,7 @@ types:
record_id::text6: text
record_id::fill6: fill
record_id::region6: region
record_id::componentbody6: componentbody
arc:
seq:
@ -570,6 +571,26 @@ types:
# repeat-expr: vertices_num+1
# type: xyf2
componentbody:
seq:
- id: sub1_len
type: u4
- id: data
type: componentbody_sub1
size: sub1_len
componentbody_sub1:
seq:
- size: 7
- id: component
type: u2
- size: 9
- id: propterties_len
type: u4
- id: properties
size: propterties_len
type: str
xy:
seq:
- id: x
@ -609,6 +630,7 @@ enums:
0x05: text6
0x06: fill6
0x0b: region6
0x0c: componentbody6
boolean:
0: false

View File

@ -26,7 +26,6 @@
#include <ki_exception.h>
#include <math/util.h>
#include <wx/translation.h>
#include "altium_parser.h"
#include "altium_parser_pcb.h"
@ -378,6 +377,28 @@ ADIMENSION6::ADIMENSION6( ALTIUM_PARSER& aReader )
}
}
AMODEL::AMODEL( ALTIUM_PARSER& aReader )
{
std::map<wxString, wxString> properties = aReader.ReadProperties();
if( properties.empty() )
{
THROW_IO_ERROR( "Classes6 stream has no properties!" );
}
name = ALTIUM_PARSER::PropertiesReadString( properties, "NAME", "" );
id = ALTIUM_PARSER::PropertiesReadString( properties, "ID", "" );
isEmbedded = ALTIUM_PARSER::PropertiesReadBool( properties, "EMBED", false );
rotation.x = ALTIUM_PARSER::PropertiesReadDouble( properties, "ROTX", 0. );
rotation.y = ALTIUM_PARSER::PropertiesReadDouble( properties, "ROTY", 0. );
rotation.z = ALTIUM_PARSER::PropertiesReadDouble( properties, "ROTZ", 0. );
if( aReader.HasParsingError() )
{
THROW_IO_ERROR( "Classes6 stream was not parsed correctly" );
}
}
ANET6::ANET6( ALTIUM_PARSER& aReader )
{
std::map<wxString, wxString> properties = aReader.ReadProperties();
@ -569,6 +590,48 @@ AARC6::AARC6( ALTIUM_PARSER& aReader )
}
}
ACOMPONENTBODY6::ACOMPONENTBODY6( ALTIUM_PARSER& aReader )
{
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
if( recordtype != ALTIUM_RECORD::MODEL )
{
THROW_IO_ERROR( "ComponentsBodies6 stream has invalid recordtype" );
}
aReader.ReadAndSetSubrecordLength();
aReader.Skip( 7 );
component = aReader.Read<uint16_t>();
aReader.Skip( 9 );
std::map<wxString, wxString> properties = aReader.ReadProperties();
if( properties.empty() )
{
THROW_IO_ERROR( "ComponentsBodies6 stream has no properties" );
}
modelName = ALTIUM_PARSER::PropertiesReadString( properties, "MODEL.NAME", "" );
modelId = ALTIUM_PARSER::PropertiesReadString( properties, "MODELID", "" );
modelIsEmbedded = ALTIUM_PARSER::PropertiesReadBool( properties, "MODEL.EMBED", false );
modelPosition.x = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "MODEL.2D.X", "0mil" );
modelPosition.y = -ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "MODEL.2D.Y", "0mil" );
modelPosition.z = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "MODEL.3D.DZ", "0mil" );
modelRotation.x = ALTIUM_PARSER::PropertiesReadDouble( properties, "MODEL.3D.ROTX", 0. );
modelRotation.y = ALTIUM_PARSER::PropertiesReadDouble( properties, "MODEL.3D.ROTY", 0. );
modelRotation.z = ALTIUM_PARSER::PropertiesReadDouble( properties, "MODEL.3D.ROTZ", 0. );
rotation = ALTIUM_PARSER::PropertiesReadDouble( properties, "MODEL.2D.ROTATION", 0. );
aReader.SkipSubrecord();
if( aReader.HasParsingError() )
{
THROW_IO_ERROR( "Components6 stream was not parsed correctly" );
}
}
APAD6::APAD6( ALTIUM_PARSER& aReader )
{
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );

View File

@ -29,6 +29,7 @@
#include <memory>
#include <vector>
#include <class_module.h>
#include <wx/gdicmn.h>
// tthis constant specifies an unconnected net
@ -395,6 +396,17 @@ struct ADIMENSION6
explicit ADIMENSION6( ALTIUM_PARSER& aReader );
};
struct AMODEL
{
wxString name;
wxString id;
bool isEmbedded;
MODULE_3D_SETTINGS::VECTOR3D rotation;
explicit AMODEL( ALTIUM_PARSER& aReader );
};
struct ANET6
{
wxString name;
@ -486,6 +498,21 @@ struct AARC6
explicit AARC6( ALTIUM_PARSER& aReader );
};
struct ACOMPONENTBODY6
{
uint16_t component;
wxString modelName;
wxString modelId;
bool modelIsEmbedded;
MODULE_3D_SETTINGS::VECTOR3D modelPosition;
MODULE_3D_SETTINGS::VECTOR3D modelRotation;
double rotation;
explicit ACOMPONENTBODY6( ALTIUM_PARSER& aReader );
};
struct APAD6_SIZE_AND_SHAPE
{
ALTIUM_PAD_HOLE_SHAPE holeshape;

View File

@ -38,8 +38,13 @@
#include <compoundfilereader.h>
#include <convert_basic_shapes_to_polygon.h>
#include <project.h>
#include <trigo.h>
#include <utf.h>
#include <wx/docview.h>
#include <wx/mstream.h>
#include <wx/wfstream.h>
#include <wx/zstream.h>
void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName,
@ -350,6 +355,16 @@ void ALTIUM_PCB::Parse( const CFB::CompoundFileReader& aReader,
[this]( auto aReader, auto fileHeader ) {
this->ParseComponents6Data( aReader, fileHeader );
} },
{ true, ALTIUM_PCB_DIR::MODELS,
[this, aFileMapping]( auto aReader, auto fileHeader ) {
wxString dir( aFileMapping.at( ALTIUM_PCB_DIR::MODELS ) );
dir.RemoveLast( 4 ); // Remove "Data" from the path
this->ParseModelsData( aReader, fileHeader, dir );
} },
{ true, ALTIUM_PCB_DIR::COMPONENTBODIES6,
[this]( auto aReader, auto fileHeader ) {
this->ParseComponentsBodies6Data( aReader, fileHeader );
} },
{ true, ALTIUM_PCB_DIR::NETS6,
[this]( auto aReader, auto fileHeader ) {
this->ParseNets6Data( aReader, fileHeader );
@ -449,6 +464,9 @@ void ALTIUM_PCB::Parse( const CFB::CompoundFileReader& aReader,
{
module->CalculateBoundingBox();
}
// Otherwise we cannot save the imported board
m_board->SetModified();
}
int ALTIUM_PCB::GetNetCode( uint16_t aId ) const
@ -783,6 +801,66 @@ void ALTIUM_PCB::ParseComponents6Data(
}
void ALTIUM_PCB::ParseComponentsBodies6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
{
ALTIUM_PARSER reader( aReader, aEntry );
while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
{
ACOMPONENTBODY6 elem( reader ); // TODO: implement
if( elem.component == ALTIUM_COMPONENT_NONE )
{
continue; // TODO: we do not support components for the board yet
}
if( m_components.size() <= elem.component )
{
THROW_IO_ERROR( wxString::Format(
"ComponentsBodies6 stream tries to access component id %d of %d existing components",
elem.component, m_components.size() ) );
}
if( !elem.modelIsEmbedded )
{
continue;
}
auto modelTuple = m_models.find( elem.modelId );
if( modelTuple == m_models.end() )
{
THROW_IO_ERROR( wxString::Format(
"ComponentsBodies6 stream tries to access model id %s which does not exist",
elem.modelId ) );
}
MODULE* module = m_components.at( elem.component );
const wxPoint& modulePosition = module->GetPosition();
MODULE_3D_SETTINGS modelSettings;
modelSettings.m_Filename = modelTuple->second;
modelSettings.m_Offset.x = Iu2Millimeter( (int) elem.modelPosition.x - modulePosition.x );
modelSettings.m_Offset.y = -Iu2Millimeter( (int) elem.modelPosition.y - modulePosition.y );
modelSettings.m_Offset.z = Iu2Millimeter( (int) elem.modelPosition.z );
modelSettings.m_Rotation.x = NormalizeAngleDegrees( elem.modelRotation.x, -180, 180 );
modelSettings.m_Rotation.y = NormalizeAngleDegrees( elem.modelRotation.y + 180, -180, 180 );
modelSettings.m_Rotation.z = NormalizeAngleDegrees(
elem.modelRotation.z + module->GetOrientationDegrees() + 180, -180, 180 );
module->Models().push_back( modelSettings );
}
if( reader.GetRemainingBytes() != 0 )
{
THROW_IO_ERROR( "ComponentsBodies6 stream is not fully parsed" );
}
}
void ALTIUM_PCB::HelperParseDimensions6Linear( const ADIMENSION6& aElem )
{
if( aElem.referencePoint.size() != 2 )
@ -1035,6 +1113,50 @@ void ALTIUM_PCB::ParseDimensions6Data(
}
}
void ALTIUM_PCB::ParseModelsData( const CFB::CompoundFileReader& aReader,
const CFB::COMPOUND_FILE_ENTRY* aEntry, const wxString aRootDir )
{
ALTIUM_PARSER reader( aReader, aEntry );
wxString altiumModelsPath = wxPathOnly( m_board->GetFileName() );
wxSetEnv( PROJECT_VAR_NAME,
altiumModelsPath ); // TODO: set KIPRJMOD always after import (not only when loading project)?
int idx = 0;
while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
{
AMODEL elem( reader );
wxString stepPath = aRootDir + std::to_string( idx++ );
const CFB::COMPOUND_FILE_ENTRY* stepEntry = FindStream( aReader, stepPath.c_str() );
size_t stepSize = static_cast<size_t>( stepEntry->size );
std::unique_ptr<char[]> stepContent( new char[stepSize] );
// read file into buffer
aReader.ReadFile( stepEntry, 0, stepContent.get(), stepSize );
wxFileName storagePath( altiumModelsPath, elem.name );
wxMemoryInputStream stepStream( stepContent.get(), stepSize );
wxZlibInputStream zlibInputStream( stepStream );
wxFileOutputStream outputStream( storagePath.GetFullPath() );
outputStream.Write( zlibInputStream );
outputStream.Close();
m_models.insert( { elem.id, "${KIPRJMOD}/" + elem.name } ); // KIPRJMOD
}
if( reader.GetRemainingBytes() != 0 )
{
THROW_IO_ERROR( "Models stream is not fully parsed" );
}
}
void ALTIUM_PCB::ParseNets6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
{

View File

@ -138,6 +138,8 @@ private:
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseDimensions6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseModelsData( const CFB::CompoundFileReader& aReader,
const CFB::COMPOUND_FILE_ENTRY* aEntry, const wxString aRootDir );
void ParseNets6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParsePolygons6Data(
@ -148,6 +150,8 @@ private:
// Binary Format
void ParseArcs6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseComponentsBodies6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParsePads6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseVias6Data(
@ -181,6 +185,7 @@ private:
BOARD* m_board;
std::vector<MODULE*> m_components;
std::vector<ZONE_CONTAINER*> m_polygons;
std::map<wxString, wxString> m_models;
size_t m_num_nets;
std::map<ALTIUM_LAYER, PCB_LAYER_ID> m_layermap; // used to correctly map copper layers
std::map<ALTIUM_RULE_KIND, std::vector<ARULE6>> m_rules;