コンテンツへスキップ
CADCAMFUN
ドキュメント

ドキュメント

ビューポート

キャンバス描画ツールと Three.js レンダリングは、編集と再生の状態を明示的に保ちながら、コア ジオメトリを共有します。

英語から自動翻訳されています。 英語の原文を見る

ビューポートコンポーネント

apps/desktop/src/viewport/index.ts 輸出 Editor2D, View3D、そのプロップのタイプ、使用可能なツール識別子、および solidsFromProfiles。どちらのコンポーネントも親を埋めます。つまり、ゼロ以外の幅と高さのコンテナを提供します。コア CAD エンティティと CAM プログラムは共有データ モデルです。

2D エディタは Canvas 2D にレンダリングし、独自の選択、カメラ、コマンド履歴を維持します。 3D ビューアは、Three.js、オービットおよび変換コントロール、非同期メッシュ ジョブ、およびプログラム再生を使用します。パスまたはソリッドのレンダリングは、パスまたはソリッドの保存や製造操作の検証とは無関係です。

統合契約

Editor2DProps 必要 drawing, onChange、 そして selectedTool。放出された図面をプロップに送り返します。コンポーネントは、自身が発行したドキュメント オブジェクトを認識します。ドキュメントを置き換えると、新しい図面が外部でロードされ、エディタの状態がリセットされます。

制御された 2D エディタ
import { createSignal } from "solid-js"
import { Editor2D, type ToolId } from "./viewport/index.ts"

const [drawing, setDrawing] = createSignal(initialDrawing)
const [tool, setTool] = createSignal<ToolId>("select")

<Editor2D drawing={drawing()} onChange={setDrawing} selectedTool={tool()} />

選択では、ロックされたエンティティとロックされたレイヤー上のエンティティが除外されます。非表示のエンティティと非表示のレイヤーは、レンダリングされた空間インデックスから除外されます。新しいエンティティはドキュメント固有の識別子を受け取り、表示されるロック解除されたレイヤーが利用可能な場合はそのレイヤーに割り当てられます。

描画ツールとジェスチャー

ツール レジストリには、選択、線分、長方形、円、楕円、ポリライン、円弧、テキスト、寸法、移動、回転、スケール、ミラー、オフセット、トリム、およびフィレットのファクトリがあります。 AVAILABLE_TOOLS 登録された工場から派生します。 startTool 追加で認識します chamfer フィレット ツール ファクトリを通じて実行されますが、面取りはその配列ではアドバタイズされません。消費者は実際のレジストリを使用して、どのツールを有効にするかを決定する必要があります。

  • ホイールまたはピンチでズームします。スペース キー、中キー キー、および右キー キーを押しながらドラッグしてパンします。
  • F インデックス付きの図面に適合します。削除または Backspace キーを押すと、ロックされていない選択範囲が削除されます。
  • Cmd/Ctrl+Z 元に戻す; Cmd/Ctrl+Shift+Z または Cmd/Ctrl+Y やり直します。
  • Enter はポリラインをコミットします。エスケープはアクティブなツールに配信され、現在のジェスチャをキャンセルします。
  • テキストがアクティブな状態で、配置する前に文字または Backspace を入力します。最初のテキストは Text.

ポインターの位置は、ツールをディスパッチする前にワールド座標に変換されます。スナップでは、空間インデックス、幾何学的候補、およびカメラ依存のグリッドが結合されます。選択ヒット許容値は 8 画面ピクセルに対応します。ツール マシンは、ファイルを書き込んだり API を呼び出したりするのではなく、コマンドとプレビューを生成します。

3D レンダリングと再生

View3D 図面、ソリッド、オプションのプロファイル押し出し、 cam.Program、ストック寸法、および工具定義。遠近法、上面、下面、前面、後面、左面、右面、アイソメトリック、および 4 つのビューのプリセットをサポートしています。表示モードは、シェーディング、エッジ付きシェーディング、ワイヤフレーム、および X 線です。照明、マテリアル プロパティ、クリッピング セクション、グリッド、軸、原点はプロップを通じて設定できます。

