C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)※記載されている内容は個人の意見であり、所属する団体等を代表する意見では御座いません・・・・よ!

六角形の中心を拘束する

最近、イヤ今年は忙しく "あれもやらなきゃ" ”これもやらなきゃ" と思いながら
すごしているのですが、最終的にはどうでも良いことをやってしまうのが人間ですよね?

こちらもそうです。
https://forums.autodesk.com/t5/fusion-api-and-scripts-forum/how-to-constrain-centre-of-a-polygon/m-p/14217580/highlight/true#M22979

スケッチで六角形を描いた中心点が拘束出来ない との事です。
処理的にみると、スケッチの点を先に拘束していてその点を中心とした六角形を描いていました。
個人的にもそれで間違っていないと思います、FusionAPI的に。

最初に試した際には行き詰ったのですが、あちらに記載したようにスケッチの点の数を出力して
気が付きました。増えた最初の点か最後の点が中心だろうと。

あちらのコードは質問者の方のものをそのまま使ったのですが、中心点用のスケッチの点は
作成する必要は無いですね。こんな感じです。(型ヒントは入れるべき)

# Fusion360API Python script

import traceback

import adsk.core as core
import adsk.fusion as fusion


def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface
        des: fusion.Design = app.activeProduct
        root: fusion.Component = des.rootComponent

        des.designType = fusion.DesignTypes.DirectDesignType

        constPoints: fusion.ConstructionPoints = root.constructionPoints
        pointIpt: fusion.ConstructionPointInput = constPoints.createInput()
        pointIpt.setByPoint(core.Point3D.create(1, 2, 3))
        points = [constPoints.add(pointIpt)]

        des.designType = fusion.DesignTypes.ParametricDesignType

        sketchPlane: fusion.ConstructionPlane = root.xYConstructionPlane
        sketches: fusion.Sketches = root.sketches

        rotationAngle: float = 0
        halfHoleSize: float = 1
        holeSize: float = 1.5
        # -----------

        hexSketch: fusion.Sketch = sketches.addWithoutEdges(sketchPlane)

        projectedPoints: fusion.SketchEntityList = hexSketch.project2(points, True)
        constraints: fusion.GeometricConstraints = hexSketch.geometricConstraints

        print(f"Before: {hexSketch.sketchPoints.count}")

        firstPolylines = hexSketch.sketchCurves.sketchLines.addScribedPolygon(
            core.Point3D.create(0, 0, 0), 6, rotationAngle, halfHoleSize, False
        )

        print(f"After: {hexSketch.sketchPoints.count}")

        _: fusion.CoincidentConstraint = constraints.addCoincident(
            hexSketch.sketchPoints[-1], projectedPoints[0]
        )

        firstParraLine: fusion.SketchLine = firstPolylines[0]
        oppLine: fusion.SketchLine = firstPolylines[3]
        hexDim: fusion.SketchOffsetDimension = (
            hexSketch.sketchDimensions.addOffsetDimension(
                firstParraLine, oppLine, firstParraLine.startSketchPoint.worldGeometry
            )
        )
        hexDim.value = holeSize

    except Exception:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

珍しく、AIパワーなしで書きましたよ。

レイヤーの扱いを考える7

多分、10年以上前のこちらの続きです。
レイヤーの扱いを考える6 - C#ATIA

最終的にはgithubのこちらになり落ち着きました。
GitHub - kantoku-code/CATIA_V5-igs2cat_groupbylayer: IgesファイルをD&Dする事でCATPartファイルに変換 · GitHub
が、最近になり新たな知識を身に着け、アクティブなPartの指定した形状セット内の要素を
レイヤー毎に形状セットに分けるものにしました。

以前のロジックでは230MBぐらいのIgesが、2400sぐらいの処理時間でしたが、
今回の物は840sぐらいで処理します。

'/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
'/_/
'/_/ レイヤー分けマクロ
'/_/
'/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
Option Explicit

Private Const ScriptName = "GroupByLayer"


