Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

C# から Open CASCAD Technology (C++) を参照する【座標抽出編】

0
Posted at

はじめに

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++ クラスを作ります(厳密には異なるようです)。

NativeOcctShape.h
#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;
};
NativeOcctShape.cpp
#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 ファイルの読込クラスを作成します。

NativeStepReader.h
#pragma once

#include "NativeOcctShape.h"

class NativeStepReader
{
public:
    static NativeOcctShape* Load(const char* path);
};
NativeStepReader.cpp
#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.cppNativeStepReader.cpp のプロパティを開き、Debug / Release 構成共に共通言語ランタイムサポートは使用しないように設定します。

2-2. C++/CLI プログラムの作成

C++/CLI 側を作っていきます。トポロジー形状データを保持するクラスを作ります。

OcctShape.h
#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;
    };
}
OcctShape.cpp
#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 ファイルの読込クラスを作成します。

StepReader.h
#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;
    };
}
StepReader.cpp
#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.cppStepReader.cpp のプロパティを開き、Debug / Release 構成共に共通言語ランタイムサポートは使用するように設定します。

3. 動作確認

テスト用に Windows Forms で画面を作りました。

インポートボタンで以下の処理が走ります。

MainForm.cs
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 を検証した時と同じファイルです。ざっと見た感じ、合ってそうでした。

Result.log
[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 に対応していないため、ファイルパスに日本語があると動きません。適宜ソースを修正してください。

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?