再生を制御する playing, playbackTime 数秒で、 playbackRate、 そして onPlaybackTimeChange。再生では、穴あけサイクルを拡張し、円形/螺旋運動をテッセレーションし、滞留時間を維持し、素早い動きを明確に描画します。ツール マップとオプションのデフォルト カッターは、カッター、シャンク、ホルダーの形状を提供します。想定される形状が視覚的に区別されます。

選択コールバックは固体インデックスを報告します。変換コントロールは、位置、回転、スケールを放出します。 onTransformCommit;呼び出し元は、その変換を永続化する方法を決定します。ツールパス オーバーレイでは、ポイントとラベル、ポイントの種類フィルター、円弧の中心、円弧の半径、およびストックの透明度を表示できます。

再生は視覚的な表現です

ビューアは提供されたプログラムをアニメーション化し、ストックやツールを描画します。材料の除去は実行されません。衝突チェックとマシンエンベロープチェックは CAM 検証モジュールに属します。番組の視聴は機械の承認にはなりません。

座標と単位

コア図面の座標とストック サイズにはミリメートルが使用されます。描画角度と Three.js 回転コンポーネントではラジアンが使用されます。この規則は、明示的に度を使用する CAM 回転軸値またはフィーチャ入力には適用しないでください。 viewport/units.ts 表示されるミリメートルとインチの変換ヘルパーが含まれています。入力/出力境界で変換するため、スナップ、ジオメトリ、およびツールパスの動作が一貫した単位系を維持します。

2D カメラは世界座標を CSS ピクセルにマッピングします。バッキング キャンバスの寸法にはデバイスのピクセル比が組み込まれます。サイズを変更すると、カメラとバッキング バッファーが更新されます。 ResizeObserver。カメラのスケールによって、グリッド間隔とスクリーンスペースのヒット許容値が決まります。

インチ表示は、1 インチあたり正確に 25.4 mm で変換され、長さはデフォルトで小数点以下 3 桁になります (apps/desktop/src/viewport/units.ts)。 2D エディタでは、各ホイール ステップは 1.15 の係数でズームし、スケールは 0.001 と 100、000 の間に固定され、ビューに合わせて 10% のマージンが残ります (editor2d/camera.ts).

リソースとライフサイクル

2D コンポーネントはキューに入れられたアニメーション フレームをキャンセルし、アンマウント時にサイズ変更オブザーバーを切断します。 3D コンポーネントは、サイズ変更と表示リスナー、オービットと変換コントロール、メッシュ ワーカー、アニメーション スケジュール、ジオメトリ、マテリアル、スプライト、ツール メッシュ、WebGL レンダラーをリリースします。

フレーム スケジューラは無効化を結合し、カメラのダンピングまたは再生でフレームが必要なときに追加のフレームを要求します。可視性が変化すると作業が中断されます。これはスケジュール ポリシーであり、すべてのモデルやデバイスに対してフレーム レートの予算が保証されているわけではありません。

モジュールマップ

  • editor2d/editor2d.tsx: 制御されたコンポーネント、ポインターのディスパッチ、カメラ、選択、履歴、およびキャンバスのペイント。
  • editor2d/tools/: ジェスチャ ステート マシンとコマンドの構築。
  • editor2d/camera.ts, render.ts、 そして snap-pipeline.ts: 座標変換、エンティティのレンダリング、ピッキング、スナップ オーケストレーション。
  • spatial/grid.ts: 図面空間インデックス。
  • view3d/view3d.tsx: シーン構成、カメラ コントロール、選択、変換、ストック、ツール。
  • view3d/playback.ts, toolpath-overlays.ts、 そして modeling.ts: モーション タイムライン、オーバーレイ データ、プロファイルからソリッドへの変換。
  • view3d/mesh-jobs.ts そして mesh-worker.ts: 背景ジオメトリの構築とジョブの所有権。

ツールの利用可能性

ToolId より大きなツールの語彙について説明します。 AVAILABLE_TOOLS は、登録されたサブセットについて説明します。インタラクティブなツールの場合は、次を使用します。 startTool(id, lookup, context, text)。選択ルックアップは候補エンティティをクエリします。コンテキストは現在の選択とエンティティへのアクセスを提供します。 text は注釈の内容を提供します。

