はじめに
C# から Open CASCAD Technology (C++) を参照する備忘録です。続きです。
改訂履歴
- 2026/07/22 : 初版公開。
本文
1. 環境
- .NET Framework 4.8.1
- Open CASCADE Technology 8.0.0
- Visual Studio Community 2022
2. 手順
2-1. C++ プログラムの作成
トポロジー形状データを保持する C++ クラスを作ります(厳密には異なるようです)。
#pragma once
#include <TopoDS_Shape.hxx>
#include <vector>
struct NativePoint3D
{
double X;
double Y;
double Z;
};
class NativeOcctShape
{
public:
explicit NativeOcctShape(const TopoDS_Shape& shape);
TopoDS_Shape GetShape() const;
std::vector<NativePoint3D> GetVertices() const;
private:
TopoDS_Shape m_shape;
};
#include "NativeOcctShape.h"
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Vertex.hxx>
#include <BRep_Tool.hxx>
#include <gp_Pnt.hxx>
NativeOcctShape::NativeOcctShape(const TopoDS_Shape& shape)
: m_shape(shape)
{
}
TopoDS_Shape NativeOcctShape::GetShape() const
{
return m_shape;
}
std::vector<NativePoint3D> NativeOcctShape::GetVertices() const
{
std::vector<NativePoint3D> result;
for (TopExp_Explorer exp(m_shape, TopAbs_VERTEX); exp.More(); exp.Next())
{
TopoDS_Vertex vertex = TopoDS::Vertex(exp.Current());
gp_Pnt p = BRep_Tool::Pnt(vertex);
result.push_back({ p.X(), p.Y(), p.Z() });
}
return result;
}
次に STEP ファイルの読込クラスを作成します。
#pragma once
#include "NativeOcctShape.h"
class NativeStepReader
{
public:
static NativeOcctShape* Load(const char* path);
};
#pragma once
#include "NativeStepReader.h"
#include "NativeOcctShape.h"
#include <IFSelect_ReturnStatus.hxx>
#include <STEPControl_Reader.hxx>
#include <TopoDS_Shape.hxx>
NativeOcctShape* NativeStepReader::Load(const char* path)
{
// 読込する。
STEPControl_Reader reader;
IFSelect_ReturnStatus status = reader.ReadFile(path);
if (status != IFSelect_RetDone)
{
return nullptr;
}
// 読込したデータを形状クラスに変換する。
reader.TransferRoots();
TopoDS_Shape readShape = reader.OneShape();
if (readShape.IsNull())
{
return nullptr;
}
// 結果を返す。
NativeOcctShape* result = new NativeOcctShape(readShape);
return result;
}
作成した NativeOcctShape.cpp・NativeStepReader.cpp のプロパティを開き、Debug / Release 構成共に共通言語ランタイムサポートは使用しないように設定します。
2-2. C++/CLI プログラムの作成
C++/CLI 側を作っていきます。トポロジー形状データを保持するクラスを作ります。
#pragma once
class NativeOcctShape;
namespace OcctLibrary
{
public value struct Point3D
{
public:
double X;
double Y;
double Z;
};
public ref class OcctShape
{
public:
OcctShape();
~OcctShape();
!OcctShape();
array<Point3D>^ GetVertices();
internal:
OcctShape(NativeOcctShape* nativePtr);
NativeOcctShape* GetNativeInstance();
private:
NativeOcctShape* m_NativeInstance;
};
}
#include "OcctShape.h"
#include "NativeOcctShape.h"
namespace OcctLibrary
{
OcctShape::OcctShape()
{
m_NativeInstance = nullptr;
}
OcctShape::OcctShape(NativeOcctShape* nativePtr)
{
m_NativeInstance = nativePtr;
}
OcctShape::~OcctShape()
{
this->!OcctShape();
}
OcctShape::!OcctShape()
{
if (m_NativeInstance != nullptr)
{
delete m_NativeInstance;
m_NativeInstance = nullptr;
}
}
array<Point3D>^ OcctShape::GetVertices()
{
if (m_NativeInstance == nullptr)
{
return nullptr;
}
std::vector<NativePoint3D> nativeVertices = m_NativeInstance->GetVertices();
array<Point3D>^ result = gcnew array<Point3D>(static_cast<int>(nativeVertices.size()));
for (int i = 0; i < static_cast<int>(nativeVertices.size()); i++)
{
result[i].X = nativeVertices[i].X;
result[i].Y = nativeVertices[i].Y;
result[i].Z = nativeVertices[i].Z;
}
return result;
}
NativeOcctShape* OcctShape::GetNativeInstance()
{
return m_NativeInstance;
}
}
次に STEP ファイルの読込クラスを作成します。
#pragma once
class NativeStepReader;
namespace OcctLibrary
{
ref class OcctShape;
public ref class StepReader
{
public:
StepReader();
~StepReader();
!StepReader();
OcctShape^ Load(System::String^ path);
private:
NativeStepReader* m_nativeReader;
};
}
#include <windows.h>
#include "NativeStepReader.h"
#include "NativeOcctShape.h"
#include "OcctShape.h"
#include "StepReader.h"
#include <msclr/marshal_cppstd.h>
namespace OcctLibrary
{
StepReader::StepReader()
{
m_nativeReader = new NativeStepReader();
}
StepReader::~StepReader()
{
this->!StepReader();
}
StepReader::!StepReader()
{
if (m_nativeReader != nullptr)
{
delete m_nativeReader;
m_nativeReader = nullptr;
}
}
OcctShape^ StepReader::Load(System::String^ path)
{
if (m_nativeReader == nullptr || System::String::IsNullOrEmpty(path))
{
return nullptr;
}
msclr::interop::marshal_context context;
const char* nativePath = context.marshal_as<const char*>(path);
NativeOcctShape* nativeShapePtr = m_nativeReader->Load(nativePath);
if (nativeShapePtr == nullptr)
{
return nullptr;
}
return gcnew OcctShape(nativeShapePtr);
}
}
作成した OcctShape.cpp・StepReader.cpp のプロパティを開き、Debug / Release 構成共に共通言語ランタイムサポートは使用するように設定します。
3. 動作確認
テスト用に Windows Forms で画面を作りました。
インポートボタンで以下の処理が走ります。
private void ImportButton_Click(object sender, EventArgs e)
{
// STEP ファイルを読込する。
using (var stepReader = new StepReader())
using (var shape = stepReader.Load(InputTextBox.Text))
{
if (shape != null)
{
// 形状の頂点座標を取得する。
Point3D[] vertices = shape.GetVertices();
// 重複を除去して、座標を小さい順にソートする。
var sorted = vertices
.GroupBy(v => new { v.X, v.Y, v.Z })
.Select(g => g.First())
.OrderBy(v => v.X)
.ThenBy(v => v.Y)
.ThenBy(v => v.Z)
.ToList();
// ソートされた座標をリストボックスに表示する。
ResultListBox.Items.Clear();
for (var i = 0; i < sorted.Count; i++)
{
ResultListBox.Items.Add($"[{i}]: X={sorted[i].X}, Y={sorted[i].Y}, Z={sorted[i].Z}");
}
}
}
}
とりあえず、座標データが抽出されました。
以前 Occt.NET を検証した時と同じファイルです。ざっと見た感じ、合ってそうでした。
[0]: X=-55, Y=-19.99999999999, Z=-100
[1]: X=-55, Y=-19.99999999999, Z=0
[2]: X=-55, Y=20, Z=-100
[3]: X=-55, Y=20, Z=0
[4]: X=-45, Y=-10, Z=-15
[5]: X=-45, Y=-10, Z=0
[6]: X=-45, Y=10, Z=-15
[7]: X=-45, Y=10, Z=0
[8]: X=-40, Y=-19.99999999999, Z=-10
[9]: X=-40, Y=-19.99999999999, Z=0
[10]: X=-40, Y=-10, Z=-10
[11]: X=-40, Y=-10, Z=0
[12]: X=-24, Y=-19.99999999999, Z=-10
[13]: X=-24, Y=-19.99999999999, Z=0
[14]: X=-24, Y=-10, Z=-10
[15]: X=-24, Y=-10, Z=0
[16]: X=-8, Y=-19.99999999999, Z=-10
[17]: X=-8, Y=-19.99999999999, Z=0
[18]: X=-8, Y=-10, Z=-10
[19]: X=-8, Y=-10, Z=0
[20]: X=8, Y=-19.99999999999, Z=-10
[21]: X=8, Y=-19.99999999999, Z=0
[22]: X=8, Y=-10, Z=-10
[23]: X=8, Y=-10, Z=0
[24]: X=20.358962517357, Y=20, Z=-100
[25]: X=20.358962517357, Y=20, Z=0
[26]: X=24, Y=-19.99999999999, Z=-10
[27]: X=24, Y=-19.99999999999, Z=0
[28]: X=24, Y=-10, Z=-10
[29]: X=24, Y=-10, Z=0
[30]: X=30.505081240903, Y=10, Z=-15
[31]: X=30.505081240903, Y=10, Z=0
[32]: X=40, Y=-19.99999999999, Z=-10
[33]: X=40, Y=-19.99999999999, Z=0
[34]: X=40, Y=-10, Z=-10
[35]: X=40, Y=-10, Z=0
[36]: X=55, Y=-19.99999999999, Z=-100
[37]: X=55, Y=-19.99999999999, Z=0
[38]: X=55, Y=-10, Z=-15
[39]: X=55, Y=-10, Z=0
[40]: X=79.999978668735, Y=15, Z=-50
[41]: X=79.999978668735, Y=15, Z=0
おわりに
残念ながら現状 UTF-8 / Unicode に対応していないため、ファイルパスに日本語があると動きません。適宜ソースを修正してください。