歯車の形に興味のある人に

FUSION:スプラインへの接線

FUSIONの英語フォーラムを見ていたところ、「(和訳)スプラインに接する線を作る方法」という投稿を見かけました。スケッチの「接線」拘束がスプラインには適用できないことに起因しています。

初回の投稿は2018年3月で、なぜ他のCADでできることがFUSIONではできないのか、早急に対応してほしいという声が上がっています。それに対し、AUTODESKエンジニアは「優先度が低い」という回答をしています。その後も「まだ対応されていないのか」という投稿が続き、関心が高いことがうかがえます。今年に入ってからも「7年経過したがまだ修正されていない」という投稿があり、それに複数の反応が寄せられています。

この投稿に注目した理由は、このブログでも同様に歯車の危険断面位置を探す際に、歯元隅肉曲線への30°接線が必要だったからです。もちろんAPIを使えば可能ですが、スケッチ作図だけで実現したいのです。
その経緯を以下の記事に投稿しています。
involutegearsoft.hatenablog.com


それで本題ですが、先の一連の投稿の中に、これなら使えるかも、という案があったので試してみました(メッセージ49)。
要は、スケッチの「接線拘束」の代わりに、コンポーネントのアセンブリの「接線関係」を使うというものです。
手順としては、スケッチからサーフェスボディのコンポーネント(スプラインと接線になる直線)を作成し、「接線関係」で接触させ、投影でスケッチに戻します。

図.歯形モデルと隅肉面オフセット
図.30°線とサーフェスボディ作成
図.アセンブリの接線関係
図.スケッチ投影して、交点表示

投稿では「2 本の線の間に一致拘束のある点を追加して交点を見つける」と書いてあるのですが、そもそも片方がスプラインでは一致拘束がかけられません。
代わりに、スケッチのリンクを外して破断コマンドで接触点(交点)が見つけられます。トリムコマンドでもいいですが。




スケッチ-アセンブリ-スケッチという工程を重ねるので面倒なのですが、近似値ではなく真値が得られるのは嬉しいです。

アップデート版「回転する開店祝い」

以前、以下の記事でFusion+igearsで作成された「回転する開店祝い」を紹介しました。
involutegearsoft.hatenablog.com


そのアップデート版が発表されたようです。制作記録が「note」にあります。
note.com