行方不明だった工場が戻ってくる undefined。無効または未実装のツールをアクティブな編集モードとして表示するのではなく、明示的に処理します。既存のジオメトリを変更するツールには、現在の選択内容とエンティティ コンテキストが必要です。これらの依存関係なしでファクトリを呼び出すだけでは、有用な編集を行うには不十分です。

メッシュ化と検証

推定頂点数が 50、000 を超えるソリッドとブラウザ ワーカー サポートの場合、ビューアはモジュール ワーカーのメッシュ構築をキューに入れます。より小さいソリッドは同期的にメッシュされます。メッシュ キューは、完了、古いジョブ、ワーカー エラー、クリーンアップを処理します。しきい値はスケジュールに使用される推定値であり、文書化されたモデル サイズの制限ではありません。

リポジトリ ルートからのビューポート ロジック チェック
bun test apps/desktop/src/viewport
bun run --filter @cadcamfun/desktop typecheck

ロジック テストには、レンダリング ヘルパー、ツール ジェスチャ、空間インデックス作成、再生、オーバーレイが含まれます。ブラウザ/WebGL の動作はグラフィック環境にも依存します。カメラ コントロール、シーンのライフサイクル、またはレンダリングを変更するときに、実際のビューポートのインタラクションを検査します。

制御された再生の例

親は、ビューアのモーション タイムラインを複製することなく、トランスポート コントロールを所有できます。このコンポーネントは、すでに構築されている CAM プログラムを受け入れ、時間、再生状態、速度、およびビューポートのプリセットを制御します。インポートパスは直下のファイルを想定しています。 apps/desktop/src.

外部トランスポートコントロールを使用したプログラムプレビュー
import { createSignal } from "solid-js"
import { cam } from "@cadcamfun/core"
import { View3D } from "./viewport/index.ts"

export function ProgramPreview(props: { program: cam.Program }) {
  const [seconds, setSeconds] = createSignal(0)
  const [playing, setPlaying] = createSignal(false)
  return <div style={{ height: "600px" }}>
    <button onClick={() => setPlaying(value => !value)}>Play / pause</button>
    <button onClick={() => { setSeconds(0); setPlaying(true) }}>Restart</button>
    <View3D
      program={props.program}
      preset="isometric"
      playing={playing()}
      playbackTime={seconds()}
      playbackRate={1}
      onPlaybackTimeChange={setSeconds}
      stock={{ origin: { x: 0, y: 0, z: -10 }, size: { x: 100, y: 80, z: 10 } }}
    />
  </div>
}

再生ビルダーは原点から開始し、サイクルを移動に拡張し、移動長とフィードから継続時間を計算します。デフォルトの高速速度は 5,000 mm/min、デフォルトの円弧テッセレーション許容値は 0.1 mm です。これらはプレビューの仮定であり、マシンの加速度やコントローラーのタイミングを測定したものではありません。 Dwell コマンドは指定された秒数を保持します。コメントと方向のみのエントリは直線移動を追加しません。

ツールの色は、各ツール識別子の最初の出現によって割り当てられ、そのパス全体で保持されます。アセンブリ ジオメトリのツール マップ/デフォルト ツールを指定します。色付きのパスだけでは、検証で使用されるカッターまたはホルダーの寸法は確立されません。

レシピと現在のツール制限の編集

手術ジェスチャーと前提条件現在の動作
オフセットサポートされているエンティティを選択し、ドラッグします。 Shift キーを押すと、代わりにカーソルの下にあるエンティティが選択されます。線分/ポリライン/長方形ではポリラインの結果が生成されます。円と円弧の実装では、半径を維持しながら中心を移動します。このツールは、曲線に対する一般的な同心オフセット操作ではありません。
トリム/延長まずソースと境界エンティティを選択し、トリムをアクティブにして、ターゲットをクリックします。 Alt キーで拡張モードを選択します。この実装では、選択された他のエンティティの交差が検索されます。線とポリラインを変更します。サポートされていないターゲットの種類または交差が欠落している場合、編集は行われません。
フィレット・面取り交差する 2 本の線を選択します。 Shift で面取りに切り替わります。共有ツールは初期半径 5 mm を使用し、そのペアをトリムされた線と接続ポリラインに置き換えます。これは任意のソリッドエッジ フィレットではありません。
ソリッドを変形する提供する onTransformCommit、メッシュを選択し、移動/回転/スケールをアクティブにします。コールバックは、ソリッド インデックスと変換値を報告します。 4 ビュー モードでは、ピッキング ギズモと変換ギズモは無効になります。

