Rendu du Tableau de Fléchettes
Implémentation et bonnes pratiques pour le rendu de tableaux de fléchettes avec p5.js. Couvre l'ordre de dessin, le layering et la gestion des coordonnées.
name: dartboard-rendering description: ダーツボード描画の実装知識とベストプラクティス。p5.jsを使用した描画順序、レイヤリング、座標系の扱いに関する専門知識を提供します。実装・テスト・レビュー時に参照してください。 allowed-tools: Read
Dartboard Rendering Knowledge
ダーツボード描画に関する実装パターンとベストプラクティスを定義します。
描画順序の原則
基本原則: 外側から内側へ、下レイヤーから上レイヤーへ
ダーツボードは以下の順序で描画することで、正しいレイヤリングを実現します:
1. 背景クリア
2. ダブルリング(170mm円全体、赤/緑交互)
3. アウターシングル(162mm円全体、黒/ベージュ交互)
4. トリプルリング(107mm円全体、赤/緑交互)
5. インナーシングル(99mm円全体、黒/ベージュ交互)
6. ★ 外側スパイダー(放射線 + リング境界の同心円)
7. アウターブル(緑色、半径7.95mm)
8. インナーブル(赤色、半径3.175mm)
9. ★ ブル用スパイダー(ブル境界の同心円)
10. セグメント番号
スパイダー(ワイヤー境界線)の2ステップ描画
重要: スパイダーは必ず2ステップに分けて描画する
問題: 一度に描画すると色が被る
// ❌ 悪い例: 全てのスパイダーを一度に描画
drawAllRings();
drawAllSpiders(); // 放射線がブルの色に被る!
drawBull();
解決策: スパイダーを分割描画
// ✅ 良い例: スパイダーを2ステップに分ける
drawAllRings();
drawSpiderOuter(); // 1. 放射線 + リング境界の同心円
drawBull(); // ブルエリアの色を塗る
drawSpiderBull(); // 2. ブル境界の同心円
理由:
- 放射線がブルエリアの色を塗る前に描画されないと、放射線が色に被って見えなくなる
- ブル境界の円はブルエリアの色を塗った後に描画しないと、境界線が見えない
セグメント境界の角度計算
問題: セグメントの真ん中に配置されてしまう
// ❌ 悪い例: セグメントの真ん中に配置される
const angle = -Math.PI / 2 + i * SEGMENT_ANGLE;
解決策: 0.5個分ずらす
// ✅ 良い例: セグメント境界に配置される
const angle = -Math.PI / 2 + (i - 0.5) * SEGMENT_ANGLE;
理由:
drawRingSegments()関数が(index - 0.5) * SEGMENT_ANGLEを使用してセグメントを描画している- スパイダーの放射線も同じ計算式を使うことで、セグメント境界に正確に配置される
座標系の分離原則
物理座標(mm)vs 画面座標(pixel)
鉄則: ビジネスロジックは必ず物理座標で処理し、描画時のみ画面座標に変換する。
// ✅ 良い例
const physicalRadius = BOARD_PHYSICAL.rings.doubleOuter; // 170mm
const screenRadius = transform.physicalDistanceToScreen(physicalRadius);
p5.circle(center.x, center.y, screenRadius * 2);
// ❌ 悪い例
const radius = 170; // 物理座標?画面座標?不明確
p5.circle(center.x, center.y, radius * 2);
CoordinateTransform クラスの使用
// 座標変換の例
const center = transform.getCenter(); // ボード中心の画面座標
const radius = transform.physicalDistanceToScreen(170); // 物理→画面
const width = transform.physicalDistanceToScreen(1.5); // 線幅も変換
p5.js 描画最適化
描画状態の設定はループ外で
// ✅ 良い例: 共通設定はループ外で一度だけ
p5.noStroke(); // ループ外で設定
SEGMENTS.forEach(() => {
p5.fill(color);
p5.arc(...);
});
// ❌ 悪い例: 毎回設定する
SEGMENTS.forEach(() => {
p5.noStroke(); // 20回呼ばれる(無駄)
p5.fill(color);
p5.arc(...);
});
実装パターン
drawSpiderOuter() - 外側スパイダー
export function drawSpiderOuter(p5: p5Types, transform: CoordinateTransform): void {
// 1. 放射線(セグメント境界): 20本
// - ボード中心からboardEdge(225mm)まで
// - (i - 0.5) * SEGMENT_ANGLE でセグメント境界に配置
// 2. リング境界の同心円: 4本
// - ダブル外側(170mm)
// - ダブル内側(162mm)
// - トリプル外側(107mm)
// - トリプル内側(99mm)
}
drawSpiderBull() - ブル用スパイダー
export function drawSpiderBull(p5: p5Types, transform: CoordinateTransform): void {
// ブル境界の同心円: 2本
// - アウターブル(7.95mm)
// - インナーブル(3.175mm)
}
テスト時の考慮事項
描画順序の検証
// 放射線→同心円の順序を検証
const allCalls = [...lineSpy.mock.invocationCallOrder, ...circleSpy.mock.invocationCallOrder];
const lineCalls = lineSpy.mock.invocationCallOrder;
const circleCalls = circleSpy.mock.invocationCallOrder;
// 最後の放射線 < 最初の同心円
expect(Math.max(...lineCalls)).toBeLessThan(Math.min(...circleCalls));
後方互換性の保持
/**
* @deprecated drawSpiderOuter と drawSpiderBull を個別に呼び出すことを推奨
*/
export function drawSpider(p5: p5Types, transform: CoordinateTransform): void {
drawSpiderOuter(p5, transform);
drawSpiderBull(p5, transform);
}
よくある間違いと対策
1. 描画順序の間違い
間違い: スパイダーを一度に全て描画 対策: スパイダーを2ステップに分ける(外側→ブル塗り→ブル用)
2. 座標系の混在
間違い: 物理座標と画面座標が混在 対策: 常に物理座標で計算し、描画直前に変換
3. セグメント境界のずれ
間違い: i * SEGMENT_ANGLE でセグメント中央に配置
対策: (i - 0.5) * SEGMENT_ANGLE で境界に配置
4. 描画最適化の不足
間違い: ループ内で毎回描画状態を設定 対策: 共通設定はループ外で一度だけ
使用方法
このskillは以下のコンテキストで参照してください:
- テスト作成時: test-writerサブエージェントで描画順序やスパイダー分割を考慮
- 実装時: implementサブエージェントで正しい描画パターンを適用
- レビュー時: review-fileサブエージェントで描画順序の正しさを検証
このドメイン知識を常に参照し、ダーツボード描画に関わる実装やテストを作成してください。
Skills similaires
Expert Next.js App Router
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Génère de la documentation API complète au format OpenAPI/Swagger.