'**********************************************************
'*  形状セット内の要素をレイヤ毎に分割する(本モジュールのエントリ)
'*  ・対象のPartDocumentであることを確認
'*  ・処理対象の形状セットをユーザに選択させる
'*  ・選択後の処理時間を計測し、結果とともに表示する
'**********************************************************
Sub CATMain()
    'PartDocument (未オープン時のエラーも含めてチェック)
    If Not CanExecute("PartDocument") Then Exit Sub

    '対象形状セット選択
    Dim TargetHB As HybridBody
    Set TargetHB = SelectHybridBody()
    If TargetHB Is Nothing Then Exit Sub

    '選択後の処理時間計測スタート
    Dim StartTime As Double
    StartTime = Timer

    'レイヤ毎に分割
    Dim CreateCount As Long
    CreateCount = GroupByLayer(TargetHB)

    Dim ElapsedTime As Double
    ElapsedTime = Timer - StartTime
    Debug.Print ElapsedTime

    If CreateCount < 1 Then
        MsgBox "レイヤが設定された要素がありませんでした" & vbCrLf & _
            "処理時間: " & Format(ElapsedTime, "0.00") & "秒", vbOKOnly, ScriptName
    Else
        MsgBox CStr(CreateCount) + "個の形状セットを作成しました" & vbCrLf & _
            "処理時間: " & Format(ElapsedTime, "0.00") & "秒", vbOKOnly, ScriptName
    End If
End Sub


'--------------------------------------------------------------------
'-  対象の形状セットをユーザに選択させる
'-  return: 選択されたHybridBody(キャンセル時はNothing)
'--------------------------------------------------------------------
Private Function SelectHybridBody() As HybridBody
    Dim Sel As Variant 'Selection
    Set Sel = CATIA.ActiveDocument.Selection

    Call Sel.Clear

    Dim Status As String
    Status = Sel.SelectElement2(Array("HybridBody"), "形状セットを選択して下さい", False)
    If Not Status = "Normal" Then Exit Function

    Set SelectHybridBody = Sel.Item(1).Value
    Call Sel.Clear
End Function


'--------------------------------------------------------------------
'-  形状セット内の要素をレイヤ毎の形状セットへ分割
'-  param: TargetHB-分割対象の形状セット
'-  return: 作成した形状セット数
'-  ・要素ごとにレイヤを取得し、レイヤ番号昇順に新規形状セットを作成
'-  ・レイヤ未設定の要素は元の形状セットに残す
'-  ・要素の移動はコピー&ペーストではなくAppendHybridShapeで行う
'--------------------------------------------------------------------
Private Function GroupByLayer(ByVal TargetHB As HybridBody) As Long
    GroupByLayer = 0

    Dim Doc As PartDocument
    Set Doc = CATIA.ActiveDocument

    Dim Pt As Part
    Set Pt = Doc.Part

    Dim Shapes As HybridShapes
    Set Shapes = TargetHB.HybridShapes

    Dim Count As Long
    Count = Shapes.Count
    If Count < 1 Then Exit Function '要素なし

    Dim Sel As Selection
    Set Sel = Doc.Selection

    Dim VisProperty As VisPropertySet
    Set VisProperty = Sel.VisProperties

    'スナップショット (AppendHybridShape でコレクションが変動するため先に確保)
    Dim ShapeLst() As HybridShape
    ReDim ShapeLst(1 To Count)
    Dim LayerLst() As Long
    ReDim LayerLst(1 To Count)

    Dim LayerDic As Object
    Set LayerDic = CreateObject("Scripting.Dictionary")

    Dim i As Long
    Dim Layertype As Long
    Dim Layer As Long

    CATIA.HSOSynchronized = False
    CATIA.RefreshDisplay = False

    'レイヤ取得
    For i = 1 To Count
        Set ShapeLst(i) = Shapes.Item(i)
        With Sel
            Call .Clear
            Call .Add(ShapeLst(i))
        End With
        Call VisProperty.GetLayer(Layertype, Layer)

        If Layertype = 0 Then
            LayerLst(i) = Layer
            If Not LayerDic.Exists(Layer) Then
                Call LayerDic.Add(Layer, Empty)
            End If
        Else
            LayerLst(i) = -1 'レイヤ未設定 元の形状セットに残す
        End If
    Next
    Call Sel.Clear

    'レイヤ番号昇順に形状セット作成
    Dim Layers As Variant
    Layers = SortedKeys(LayerDic)

    Dim HBDic As Object
    Set HBDic = CreateObject("Scripting.Dictionary")

    Dim HB As HybridBody
    For i = 0 To UBound(Layers)
        Set HB = Pt.HybridBodies.Add()
        HB.Name = "Layer" & CStr(Layers(i))
        Call HBDic.Add(Layers(i), HB)
    Next

    '要素の移動 (コピペ/削除なし)
    For i = 1 To Count
        If LayerLst(i) >= 0 Then
            Set HB = HBDic.Item(LayerLst(i))
            Call HB.AppendHybridShape(ShapeLst(i))
        End If
    Next

    Call Pt.Update

    CATIA.RefreshDisplay = True
    CATIA.HSOSynchronized = True

    GroupByLayer = HBDic.Count