これらの詳細は、パレットのラベルではなく、ジェスチャの実装から得られます。コミットする前にプレビューを確認し、結果が意図したジオメトリと異なる場合は、「元に戻す」を使用してください。ツールがコマンドを返さない場合は、エディターがポインター イベントを失ったわけではなく、幾何学的前提条件が満たされていないことを意味している可能性があります。

レンダリングとメッシュの失敗

バックグラウンド メッシュ キューは、デフォルトで最大 128 個の保留中のジョブを許可し、一度に 1 つずつ実行され、ジョブごとにデフォルトで 15 秒のタイムアウトが設定されます。シーンをリセットするとワーカーが終了し、キューに入れられた作業がクリアされます。古い応答は無視されます。無効なワーカー応答、メッセージ デコード エラー、タイムアウト、およびキュー容量障害は、キュー コールバックを通じてエラーを報告します。

症状アクション
ジオメトリのない空のキャンバス親にゼロ以外のサイズを指定し、エンティティ/レイヤーの可視性を確認して、ビューをフィットさせます。
何も選択できません2D でエンティティ/レイヤのロックを確認します。 3D で 4 ビュー モードと変換状態を確認します。
メッシュタイムアウトまたはキューがいっぱいですシーンの複雑さまたは同時に送信されるソリッドの数を減らし、ワーカー エラーを検査します。失敗したバッチは、完了したジョブとして部分的にインストールされるのではなく、クリアされます。
再生時間が間違っているようですプログラムのフィード、滞留移動、高速速度の仮定、および変更されたプログラムが親時間をリセットしたかどうかを確認します。
WebGLは利用できませんサポートされているグラフィックス環境を使用し、コンポーネントの使用できないレンダラー状態を検査します。ロジック テストでは GPU の可用性を証明できません。

関連するテストには以下が含まれます editor2d/tools/offset.test.ts, annotation-transform.test.ts, spatial/grid.test.ts, view3d/playback.test.ts、 そして toolpath-overlays.test.ts。メッシュキューの実装は次のとおりです。 view3d/mesh-jobs.ts.

表示単位の詳細

エディターは常にミリメートル単位で作業します。以下の正確な要素を使用して、ユーザーが読み取って入力した内容のみが変換されます。保存された図面自体のユニットはそのまま残ります mm または inch;追加の単位は表示のみの選択肢です。

ユニットラベル単位当たりのmmデフォルトの小数点Gコードプログラムユニット
mmmm13mm
umµm0.0011mm
cmcm104mm
mm10006mm
inchインチ25.44インチ
thouあなた0.02542インチ
長さの変換と解析 (apps/desktop/src/viewport/units.ts)
import { formatLength, parseLength, formatAngle, programUnitsOf } from "./units"

formatLength(25.4, "inch")     // "1.0000 inch"
formatLength(12.5)             // "12.500 mm"
formatLength(0.0254, "thou")   // "1.00 thou"
parseLength("2 in", "inch")    // 50.8  (a trailing unit word is ignored)
parseLength("abc")             // undefined
formatAngle(Math.PI / 2)       // "90.0°"
programUnitsOf("thou")         // "inch"
parseLength 末尾のアルファベット接尾辞 (を含む) を削除します。 µ そして °) を使用して、現在の表示単位の数値を解釈します。接尾辞は単位の選択には使用されないため、次のように入力します。 2 mm 表示がインチである場合、換算値は 50.8 mm になります。空、非数値、および無限の入力が返される undefinedその場合、編集者は以前の値を保持する必要があります。

2D カメラとグリッド