x見てたら流れてきました。御本人のポストよりも、次のポストのほうが18万再生されている。
[https://x.com/hfujikawa77/status/1974359475575476556:embed]

AUTODESK社ページで「インボリュート歯車」ブログ(1)~(4)公開

AUTODESK社コミュニティブログで、2025年1月に「インボリュート歯車の理解と正確なモデルの作成方法」シリーズの第1回を公開し、3月に第2回、7月に第3回を公開いたしました。
このたび第4回が公開されましたので、ぜひご覧ください。

このシリーズでは、Fusion上で歯車の製作工程を再現し、インボリュート歯車の創成を体験していただくことを目的としています。ブラックボックスになりがちな諸元計算の背景を理解していただき、「歯車は難しい」というネガティブなイメージを「面白い」「そういうことだったのか」と感じていただけると嬉しいです。


以下にリンクと要約を記載します。

Autodesk Fusion:インボリュート歯車の理解と正確なモデルの作成方法(第1回目)
Fusionの標準機能だけでJIS規格準拠の高精度インボリュート歯車をモデリングする方法を紹介。 実際の創成加工を3Dブーリアン演算で再現し、μm単位の誤差まで検証。


Autodesk Fusion:インボリュート歯車の理解と正確なモデルの作成方法(第2回目)
Fusionで創成図を用いたインボリュート歯車のモデリング手法を紹介。 加工工具の移動と回転を組み合わせて、歯形の軌跡を再現し、正確な歯形を抽出。


Autodesk Fusion:インボリュート歯車の理解と正確なモデルの作成方法(第3回目)
Fusionを使って、多角形誤差のない滑らかなインボリュート歯形を作図する方法を解説。 歯元隅肉曲線(トロコイド曲線)をCADのオフセット機能で正確に再現。 スプライン接続により、歯形全体が自然につながる高精度なモデルが完成。


Autodesk Fusion:インボリュート歯車の理解と正確なモデルの作成方法(第4回目)
Fusionでインボリュート曲線の数式を用いて内歯車の正確な歯形を作成する方法を解説。 Excelで座標を算出し、CSVデータをスプラインに変換して歯形プロファイルを構築。 外歯車とのかみ合いも検証し、クリアランスや干渉の確認まで丁寧に行っている。インボリュート曲線計算シート添付。

FreeCAD用遊星歯車作成&アニメーションスクリプト

2024年5月の記事「FreeCAD用遊星歯車作成スクリプト」のバージョンアップです。ダウンロード先は、末尾に記載しています。

以前の記事では、スクリプト内に歯数やモジュールなどの諸元を直接記載していたため、その都度エディターで修正する必要がありました。単純にGUIの作り方を知らなかったためです。

結局、FreeCADのGUIはFusionのように専用のAPIで描画するのとは異なり、Python標準のtkinterを使えば良いことが分かり、cursor+claude4 sonnetを利用してGUIを作成しました。さらに、アニメーションも実行できるようにし、キャリア固定、サンギヤ固定、リングギヤ固定から選択可能にしました。また、回転方向や回転速度も可変にしています。

図.遊星歯車設計例
図.諸元設定&アニメーション実行画面

以下にreadme.mdの内容を記載します。

FreeCAD 遊星歯車マクロ

FreeCAD用の遊星歯車自動生成マクロです。直感的なGUI操作でパラメータを設定し、遊星歯車を簡単に作成・アニメーションできます。

特徴

基本機能
  • 🖥️ GUI インターフェース - tkinterベースの使いやすいGUI
  • ⚙️ パラメータ化設計 - モジュール、歯数、角度など全てのパラメータを調整可能
  • 📋 複数設定管理 - 異なる設定を複数追加して一度に複数の歯車セットを作成
  • 🎨 プリセット機能 - 設定例をワンクリックで読み込み
  • 🛡️ 入力値検証 - 無効な値や不適切な歯車関係の自動チェック
  • 📐 遊星歯車理論準拠 - 正しい遊星歯車の歯数関係式 `zr - zs = 2 * zp` をチェック
アニメーション機能 (安定版)
  • 🎬 3つの固定モード - キャリア固定/リング固定/サン固定を動的切り替え
  • 🔄 逆回転対応 - -3.0倍~+3.0倍の速度範囲で正逆回転
  • 軽量アニメーション - QTimerベースの高速で安定した動作
  • 🎯 位相連続性 - 角度ジャンプなしの滑らかな動作
  • 📊 詳細デバッグ - 歯数比と動作モードの詳細表示
  • 🔧 正確なかみ合い - 遊星歯車4個以上でも正しくかみ合う

動作環境

  • FreeCAD 0.18 以上
  • Python 3.x (FreeCADに内蔵)
  • freecad.gears アドオン
  • tkinter (Pythonに標準付属)

インストール

1. FreeCADのGear Workbenchアドオンをインストール:

  • FreeCAD → Tools → Addon Manager
  • "Gear" を検索してインストール

2. マクロファイルをFreeCADのマクロディレクトリに配置:

  • planetaryGearAnimationStable.FCMACRO

3. マクロディレクトリの場所:

  • Windows: `%APPDATA%\FreeCAD\Macro\`
  • Linux: `~/.FreeCAD/Macro/`
  • macOS: `~/Library/Preferences/FreeCAD/Macro/`

使用方法

基本的な使い方

1. FreeCADを起動
2. Macro → Macros... → `planetaryGearAnimationStable.FCMACRO`を選択
3. Executeをクリック
4. GUIが起動します

GUI操作手順

1. 基本パラメータの設定
- モジュール: 歯車のサイズを決定(デフォルト: 2)
- ねじれ角: ヘリカル角度(デフォルト: 20度)
- 歯幅: 歯車の厚み(デフォルト: 30)
- バックラッシ: 歯間の遊び(デフォルト: 0.0)

2. 歯車設定
- リング歯車歯数: 外輪の歯数(デフォルト: 48)
- 太陽歯車歯数: 中心軸の歯数(デフォルト: 18)
- 遊星歯車歯数: 遊星歯車の歯数(デフォルト: 15)
- 遊星歯車の数: 遊星歯車の個数(デフォルト: 3)

3. 固定モード選択
- キャリア固定: 遊星歯車の位置固定、基本的な歯数比確認
- リング固定: 実用的な減速機モード、遊星歯車が公転
- サン固定: 差動歯車モード、逆転動作の観察

4. アニメーション制御
- 速度スライダー: -3.0x~+3.0x(逆回転対応)
- 方向表示: `← 逆回転 | 0 | 順回転 →`
- ▶ アニメーション開始: 遊星歯車の回転アニメーション開始
- ⏹ アニメーション停止: アニメーション停止

5. 設定管理
- 「設定を追加」: 現在の設定をリストに追加
- 「設定をクリア」: 全ての設定をクリア

6. 歯車作成
- 「遊星歯車を作成」: FreeCAD内に歯車を生成(常に現在のGUI設定を使用)

7. プリセット
- **軽量設定1**: R54-S18-P18-N4
- **軽量設定2**: R69-S39-P15-N9

プリセット設定

プリセット名 リング歯車 太陽歯車 遊星歯車 遊星数 用途
軽量設定1 54 18 18 4 標準的な遊星歯車
軽量設定2 69 39 15 9 高減速比
よくある問題
Q: 歯車がかみ合わない**

A: 歯数関係式 `zr - zs = 2 * zp` を確認してください

Q: アニメーションが重い**

A: 速度を下げるか、歯数を減らしてください

ダウンロード先

Fusionでつくる:CVT(無段変速機)モデル第2弾

今回も、CVTモデルです。前回作ったのは、ちょっと特殊な構造のCVTだったのですが、それを説明するためにコンベンショナルな構造のCVTを作成しました。

図.コンベンショナルCVT


内部が見えるように半透明化したのはこちら。青く塗った遊星歯車が見えますか。
(デフ差動歯車は省略)

図.一部を半透明化


側面図と軸断面図は以下のようになっています。このレイアウトは、もっともポピュラーな4軸構成で、前後進切替機構がベルトの前に配置されます。

図.軸断面図と側面図

自動車用の変速機は、上図左のように各軸中心を結ぶ線に沿った断面図が公開される場合が多いです。構成部品はほとんどが回転体なので、断面から形状をトレースして回転させ、そこに歯車のような非回転体フィーチャを追加していけば大方完成します。難しいのは、部品相互の接続がどうなっているかを読み取るところだと思います。とくに遊星歯車周りは、なかなか理解が追い付かない。

図.断面図から回転体を作成

動力の流れ

  1. エンジンからトルクコンバータを経た動力が、入力軸に入ります(エンジンから見て右回転)
  2. 前進時:遊星歯車が一体回転するので、プーリは右回転です。
  3. 後退時:遊星歯車のキャリア入力、リング固定、サンギヤ出力とすることで、回転方向を反転します。プーリは左回転です。
  4. 第3軸、第4軸で歯車によりそれぞれ回転が反転します。

結果、出力軸(タイヤ軸)は前進時右回転、後退時左回転となります。

Fusionでつくる:CVT(無段変速機)モデル

ネットで某社の自動車用CVT(無段変速機)の断面図と側面図を見つけたので、Fusionを使ってモデルを作成してみました。

図. CVT Assy

細かい寸法や歯車の諸元は適当です。

図の黄色い部品が、CVTの変速を行う「金属ベルト」です。ベルトというと、スクーターのゴム(樹脂)ベルトを思い浮かべるかもしれませんが、これは全く異なり、金属製で「プッシュベルト」と呼ばれます。一方、ゴム(樹脂)ベルトは「プルベルト」です。プルベルトは、機構学の教科書に載っている「摩擦伝動ベルト」で、「引張」で動力を伝えます。

自動車用CVTに使われる金属ベルトは、主に「圧縮」で動力を伝えるため「プッシュベルト」と呼ばれます。ただし、圧縮だけではベルトは成立しません。ベルトは円周上にあるため、押すとプーリーから浮き上がります。これを抑えるためには「張力」が必要です。

金属ベルトは、工具鋼製の約400個の「エレメント」と、マルエージング鋼製の厚さ0.2mmの「リング」6~12枚×2セットで構成されています。工具鋼はベアリングなどに使われる硬く摩耗しにくい材料です。マルエージング鋼は「超強力鋼」と呼ばれ、コバルトなどの希少成分を含むため非常に高価です。しかし、この「張力」は、薄くて高強度(高靭性)のマルエージング鋼製リングによって支えられています。

図.金属ベルト

エレメントとリングという2つの構成要素が、それぞれ異なるピッチ半径を持つため、動力伝達の力学は複雑です。プッシュとプルが作用し合い、エンジンのパワーを伝えています。

「金属ベルト」と言えば、一般的にはこのプッシュ方式を指しますが、「金属チェーンCVT」というものも存在します。これは主にスバルで採用され、自転車のチェーンのようなコマが数百個、縦横に連結されています。この方式は「プルベルト」です。

これから、おいおいCVTの話題についても触れていく予定です。

Fusionのオフセットスプラインは「品質向上」したのか(3)

混沌としているのですが、このブログの結論です。
オフセットスプラインは品質改善していない

そう考えるに至った経緯をデータとともに順に説明します。

元データの準備
図1.楕円とオフセット
5月度更新で「品質改善」された曲率コーム

曲率連続な形状になっていて、改善されたように見えます。

図2.オフセットの曲率コーム
疑いはここから始まる

オフセット拘束を外すと、5月更新以前の不連続な曲率コーム状態に戻ります。念のため、APIで曲線のCurvetureを計算して表示すると、同じ不連続な形状です。

図3.オフセット拘束を外すと
Bodyではどうなる

スケッチオフセットの挙動が不審なので、最終形状に直結するサーフェスとソリッドの形状で確認します。左手がサーフェス、右手側がソリッドです。

図4.サーフェス化、ソリッド化
投影

まず、構築平面に対して、輪郭を投影してみます。不連続です。

図5.投影の曲率コーム
交差

つぎに構築平面に対して、交差を取ります。連続です。

図6.交差の曲率コーム
検査「アイソ解析」

さらに、検査メニューの「アイソ解析」で表面の検査を行います。不連続です。

図7.アイソ解析
検査「ゼブラ解析」

検査メニューの「ゼブラ解析」で、実際の表面が連続か不連続かを確認します。不連続の曲率コームが、段付きになる位置で、ゼブラの疎密も変化しているので、実際の表面性状は曲率不連続と判断します。

図8.ゼブラ解析
2つの形状の差

投影と交差で断面形状が異なります。

図9.投影形状と交差形状

重ねて描きました。最大9μmの誤差があります。構成するセグメント数も違うし、制御点の配置も違います。

図10.投影形状と交差形状の差
理論形状との差

Fusionのオフセットは信用できないので、自前でオフセット計算します。オフセットを、オフセット距離に相当する直径の円を無数に並べたときの包絡線として表した場合と、法線方向にオフセット相当の距離離れた平行線として表す場合があり、どちらも実際に書いて求めたのが下図です。

図11.正確なオフセット形状

連続なほうを、理論オフセットと重ねて描くと、両者は一致しています。ここだけ見ると確かに品質改善されています。しかし、ボディ形状には反映されていないのです。

図12.「曲率連続」の形状は、「正確なオフセット」形状に一致
STEP形式出力と再読み込み

ボディに反映されていない時点で、分かっていることですが、念のためSTEP出力してみます。するとやはり、不連続な曲率です。ということは誤差が9μmあります。これによって、結論を確定しました。
( CAMで加工するとどうなるんでしょうね。不連続と予測しますが)

図13.STEP出力すると品質改善していない

以下推定

なんとなく感じていること
・幾何形状情報は2種類持っている。今回でいえば曲率コームの連続なものと不連続なもの。
・5月度更新で幾何情報2つを同時に修正すべきところを、片方しか修正しなかったのではなないか。
・そのため、状況により、どちらかの幾何情報が出現すると推定
・幾何情報の差は、どこについて回るか。たとえばSketchCurves、BrepbodyのEdge,Face,Bodyで違うかも、と思ったが断定できず。
・設計者から見ると一見改善されたようだが、アウトプットに反映されていなければ、製作側との齟齬が拡大するのでは。
 また、書いた通りの形状になっていないというのは、CADの信頼性を損ねる。

以上です。本件は日本のAutoDeskサポートに連絡済みです。

Fusionのオフセットスプラインは「品質向上」したのか(2)

Fusionのオフセットスプラインは「品質向上」したのか」の続きです。別な手段で検証していきます。

確認した事実1

今のところ「曲率コーム」は不可解な現象があり、意図的な細工が可能なようなので、これに頼らない方法を取ります。それには、APIで曲線の「curverture」を取得するアドインを使いました。ソースコードは末尾に添付します。

結果

下図に結果を示します。Fusionの曲率コーム輪郭(赤線)が滑らかなのに、実測ではガタガタです。しかもガタガタの形は、前報で示した操作後の曲率コームと一致するのです。

図.自作の曲率コームと、Fusionの曲率コーム

次回に続きます。

ソースコード

import adsk.core, adsk.fusion, adsk.cam, traceback

# グローバル変数としてイベントハンドラを保持するリスト
handlers = []

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

        # コマンドIDを定義
        cmd_def_id = 'CurvatureComb_Command'

        # 既存のコマンド定義を確認して削除
        existing_cmd_def = ui.commandDefinitions.itemById(cmd_def_id)
        if existing_cmd_def:
            existing_cmd_def.deleteMe()

        # コマンドの定義
        cmd_def = ui.commandDefinitions.addButtonDefinition(
            cmd_def_id,
            '曲率半径の線分を作成',
            '選択した曲線をn分割し、各分割点で曲率半径の線分を描画します。'
        )

        # コマンド作成イベントハンドラを追加
        on_command_created = CommandCreatedEventHandler()
        cmd_def.commandCreated.add(on_command_created)
        handlers.append(on_command_created)

        # アドインのパネルにコマンドを追加
        workspace = ui.workspaces.itemById('FusionSolidEnvironment')
        if not workspace:
            ui.messageBox('FusionSolidEnvironment workspace not found.')
            return

        toolbar_panel = workspace.toolbarPanels.itemById('SolidScriptsAddinsPanel')
        if not toolbar_panel:
            ui.messageBox('SolidScriptsAddinsPanel toolbar panel not found.')
            return

        # 既存のコマンドコントロールを削除
        existing_control = toolbar_panel.controls.itemById(cmd_def_id)
        if existing_control:
            existing_control.deleteMe()

        toolbar_panel.controls.addCommand(cmd_def)

    except:
        if ui:
            ui.messageBox('実行中にエラーが発生しました:\n{}'.format(traceback.format_exc()))

def stop(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface

        # コマンドIDを定義
        cmd_def_id = 'CurvatureComb_Command'

        # コマンド定義を取得
        cmd_def = ui.commandDefinitions.itemById(cmd_def_id)
        if cmd_def:
            cmd_def.deleteMe()

        # グローバルハンドラリストをクリア
        global handlers
        handlers.clear()

    except:
        if ui:
            ui.messageBox('停止中にエラーが発生しました:\n{}'.format(traceback.format_exc()))

class CommandCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        try:
            cmd = args.command
            inputs = cmd.commandInputs

            # 曲線を選択する入力
            selection_input = inputs.addSelectionInput('curve', '曲線を選択', '曲線を選択してください')
            selection_input.addSelectionFilter('SketchCurves')
            selection_input.setSelectionLimits(1,1)

            # 分割数を指定する入力
            n_input = inputs.addIntegerSpinnerCommandInput('n_divisions', '分割数 n', 1, 1000, 1, 10)

            # コマンド実行時のイベントハンドラを追加
            on_execute = CommandExecuteHandler()
            cmd.execute.add(on_execute)
            handlers.append(on_execute)

        except:
            app = adsk.core.Application.get()
            ui  = app.userInterface
            ui.messageBox('コマンドの作成中にエラーが発生しました:\n{}'.format(traceback.format_exc()))

class CommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        try:
            event_args = adsk.core.CommandEventArgs.cast(args)
            inputs = event_args.command.commandInputs

            # 入力から選択した曲線と分割数を取得
            curve_input = inputs.itemById('curve')
            n_input = inputs.itemById('n_divisions')

            selected_curve = curve_input.selection(0).entity
            n = n_input.value

            # 曲線のEvaluatorを取得
            curve_geometry = selected_curve.geometry
            evaluator = curve_geometry.evaluator

            # パラメータ範囲を取得
            success, start_param, end_param = evaluator.getParameterExtents()
            if not success:
                ui = adsk.core.Application.get().userInterface
                ui.messageBox('パラメータ範囲の取得に失敗しました')
                return

            # パラメータ範囲をn分割
            parameters = [start_param + i * (end_param - start_param) / n for i in range(n + 1)]

            # getCurvaturesを使用して一括取得
            success, directions, curvatures = evaluator.getCurvatures(parameters)
            if not success:
                ui = adsk.core.Application.get().userInterface
                ui.messageBox('曲率の取得に失敗しました')
                return

            # スケッチを作成
            app = adsk.core.Application.get()
            design = adsk.fusion.Design.cast(app.activeProduct)
            root_comp = design.rootComponent
            sketches = root_comp.sketches

            # 曲線が存在する平面にスケッチを作成する
            if hasattr(selected_curve, 'parentSketch') and selected_curve.parentSketch:
                curve_sketch = selected_curve.parentSketch
                curve_plane = curve_sketch.referencePlane
            else:
                # 3D曲線の場合はXY平面を使用
                curve_plane = root_comp.xYConstructionPlane

            # 新しいスケッチを作成
            sketch = sketches.add(curve_plane)
            sketch.name = 'CurvatureRadiusLines_Sketch'
            lines = sketch.sketchCurves.sketchLines

            # 各パラメータ値で曲率半径の線分を描く
            for i in range(len(parameters)):
                param = parameters[i]
                curvature = curvatures[i]
                direction = directions[i]

                if curvature == 0:
                    continue  # 曲率がゼロの場合はスキップ

                # 曲率半径を計算
                radius_of_curvature = -curvature / 10
                # radius_of_curvature = curvature

                # 法線ベクトルを取得(方向ベクトルを正規化)
                normal = direction.copy()
                normal.normalize()

                # 曲線上の点を取得
                success, point_on_curve = evaluator.getPointAtParameter(param)
                if not success:
                    continue  # 次の分割点へ

                # スケーリングされた法線ベクトルを作成
                scaled_normal = normal.copy()
                scaled_normal.scaleBy(radius_of_curvature)

                # 終点を計算
                end_point = adsk.core.Point3D.create(
                    point_on_curve.x + scaled_normal.x,
                    point_on_curve.y + scaled_normal.y,
                    point_on_curve.z + scaled_normal.z
                )

                # 線分を作成
                lines.addByTwoPoints(point_on_curve, end_point)

        except:
            app = adsk.core.Application.get()
            ui  = app.userInterface
            ui.messageBox('コマンドの実行中にエラーが発生しました:\n{}'.format(traceback.format_exc()))

Fusionのオフセットスプラインは「品質向上」したのか

Fusionの「What’s New 2025」を見ていたら、5月度更新のところに次の記述がありました。

よりスムーズな曲線のための改善


スプライン/曲線の滑らかさを改善するために、基礎となる曲線の使用方法を改善しました。オフセットおよび投影から生成されるスプラインの品質向上、オフセット曲線の結果の信頼性向上など、いくつかの利点があります。これにより、曲線の再モデリングの必要がなくなります。これらのスプライン/曲線の滑らかさは、曲率コームで検証できます。


これが気になったのは訳があります。以前の記事「CADのスケッチオフセットの精度と歯形曲線」で書いたように、Fusionのオフセットは精度が良くないのです。それは曲率コームを見ても、スプラインを構成するセグメント間の曲率が不連続である、つまり曲率コームの外観がガタガタであることからもうかがい知れます。
下図が、その時の資料です。左側の楕円のオフセットが曲率不連続です。

図.オフセット曲線の曲率コーム

確認結果

で、曲率コームが滑らかになったということは、精度改善も期待できるのでは、ということで見てみました。すると、下図のように滑らかになっているので、「ああ、直ったんだ」と思ったのですが、、、

図.滑らかになっている


ところが、なにか操作を加えると、曲率コームがガタガタになるのです。「操作」というのはたとえば

  • スケッチをコピーして貼り付ける
  • オフセット拘束を削除して、図形を動かす
  • 別コンポーネントのスケッチに投影する
  • DXFやf3dに書きだす

ということです。

下図の左がオフセット直後ですが、コピーして動かしたのが中央です。そしてもともとの楕円の作り方を上下逆にすると(楕円を下から書き始めると)、右側のようになって、きれいに上下反転します。

図.操作を加えると、、


また、前回報告「Fuisonのスプライン表現」の「sketch.geominfo」でも見たのですが、上の3例でいずれも同じ出力となりました。

app.executeTextCommand('sketch.GeomInfo' )
\t Geometry: id: 334 Spline3D No start point No end point\nSpline details\n\tdegree: 3 \n\trational: 0 \n\tknots: -3.14159 -3.14159 -3.14159 -3.14159 -2.9092 -2.9092 -2.9092 -2.5716 -2.5716 -2.5716 -2.04631 -2.04631 -2.04631 -1.24448 -1.24448 -1.24448 -0.696222 -0.696222 -0.696222 -0.350015 -0.350015 -0.350015 -0.0970022 -0.0970022 -0.0970022 0.131019 0.131019 0.131019 0.400426 0.400426 0.400426 0.823048 0.823048 0.823048 1.5694 1.5694 1.5694 2.27749 2.27749 2.27749 2.70954 2.70954 2.70954 2.9712 2.9712 2.9712 3.14159 3.14159 3.14159 3.14159 \n\tcontrol points: (略) \n\tweights:

上記内容は、3次ベジエ曲線を複数連結したものと言えそうです。3次ベジエは通過する両端点と2つの通過しない制御点を持ち、ノットベクトルを3回繰り返すことが特徴です。この曲線は、フィット点スプラインの代わりに、多数の3次ベジエの両端点を通過点として扱い、3次ベジエ間は、「接線連続」で接続した、と推定します。

せっかく曲率連続で入力可能なNURBS形式に、接線連続な複合ベジエで書き込む理由がわからないのですが、メモリ容量か処理速度によるものかもしれません。

次回に続きます。

Fusionのスプライン表現

Fusionで自由曲線を作る場合、次の2つの曲線タイプがあります。

  • フィット点スプライン(FiitedPointSpline)
  • 制御点スプライン(ControlPointSpline)

この曲線の内部形式を見て、どのような形で保存されていて、何が違うのかをはっきりさせようというのが、この記事の内容です。答えはある程度分かっていて、保存形式はNurbsです。ただ、これは入れ物で合って、中身はベジエ曲線、B-Spline、 Nurbsが、Nurbs形式で管理されているのです。制御点スプラインは「ベジエ曲線」であることも分かっています。
フィット点スプラインがNurbsなのかB-Splineなのかがよくわからなかったのです。

まず、それぞれの曲線で第1象限に1/4円弧を書いてみます。
最初に制御点スプライン

図.制御点スプライン

制御点スプラインというか、3次ベジエ曲線の円弧は、接線ハンドルの長さを半径×0.55228にとるとよいとされます。

  • 制御点の距離 = 半径 × (4/3) × tan(π/8) ≈ 半径 × 0.55228

次にフィット点スプライン

図.フィット点スプライン

こちらは0.188にしていますが、目分量でなるべくぴったりになるよう合わせたものです。

Geom情報取得

この曲線の情報を得るには、曲線をピックしてから、次のコマンドを画面下段のテキストコマンド欄に入力します。

app = adsk.core.Application.get() ・・・・以降省略可能
app.executeTextCommand('sketch.GeomInfo' ) 

これで次の情報を得ました。
制御点スプライン

app.executeTextCommand(textCommand1) 
\t Geometry: id: 414 Spline3D Start point: (0, 0.1, 0) End point: (0.1, 0, 0)\nSpline details\n\tdegree: 3 \n\trational: 1 \n\tknots: 0 0 0 0 1 1 1 1 \n\tcontrol points: (0, 0.1, 0)(0.05522852, 0.1, 0)(0.1, 0.05522852, 0)(0.1, 0, 0) \n\tweights: 1 1 1 1

フィット点スプライン

app.executeTextCommand(textCommand1) 
\t Geometry: id: 467 Spline3D Start point: (0, 0.1, 0) End point: (0.1,0, 0)\nSpline details\n\tdegree: 5 \n\trational: 1 \n\tknots: 0 0 0 0 0 0 0.2 0.4 0.6 0.8 1 1 1 1 1 1 \n\tcontrol points: (0, 0.1, 0)(0.006971742880087394, 0.1, 0)(0.020378689477397946, 0.098938528152542754, 0)(0.038880625084621694, 0.09416466866241438, 0)(0.060504226681762162, 0.082226752495383262, 0)(0.082226752495380542, 0.060504226681764237, 0)(0.094164668662412312, 0.038880625084622381, 0)(0.09893852815254249, 0.020378689477398088, 0)(0.1, 0.0069717428800874209, 0))((-0.1,0, 0) \n\tweights: 1 1 1 1 1 1 1 1 1 1 

見にくいので整理すると

// 制御点スプライン(ID: 414)
const spline1 = {
id: 414,
type: "Spline3D",
startPoint: [0, 0.1, 0],
endPoint: [0.1,0, 0],
degree: 3,
rational: 1,
knots: [0, 0, 0, 0, 1, 1, 1, 1],
controlPoints: [
[0, 0.1, 0],
[0.055228512, 0.1, 0],
[0.1, 0.05522852, 0],
[0.1, 0, 0]
],
weights: [1, 1, 1, 1]
};

// フィットスプライン(ID: 467)
const spline2 = {
id: 467,
type: "Spline3D",
startPoint: [0, 0.1, 0],
endPoint: [0.1,0, 0],
degree: 5,
rational: 1,
knots: [0, 0, 0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1, 1, 1],
controlPoints: [
[0, 0.1, 0],
[0.006971742880087394, 0.1, 0],
[0.020378689477397946, 0.098938528152542754, 0],
[0.038880625084621694, 0.09416466866241438, 0],
[0.060504226681762162, 0.082226752495383262, 0],
[0.082226752495380542, 0.060504226681764237, 0],
[0.094164668662412312, 0.038880625084622381, 0],
[0.09893852815254249, 0.020378689477398088, 0],
[0.1, 0.0069717428800874209, 0],
[0.1, 0, 0]
],
weights: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
};

Claudeで分析

これを、Claudeに分析してもらうと、フィット点スプラインは5次のBスプラインとなります。

3Dスプライン曲線データの整理
スプライン1(ID: 414)- 3次ベジェ曲線

次数: 3次
制御点数: 4点
開始点: (0, 0.1, 0)
終了点: (0.1, 0, 0)
形状: 右上から右下への滑らかな曲線(90度の円弧に近い)

スプライン2(ID: 467)- 5次B-スプライン

次数: 5次
制御点数: 10点
開始点: (0, 0.1, 0)
終了点: (-0.1, 0, 0)
形状: 右上から右下への滑らかな曲線(より複雑な形状)

判別方法

以下に判別方法を解説してもらいました。

Fusion 360では、すべての曲線が内部でNURBS形式で表現されていますが、データの特徴からベジエ曲線、Bスプライン、NURBSを区別できます。

各曲線タイプの特徴

ベジエ曲線(Bezier Curve)

  • 制御点の数: 次数 + 1
  • ノットベクトル: 特殊なパターン
  • 3次ベジエ: [0,0,0,0, 1,1,1,1]
  • 2次ベジエ: [0,0,0, 1,1,1]
  • 特徴: 端点で制御点と一致、中間点は制御点の影響を受ける

Bスプライン(B-Spline)

  • 制御点の数: 任意(次数 + 1以上)
  • ノットベクトル: 一様または非一様
  • 一様Bスプライン: [0,1,2,3,4,5,6,7]
  • 非一様Bスプライン: 任意の値
  • 特徴: 端点で制御点と一致しない場合がある

NURBS(Non-Uniform Rational B-Spline)

  • 制御点の数: 任意
  • ノットベクトル: 非一様
  • 重み: 各制御点に重みが付く
  • 特徴: 最も柔軟な表現

用語解説

ノットベクトル(Knot Vector)

定義

  • ノットベクトルとは、NURBS曲線のパラメータ空間を定義する値の配列です。

役割

  • パラメータ範囲の定義: 曲線のパラメータtの範囲を指定
  • 基底関数の制御: Bスプライン基底関数の形状を決定
  • 曲線の連続性: 曲線の滑らかさを制御

# 2次ベジエ曲線のノットベクトル
knots = [0, 0, 0, 1, 1, 1]
# 意味: パラメータtは0から1の範囲、端点で重複度3

規則

  • 長さ: 制御点の数 + 次数 + 1
  • 単調増加: 値は単調に増加する必要がある
  • 重複度: 端点では次数+1回重複
有理(Rational)

定義

  • 有理とは、制御点に重み(weight)が付いていることを意味します。

数学的表現

# 非有理(Non-rational)
P(t) = Σ Ni,p(t) * Pi

# 有理(Rational)
P(t) = Σ Ni,p(t) * wi * Pi / Σ Ni,p(t) * wi

特徴

  • 非有理: 重みがすべて1.0、または重みなし
  • 有理: 重みが1.0以外の値を持つ

用途: 円弧や円などの正確な表現が可能

# 非有理Bスプライン
weights = [1.0, 1.0, 1.0, 1.0]  # すべて1.0

# 有理NURBS
weights = [1.0, 2.0, 1.0]  # 異なる重み
重み(Weights)

定義

  • 重みとは、各制御点の影響力を表す数値です。

効果

  • 重み = 1.0: 標準的な影響力
  • 重み > 1.0: 制御点の影響力が増加
  • 重み < 1.0: 制御点の影響力が減少

視覚的効果

# 重みの効果
weights = [1.0, 2.0, 1.0]
# 中央の制御点の影響力が2倍になり、曲線がその点に引き寄せられる

特殊なケース

# すべての重みが1.0の場合
weights = [1.0, 1.0, 1.0, 1.0]
# 実質的に非有理Bスプラインと同じ
一様(Uniform)

定義

  • 一様とは、ノットベクトルの値が等間隔であることを意味します。

一様Bスプライン

# 一様ノットベクトル
knots = [0, 1, 2, 3, 4, 5, 6, 7, 8]
# 特徴: 値が等間隔(1ずつ増加)

非一様Bスプライン

# 非一様ノットベクトル
knots = [0, 0, 0, 0, 1, 2, 3, 4, 5]
# 特徴: 値が不均等(開始部分で重複)

違いの効果

  • 一様Bスプライン
    • 特徴: 滑らかで予測可能
    • 用途: 一般的な形状設計
    • 制御: 制御点の影響が均等
  • 非一様Bスプライン
    • 特徴: 端点での制御が可能
    • 用途: 精密な形状制御
    • 制御: 端点での重複により端点通過性を制御


実用的な判別方法
1. ノットベクトルの判別

def isUniformKnots(knots):
    """一様ノットベクトルかチェック"""
    for i in range(1, len(knots)):
        if knots[i] - knots[i-1] != 1:
            return False
    return True

2. 有理性の判別

def isRational(weights):
    """有理曲線かチェック"""
    if not weights:
        return False
    for weight in weights:
        if abs(weight - 1.0) > 1e-10:
            return True
    return False

3. 重みの効果確認

def analyzeWeights(weights):
    """重みの分析"""
    if not weights:
        return "重みなし(非有理)"
    
    all_one = all(abs(w - 1.0) < 1e-10 for w in weights)
    if all_one:
        return "すべて1.0(実質的に非有理)"
    else:
        return f"有理(重み: {weights})"

これらの概念を理解することで、NURBS曲線の特性を正確に把握し、適切な曲線タイプを選択できるようになるはず。