End Function


'--------------------------------------------------------------------
'-  Dictionaryのキー(数値)を昇順ソートして返す
'-  param: Dic-対象Dictionary(キーが数値であること)
'-  return: 昇順ソートしたキーのVariant配列
'-  ・挿入ソートで実装
'--------------------------------------------------------------------
Private Function SortedKeys(ByVal Dic As Object) As Variant
    Dim Keys As Variant
    Keys = Dic.Keys

    Dim i As Long
    Dim j As Long
    Dim Temp As Long
    For i = 1 To UBound(Keys)
        Temp = Keys(i)
        j = i - 1
        Do While j >= 0
            If Keys(j) <= Temp Then Exit Do
            Keys(j + 1) = Keys(j)
            j = j - 1
        Loop
        Keys(j + 1) = Temp
    Next

    SortedKeys = Keys
End Function


'--------------------------------------------------------------------
'-  マクロスタートチェック
'-  主にアクティブドキュメントの型チェック
'-  param: arrDocTypes-許可するアクティブなドキュメントの型
'-      単体文字列OK, カンマ区切りの文字列OK, 文字の配列OK
'-  return: OKならTrue
'-  ・ウィンドウが1つも開かれていない場合もここでチェックする
'--------------------------------------------------------------------
Private Function CanExecute( _
        ByVal arrDocTypes As Variant) As Boolean

    CanExecute = False

    'MAP
    Dim oDocTypeMap As Object
    Set oDocTypeMap = CreateObject("Scripting.Dictionary")
    With oDocTypeMap
        Call .Add("PartDocument", "CATPart")
        Call .Add("ProductDocument", "CATProduct")
        Call .Add("DrawingDocument", "CATDrawing")
        Call .Add("AnalysisDocument", "CATAnalysis")
        Call .Add("ProcessDocument", "CATProcess")
    End With

    'ウィンドウ数チェック
    If CATIA.Windows.Count < 1 Then
        MsgBox "ファイルが開かれていません"
        Exit Function
    End If

    'カンマ区切りの文字列
    If VarType(arrDocTypes) = vbString Then
        arrDocTypes = Split(arrDocTypes, ",")
    End If

    'エラー時のメッセージ
    Dim arrFileExts() As Variant
    ReDim arrFileExts(UBound(arrDocTypes))

    Dim i As Long
    For i = 0 To UBound(arrDocTypes)
        arrFileExts(i) = oDocTypeMap(arrDocTypes(i))
    Next

    Dim ErrMsg As String
    ErrMsg = "ファイルのタイプが異なります。" + vbCrLf + "(" + Join(arrFileExts, ",") + " のみです)"

    'ドキュメントかチェック
    Dim ActDoc As Document
    On Error Resume Next
        Set ActDoc = CATIA.ActiveDocument
    On Error GoTo 0
    If ActDoc Is Nothing Then
        MsgBox ErrMsg, vbExclamation + vbOKOnly
        Exit Function
    End If

    If UBound(Filter(arrDocTypes, TypeName(ActDoc))) < 0 Then
        MsgBox ErrMsg, vbExclamation + vbOKOnly
        Exit Function
    End If

    CanExecute = True