ワールド (mm、+Y 上)画面 (ピクセル、+Y 下) 中心+X+Y (幅/2、高さ/2)+x+y →
2D エディタのカメラは、ビューポートの中心のワールド ポイントと、1 ミリメートルあたりのピクセル単位のスケールを保存します。 worldToScreen y を反転して、ワールド +Y が画面上で上を向くようにします。
定数・関数価値行動
ZOOM_STEP1.15ホイールステップごとの係数。 zoomAt カーソルの下のワールド ポイントを固定したままにします。
MIN_SCALE / MAX_SCALE0.001 / 100,000 ピクセル/mmスケールはこの範囲に固定されます。
FIT_MARGIN0.1マージンとして保持されるビューポートの割合 cameraForBounds.
createCamera()中心(0、0)、スケール 1、800 × 600最初のサイズ変更前の初期カメラ。
gridStep(scale, major = 10, minPixels = 8)1-2-5 はしごグリッド ドットを少なくとも 8 ピクセル離すための最小ステップ。
世界と画面間のマッピング
const camera = createCamera({ x: 0, y: 0 }, 2, 800, 600)   // 2 px per mm
worldToScreen(camera, { x: 10, y: 10 })   // { x: 420, y: 280 }
screenToWorld(camera, { x: 420, y: 280 }) // { x: 10, y: 10 }
gridStep(2)    // 5   -> 5 mm apart = 10 px
gridStep(0.5)  // 20  -> 20 mm apart = 10 px

レンダリングによりエンティティがカリングされる visibleBounds(camera)。また、スナップによって図面全体のスキャンが回避されます。均一な空間グリッドは、境界がスナップ許容値に 2 mm の候補マージンを加えた範囲内にあるエンティティを返し、それらのみがコアに渡されます。 findSnap。これにより、多くのエンティティを含む図面でのポインタの動きが速くなります。

スナップリファレンス

スナップ タイプは優先順位に従って試行されます。デフォルトの順序は従来のエディターと一致します。 endpoint, midpoint, center, intersection, perpendicular, tangent, nearest、 そして quadrant。エンドポイントはソース エンティティの頂点であり、テッセレーションの頂点ではありません。

設定意味
enabledマスタースイッチ。 false すべてのスナップを無効にします。
types有効なスナップ タイプ。最も高い優先順位が最初に設定されます。
toleranceスナップ半径 (ミリメートル単位)。ズームには依存しません。
gridEnabled, gridSpacingオブジェクト スナップが適用されない場合は、グリッドにスナップします。
referencePoint引き出されるポイント。 perpendicular そして tangent それなしでは何も見つかりません。

パイプラインは、スナップ、カーソルの下にあるエンティティの ID (選択ツールが使用する最も近いものから順)、および考慮された候補の数を返します。

ツールパスの再生タイミングの調整方法

buildPlayback(program, rapidRate = 5000, tolerance = 0.1) cAM プログラムを時間指定セグメントに変換します。各移動の持続時間は、その長さをその送り量(または急流の場合は速さ)で割ったものに 60 を掛けたもので、秒数が得られます。滞在者は移動せずに自分の時間を追加します。コメント、方向移動、固定サイクルはセグメントとして描画されません。アークはコードに分割され、そのサジッタはコード内に留まります tolerance (2 と 65 の間、弧ごとに 536 弦)、Z はらせんの弧に沿って補間されます。
プレビューを検索して色付けする
const playback = buildPlayback(program)          // rapids at 5000 mm/min
playback.duration                                // total seconds
positionAt(playback, 12.5)                       // tool position at t = 12.5 s
toolAt(playback, 12.5)                           // toolId cutting at that time
toolColors(program)                              // Map toolId -> colour, first use order

// A 10 mm feed move at 600 mm/min lasts 10 / 600 × 60 = 1 s.
// The same 10 mm as a rapid lasts 10 / 5000 × 60 = 0.12 s.
再生プレビュー ドラッグして周回します · スクロールしてズーム · 右ドラッグしてパンします
CAM ページと同じポケット プログラム。最初のツール カラー (388bdf22) で再生されます。デスクトップでは、追加の各ツールがパレット内の次の色を取得します: 5597316c879e、9f15、#e#f#、#facc# など)。
segmentIndexAt は二分探索によって再生セグメントを見つけるため、長いプログラムであってもシークは安価です。最後を越えてシークすると最終位置が返され、 toolAt 次に、最後のツールを報告します。再生時間は、プログラムされたフィードに基づく視覚的な推定値です。マシンの加速は無視されるため、実際のマシンではさらに時間がかかります。

