349 lines
10 KiB
C++
349 lines
10 KiB
C++
|
/*
|
||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||
|
*
|
||
|
* Copyright (C) 2023 Alex Shvartzkop <dudesuchamazing@gmail.com>
|
||
|
* Copyright (C) 2023 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 "pcbnew_utils_3d.h"
|
||
|
|
||
|
#include <memory>
|
||
|
|
||
|
#include <wx/filename.h>
|
||
|
#include <wx/wfstream.h>
|
||
|
#include <wx/log.h>
|
||
|
|
||
|
#include <TDocStd_Document.hxx>
|
||
|
#include <XCAFApp_Application.hxx>
|
||
|
#include <XCAFDoc_DocumentTool.hxx>
|
||
|
#include <XCAFDoc_ShapeTool.hxx>
|
||
|
#include <XCAFDoc_ColorTool.hxx>
|
||
|
|
||
|
#include <TopoDS.hxx>
|
||
|
#include <TopExp_Explorer.hxx>
|
||
|
|
||
|
#include <Bnd_Box.hxx>
|
||
|
#include <BRepBndLib.hxx>
|
||
|
|
||
|
#include <BRepTools.hxx>
|
||
|
#include <BRep_Builder.hxx>
|
||
|
#include <BRepBuilderAPI_Transform.hxx>
|
||
|
#include <BRepBuilderAPI_GTransform.hxx>
|
||
|
|
||
|
#include <STEPCAFControl_Reader.hxx>
|
||
|
#include <STEPCAFControl_Writer.hxx>
|
||
|
#include <Interface_Static.hxx>
|
||
|
|
||
|
#include <XCAFDoc_Color.hxx>
|
||
|
#include <Quantity_Color.hxx>
|
||
|
#include <ShapeCustom.hxx>
|
||
|
|
||
|
// precision for mesh creation; 0.07 should be good enough for ECAD viewing
|
||
|
#define USER_PREC ( 0.14 )
|
||
|
|
||
|
|
||
|
static void transformDoc( Handle( TDocStd_Document ) & source, Handle( TDocStd_Document ) & dest,
|
||
|
const gp_GTrsf& transform )
|
||
|
{
|
||
|
// transfer data from Source into a top level component of Dest
|
||
|
BRepBuilderAPI_GTransform brep( transform );
|
||
|
|
||
|
// s_assy = shape tool for the source
|
||
|
Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( source->Main() );
|
||
|
|
||
|
// retrieve all free shapes within the assembly
|
||
|
TDF_LabelSequence frshapes;
|
||
|
s_assy->GetFreeShapes( frshapes );
|
||
|
|
||
|
// d_assy = shape tool for the destination
|
||
|
Handle( XCAFDoc_ShapeTool ) d_assy = XCAFDoc_DocumentTool::ShapeTool( dest->Main() );
|
||
|
|
||
|
int nshapes = frshapes.Length();
|
||
|
int id = 1;
|
||
|
Handle( XCAFDoc_ColorTool ) scolor = XCAFDoc_DocumentTool::ColorTool( source->Main() );
|
||
|
Handle( XCAFDoc_ColorTool ) dcolor = XCAFDoc_DocumentTool::ColorTool( dest->Main() );
|
||
|
TopExp_Explorer dtop;
|
||
|
TopExp_Explorer stop;
|
||
|
|
||
|
while( id <= nshapes )
|
||
|
{
|
||
|
TopoDS_Shape shape = s_assy->GetShape( frshapes.Value( id ) );
|
||
|
|
||
|
if( !shape.IsNull() )
|
||
|
{
|
||
|
TopoDS_Shape transformed_shape( shape );
|
||
|
|
||
|
if( transform.Trsf().ScaleFactor() != 1.0 )
|
||
|
{
|
||
|
transformed_shape =
|
||
|
ShapeCustom::ScaleShape( shape, transform.Trsf().ScaleFactor() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
brep.Perform( shape, Standard_False );
|
||
|
|
||
|
if( brep.IsDone() )
|
||
|
transformed_shape = brep.Shape();
|
||
|
}
|
||
|
|
||
|
TDF_Label niulab = d_assy->AddShape( transformed_shape, false, false );
|
||
|
|
||
|
// check for per-surface colors
|
||
|
stop.Init( shape, TopAbs_FACE );
|
||
|
dtop.Init( d_assy->GetShape( niulab ), TopAbs_FACE );
|
||
|
|
||
|
while( stop.More() && dtop.More() )
|
||
|
{
|
||
|
Quantity_Color face_color;
|
||
|
|
||
|
TDF_Label tl;
|
||
|
|
||
|
// give priority to the base shape's color
|
||
|
if( s_assy->FindShape( stop.Current(), tl ) )
|
||
|
{
|
||
|
if( scolor->GetColor( tl, XCAFDoc_ColorSurf, face_color )
|
||
|
|| scolor->GetColor( tl, XCAFDoc_ColorGen, face_color )
|
||
|
|| scolor->GetColor( tl, XCAFDoc_ColorCurv, face_color ) )
|
||
|
{
|
||
|
dcolor->SetColor( dtop.Current(), face_color, XCAFDoc_ColorSurf );
|
||
|
}
|
||
|
}
|
||
|
else if( scolor->GetColor( stop.Current(), XCAFDoc_ColorSurf, face_color )
|
||
|
|| scolor->GetColor( stop.Current(), XCAFDoc_ColorGen, face_color )
|
||
|
|| scolor->GetColor( stop.Current(), XCAFDoc_ColorCurv, face_color ) )
|
||
|
{
|
||
|
dcolor->SetColor( dtop.Current(), face_color, XCAFDoc_ColorSurf );
|
||
|
}
|
||
|
|
||
|
stop.Next();
|
||
|
dtop.Next();
|
||
|
}
|
||
|
|
||
|
// check for per-solid colors
|
||
|
stop.Init( shape, TopAbs_SOLID );
|
||
|
dtop.Init( d_assy->GetShape( niulab ), TopAbs_SOLID, TopAbs_FACE );
|
||
|
|
||
|
while( stop.More() && dtop.More() )
|
||
|
{
|
||
|
Quantity_Color face_color;
|
||
|
|
||
|
TDF_Label tl;
|
||
|
|
||
|
// give priority to the base shape's color
|
||
|
if( s_assy->FindShape( stop.Current(), tl ) )
|
||
|
{
|
||
|
if( scolor->GetColor( tl, XCAFDoc_ColorSurf, face_color )
|
||
|
|| scolor->GetColor( tl, XCAFDoc_ColorGen, face_color )
|
||
|
|| scolor->GetColor( tl, XCAFDoc_ColorCurv, face_color ) )
|
||
|
{
|
||
|
dcolor->SetColor( dtop.Current(), face_color, XCAFDoc_ColorGen );
|
||
|
}
|
||
|
}
|
||
|
else if( scolor->GetColor( stop.Current(), XCAFDoc_ColorSurf, face_color )
|
||
|
|| scolor->GetColor( stop.Current(), XCAFDoc_ColorGen, face_color )
|
||
|
|| scolor->GetColor( stop.Current(), XCAFDoc_ColorCurv, face_color ) )
|
||
|
{
|
||
|
dcolor->SetColor( dtop.Current(), face_color, XCAFDoc_ColorSurf );
|
||
|
}
|
||
|
|
||
|
stop.Next();
|
||
|
dtop.Next();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
++id;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
|
||
|
VECTOR3D UTILS_BOX3D::GetCenter()
|
||
|
{
|
||
|
return VECTOR3D( ( m_min.x + m_max.x ) * 0.5, ( m_min.y + m_max.y ) * 0.5,
|
||
|
( m_min.z + m_max.z ) * 0.5 );
|
||
|
}
|
||
|
|
||
|
|
||
|
VECTOR3D UTILS_BOX3D::GetSize()
|
||
|
{
|
||
|
return VECTOR3D( m_max.x - m_min.x, m_max.y - m_min.y, m_max.z - m_min.z );
|
||
|
}
|
||
|
|
||
|
|
||
|
VECTOR3D& UTILS_BOX3D::Min()
|
||
|
{
|
||
|
return m_min;
|
||
|
}
|
||
|
|
||
|
|
||
|
VECTOR3D& UTILS_BOX3D::Max()
|
||
|
{
|
||
|
return m_max;
|
||
|
}
|
||
|
|
||
|
|
||
|
struct UTILS_STEP_MODEL::DATA
|
||
|
{
|
||
|
Handle( TDocStd_Document ) m_frontDoc;
|
||
|
Handle( TDocStd_Document ) m_backDoc;
|
||
|
STEPCAFControl_Reader m_cafReader;
|
||
|
};
|
||
|
|
||
|
|
||
|
UTILS_STEP_MODEL* UTILS_STEP_MODEL::LoadSTEP( const wxString& aFileName )
|
||
|
{
|
||
|
std::unique_ptr<UTILS_STEP_MODEL::DATA> data = std::make_unique<UTILS_STEP_MODEL::DATA>();
|
||
|
|
||
|
Handle( XCAFApp_Application ) m_app = XCAFApp_Application::GetApplication();
|
||
|
m_app->NewDocument( "MDTV-XCAF", data->m_frontDoc );
|
||
|
m_app->NewDocument( "MDTV-XCAF", data->m_backDoc );
|
||
|
|
||
|
STEPCAFControl_Reader reader;
|
||
|
IFSelect_ReturnStatus stat = reader.ReadFile( aFileName );
|
||
|
|
||
|
if( stat != IFSelect_RetDone )
|
||
|
return nullptr;
|
||
|
|
||
|
// Enable user-defined shape precision
|
||
|
if( !Interface_Static::SetIVal( "read.precision.mode", 1 ) )
|
||
|
return nullptr;
|
||
|
|
||
|
// Set the shape conversion precision to USER_PREC (default 0.0001 has too many triangles)
|
||
|
if( !Interface_Static::SetRVal( "read.precision.val", USER_PREC ) )
|
||
|
return nullptr;
|
||
|
|
||
|
// set other translation options
|
||
|
reader.SetColorMode( true ); // use model colors
|
||
|
reader.SetNameMode( true ); // use label names
|
||
|
reader.SetLayerMode( false ); // ignore LAYER data
|
||
|
|
||
|
if( !reader.Transfer( data->m_frontDoc ) )
|
||
|
return nullptr;
|
||
|
|
||
|
data->m_cafReader = reader;
|
||
|
|
||
|
UTILS_STEP_MODEL* model = new UTILS_STEP_MODEL();
|
||
|
model->m_data = data.release();
|
||
|
return model;
|
||
|
}
|
||
|
|
||
|
|
||
|
UTILS_STEP_MODEL::~UTILS_STEP_MODEL()
|
||
|
{
|
||
|
delete m_data;
|
||
|
}
|
||
|
|
||
|
|
||
|
UTILS_BOX3D UTILS_STEP_MODEL::GetBoundingBox()
|
||
|
{
|
||
|
Handle( XCAFDoc_ShapeTool ) assy =
|
||
|
XCAFDoc_DocumentTool::ShapeTool( m_data->m_frontDoc->Main() );
|
||
|
|
||
|
TDF_LabelSequence frshapes;
|
||
|
assy->GetFreeShapes( frshapes );
|
||
|
|
||
|
int nshapes = frshapes.Length();
|
||
|
Bnd_Box bbox;
|
||
|
UTILS_BOX3D result;
|
||
|
|
||
|
if( nshapes == 0 )
|
||
|
return result;
|
||
|
|
||
|
for( const TDF_Label& label : frshapes )
|
||
|
{
|
||
|
TopoDS_Shape shape = assy->GetShape( label );
|
||
|
BRepBndLib::Add( shape, bbox );
|
||
|
}
|
||
|
|
||
|
gp_Pnt min = bbox.CornerMin();
|
||
|
gp_Pnt max = bbox.CornerMax();
|
||
|
|
||
|
result.Min().x = min.X();
|
||
|
result.Min().y = min.Y();
|
||
|
result.Min().z = min.Z();
|
||
|
|
||
|
result.Max().x = max.X();
|
||
|
result.Max().y = max.Y();
|
||
|
result.Max().z = max.Z();
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
void UTILS_STEP_MODEL::Translate( double aX, double aY, double aZ )
|
||
|
{
|
||
|
// Create a translation transformation
|
||
|
gp_Trsf transform;
|
||
|
transform.SetTranslation( gp_Vec( aX, aY, aZ ) );
|
||
|
|
||
|
transformDoc( m_data->m_frontDoc, m_data->m_backDoc, transform );
|
||
|
std::swap( m_data->m_frontDoc, m_data->m_backDoc );
|
||
|
|
||
|
Handle( XCAFApp_Application ) m_app = XCAFApp_Application::GetApplication();
|
||
|
|
||
|
m_data->m_backDoc->Main().ForgetAllAttributes();
|
||
|
m_app->Close( m_data->m_backDoc );
|
||
|
m_app->NewDocument( "MDTV-XCAF", m_data->m_backDoc );
|
||
|
}
|
||
|
|
||
|
|
||
|
void UTILS_STEP_MODEL::Scale( double aScale )
|
||
|
{
|
||
|
|
||
|
// Create a scale transformation
|
||
|
gp_Trsf transform;
|
||
|
transform.SetScaleFactor( aScale );
|
||
|
|
||
|
transformDoc( m_data->m_frontDoc, m_data->m_backDoc, transform );
|
||
|
std::swap( m_data->m_frontDoc, m_data->m_backDoc );
|
||
|
|
||
|
Handle( XCAFApp_Application ) m_app = XCAFApp_Application::GetApplication();
|
||
|
|
||
|
m_data->m_backDoc->Main().ForgetAllAttributes();
|
||
|
m_app->Close( m_data->m_backDoc );
|
||
|
m_app->NewDocument( "MDTV-XCAF", m_data->m_backDoc );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool UTILS_STEP_MODEL::SaveSTEP( const wxString& aFileName )
|
||
|
{
|
||
|
STEPCAFControl_Writer writer;
|
||
|
writer.SetColorMode( true );
|
||
|
writer.SetNameMode( true );
|
||
|
|
||
|
// To reduce file size
|
||
|
if( !Interface_Static::SetIVal( "write.surfacecurve.mode", 0 ) )
|
||
|
return false;
|
||
|
|
||
|
if( !writer.Transfer( m_data->m_frontDoc, STEPControl_AsIs ) )
|
||
|
return false;
|
||
|
|
||
|
if( writer.Write( aFileName ) != IFSelect_RetDone )
|
||
|
return false;
|
||
|
|
||
|
Handle( XCAFApp_Application ) m_app = XCAFApp_Application::GetApplication();
|
||
|
|
||
|
m_data->m_frontDoc->Main().ForgetAllAttributes();
|
||
|
m_data->m_backDoc->Main().ForgetAllAttributes();
|
||
|
|
||
|
m_app->Close( m_data->m_frontDoc );
|
||
|
m_app->Close( m_data->m_backDoc );
|
||
|
|
||
|
return true;
|
||
|
}
|