End Function

以前は一番使っていたマクロだったのですが、今はすっかり使わない・・・

AutoRemesher

一時期は頻繁に模索していたのですが、結局Fusionの人力による方法でした。

ほぼプルリバースエンジニアリング の検索結果 - C#ATIA

その際に見つけた自動的?半自動的?に変換する方法の一つはInstant Meshesでした。
この時にAutoRemesherも見つけていたのですが、ブログのネタにはしていませんでしたね・・・。

そのAutoRemesherがver1.0.0となったようです。
GitHub - huxingyi/autoremesher: Automatic quad remeshing tool · GitHub

Releasesからダウンロードして展開で終了。インストーラー無しなのが手軽すぎです。
さっそく試してみました。

まず、どのご家庭にもある3Dスキャンデータを用意します。もしない場合は
Web上にあるSTLでも探してください。
AutoRemesherはOBJフォーマットしか対応していない為、FusionでOBJでエクスポートです。

続いてAutoRemesherを起動し、Openから先程のOBJファイルを開きます。
時は金なり。読み直後から変換開始します。(心の準備が間に合わん)

トイレに行く暇も与えないぐらいの時間で、クワッドメッシュ(四角形メッシュ)化
が終了しています。

・・・パラメータを調整出来るのですが、意味が良く分からないので割愛。
(どれぐらい荒くする、細かくするとかだと思います)
変換後はクワッドメッシュをOBJとして保存。

再度FusionでOBJをインポート。

トライアングルメッシュ(三角形のメッシュ)としてインポートされる場合は
基本設定のチェックを外しておいてね。(先に書くべき)

フォームワークスペースに入り変換コマンド

"クワッドメッシュからTスプライン"に切り替えて、メッシュボディ(クワッドメッシュ)を選択

Tスプライン化に成功です。アプリによってはトライアングルメッシュが混在しているデータを
作るものもあるようですが、AutoRemesherは混在しないように変換するようです。えらい!

後はフォームワークスペースから抜け出せば終了です。

素晴らしいです。

GPT-5.6を試してみた

Codexで試してみました・・・5.5との違いは分からん。

都道府県等の情報は伏せておきます。
念のため伝えておきますが、サイトを見ると電子申請Okなんですけどね。
・・・って隣の席の斎藤さんが言ってました。
道路使用許可に関する申請手続 - 埼玉県警察

カスタムテーブルにアクセスする

7月のアップデートで、もう一つに気になるものがありました。長年期待されていたんですが、
なかなか実装が進まない図面のAPIです。

細かくは見ていないんですが、それほど多くの機能は実装されていないです。
で、アクセスできそうだなと気がついたのは、こちらのカスタムテーブルです。
Help

とりあえず簡単な図面を作成し、テーブルを幾つか配置しました。

上からメニューの順にテーブルを作成。シートメタル部品では無いので曲げテーブルはありません。

こちらも試行錯誤して作ったスクリプトがこちらです。

# Fusion360API Python script

import traceback

import adsk.core as core
import adsk.drawing as drawing


def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        drawDoc: drawing.DrawingDocument = drawing.DrawingDocument.cast(
            app.activeDocument
        )
        if not drawDoc:
            return

        draw: drawing.Drawing = drawDoc.drawing
        sheet: drawing.Sheet = draw.activeSheet
        ui.messageBox(f"カスタムテーブル数: {len(sheet.customTables)}", "失敗です")

    except Exception:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