ビューポートの実践

  • エッジで変換: ミリメートルとラジアンで保存および計算し、表示専用にフォーマットします。 formatLength そして formatAngle.
  • 解析後の長さは決して再利用しないでください parseLength 返品 undefined;以前の値を保持し、検証ヒントを表示します。
  • 描画ツールを追加するときは、それをツール ファクトリ テーブルに登録し、ツールバー、ショートカット、テストが同じ ID でアドレス指定できるようにします。
  • スナップ候補クエリを空間的に保ちます。すべてのエンティティを渡す findSnap ポインタが移動するたびにインデックスが無効になります。
  • 再生時間をサイクルタイムの見積もりではなくプレビューとして扱います。

大規模なヒット テスト: 空間グリッド

2D エディタは、60、10 エンティティを使用して 000 fps をターゲットにしているため、ヒット テストとカリングではポインタ イベントごとにすべてのエンティティをスキャンすることはありません。 viewport/spatial/grid.ts 均一なグリッドを維持します。各アイテムはその境界ボックスが接するすべてのセルに登録され、クエリは境界がクエリ ボックスと交差するすべてのアイテムを挿入順に返します。結果は正確であり、決してサンプルではありません。

設定価値なぜ
デフォルトのセルサイズ50 世界単位 (mm)直径約 1 m の図面に適しています
最小セルサイズ1e-6永久にループするゼロまたは負のサイズを防止します。
アイテムあたりのセル数の制限4,096大きな項目 (シート全体にわたる長い線) は、セルごとではなく脇に追跡されます。
方法行動
build(items)ゼロから再構築する
upsert(id, item)1 つの項目を ID で挿入または置換します
remove(id)取り除く;不明な ID は無視されます
query(bounds)境界がボックスと交差する ID
クエリボックスエンティティAB長い列

破線のクエリ ボックスに触れたセルがアクセスされます。 A と長い線は、それらの境界が交差するため返されますが、B は交差しません。

3D メッシュ キュー

ソリッドは Web Worker でメッシュ化されるため、UI スレッドがブロックされることはありません。 MeshWorkerQueue で view3d/mesh-jobs.ts 一度に 1 つのジョブを実行します。

ルール価値失敗時のメッセージ
容量 (キュー + 実行中)1283D メッシュ キューがいっぱいです。シーンの複雑さを軽減します。
ジョブごとのタイムアウト15 s3D メッシュ作成がタイムアウトしました。シーンの複雑さを軽減します。
作業者のエラー—ワーカーのメッセージ、または「メッシュ ワーカーが予期せず停止しました。」
デコード不能な応答—メッシュ ワーカーはその応答をデコードできませんでした。
不正なジオメトリ位置は次でなければなりません Float32Array、インデックス Uint32Arrayメッシュ ワーカーが無効なジオメトリを返しました。

失敗するとワーカーが終了し、キュー全体がクリアされるため、新しい編集後に古いシーン作業が反映されることはありません。次回の送信では新しいワーカーが開始されます。応答はジョブ ID によって照合されるため、解雇された従業員からの遅れた応答は無視されます。

2D ツールの実装

ファイルツール
tools/two-point.ts線、長方形、直径による円、楕円
tools/chained.tsポリライン、3 点円弧
tools/fillet-chamfer.ts2本の線の間の角を丸めるか面取りする
tools/offset.tsポリライン、長方形、円、または円弧を距離だけオフセットします
tools/trim.tsトリミングして延長する
tools/select.tsクリックしてボックスを選択し、選択項目を追加/削除します
tools/annotation-transform.ts注釈の移動と変形

各ツールは、次の機能を備えた小さなステート マシンです。 onPointerDown, onPointerMove そして onPointerUpの工場によって作成されました。 tools/index.ts、パレット エントリごとに 1 つ。ポインターの位置が通過します snap-pipeline.ts ツールがそれらを認識する前に。