お気づきかもしれませんが、実際に実行させると・・・

カスタムテーブルも配置しているのですが、何故か数が0なんです。ん~
間違っているとは思えないので、まだまだですかね。
DrawingこそAPIが必要なんですけどね。まぁ今後機能が充実するでしょう。


話は違うんですが、図面ついでに。先日、ちょっとわけがあって、JWCADを
インストールして使ってみたのですが、まあ、使いづらい。 もちろん慣れの問題もあるとはいえ、
見た目も、まあ古いです。
半べそかきながら作業したけど、自分は二度と使いたくないですね。
使える人天才です。

Expert Elite ミートアップ

毎年お声がけ頂いているのですが、昨年は仕事の都合が付かず断念。
今年は無事参加できました。

直接エキスパートエリートの方々とお会いできる機会がこの時しかない上、
異業種の方々とお話しする機会がこの時しかないため貴重な機会だと思っています。
有難い。

で、戦利品がこれです。他にも頂きましたが、迷わず選ばせて頂きました。

メッシュ比較

7月のアップデートで気になった機能がメッシュ比較です。
Help

これ絶対便利ですよ。間違いなく。なので試します。

少しだけ違うメッシュボディを2個重なるように作成しました。

試行錯誤してこの様なサンプルを作成しました。

# Fusion360API Python script

import traceback

import adsk.core as core
import adsk.fusion as fusion


def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface
        des: fusion.Design = app.activeProduct
        root: fusion.Component = des.rootComponent

        mesh1: fusion.PolygonMesh = root.meshBodies[0].mesh
        mesh2: fusion.PolygonMesh = root.meshBodies[1].mesh

        res = mesh1.compareWith(mesh2)

        print("--mesh1.compareWith(mesh2)--")
        for idx, dist in enumerate(res):
            node: core.Point3D = mesh1.nodeCoordinates[idx]
            print(f"{idx}: {dist} - {node.asArray()}")

        res = mesh2.compareWith(mesh1)

        print("--mesh2.compareWith(mesh1)--")
        for idx, dist in enumerate(res):
            node: core.Point3D = mesh2.nodeCoordinates[idx]
            print(f"{idx}: {dist} - {node.asArray()}")

    except Exception:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

mesh1.compareWith(mesh2) と mesh2.compareWith(mesh1)を行っています。
これが気が付かなかったし、何故か分からないんです。

実際の結果はこちら。

--mesh1.compareWith(mesh2)--
0: 0.0 - (2.5, 2.5, 0.0)
1: 0.0 - (0.0, 2.5, 0.0)
2: 0.0 - (0.0, 2.5, 1.5)
3: 0.0 - (2.5, 2.5, 1.5)
4: 0.0 - (2.5, 0.0, 0.0)
5: 0.0 - (2.5, 0.0, 1.5)
6: 0.0 - (0.0, 0.0, 0.0)
7: 0.0 - (0.0, 0.0, 1.5)
--mesh2.compareWith(mesh1)--
0: 0.0 - (2.5, 2.5, 0.0)
1: 0.0 - (0.0, 2.5, 0.0)
2: 0.21872162818908691 - (0.0, 2.5, 1.718721628189087)
3: 0.0 - (2.5, 2.5, 1.5)
4: 0.0 - (2.5, 0.0, 0.0)
5: 0.0 - (2.5, 0.0, 1.5)
6: 0.0 - (0.0, 0.0, 0.0)
7: 0.21872162818908691 - (0.0, 0.0, 1.718721628189087)

mesh1.compareWith(mesh2)を行うと全ての頂点が一致している(距離が0)を返しています。
イヤイヤそんなことないでしょう。わざわざちょっとだけ変えたモデルで比較してんだから。

mesh2.compareWith(mesh1)に関しては欲しかった結果が得られます。
mesh2.compareWith(mesh1)は常に正しい結果を返してくるか?と言われるとそうだとも
限らないです。形状の影響を受けているのだとは感じていますが、上手く表現できないです。