Claude 3.7 Sonnetのコーディング能力を試す 8


以前の投稿でClaude 3.7 Sonnetのコーディング能力を試した.

今回は引き続きReactを使ってコードを書いてもらいシェルピンスキーの三角形の勉強を行ってみた.

コードと出力結果は下記.

シェルピンスキーの三角形を様々な形で描画してフラクタル構造を理解しやすく学ぶことができるコードを書いて.
Reactで動的に処理できるようにして.
エラーが出ないように自己点検して.
import { useState, useEffect } from 'react';

// シェルピンスキーの三角形を表示するメインコンポーネント
export default function SierpinskiTriangleExplorer() {
  const [iterations, setIterations] = useState(5);
  const [method, setMethod] = useState('recursive');
  const [size, setSize] = useState(512);
  const [color, setColor] = useState('#ff0000');
  const [bgColor, setBgColor] = useState('#f8f8f8');
  const [showControls, setShowControls] = useState(true);
  
  // パフォーマンス測定用
  const [renderTime, setRenderTime] = useState(0);
  
  const handleIterationsChange = (e) => {
    const value = parseInt(e.target.value);
    if (value >= 0 && value <= 9) {
      setIterations(value);
    }
  };
  
  return (
    <div className="flex flex-col items-center p-4 bg-gray-50 min-h-screen">
      <h1 className="text-3xl font-bold mb-4">シェルピンスキーの三角形エクスプローラー</h1>
      
      {/* コントロールパネル */}
      <div className="mb-4 w-full max-w-3xl">
        <button 
          onClick={() => setShowControls(!showControls)}
          className="mb-2 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
        >
          {showControls ? 'コントロールを隠す' : 'コントロールを表示'}
        </button>
        
        {showControls && (
          <div className="p-4 bg-white rounded shadow-md">
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
              <div>
                <label className="block mb-2">
                  描画方法:
                  <select 
                    value={method} 
                    onChange={(e) => setMethod(e.target.value)}
                    className="ml-2 p-1 border rounded"
                  >
                    <option value="recursive">再帰的描画</option>
                    <option value="chaos">カオスゲーム法</option>
                    <option value="iterative">反復的な四角形除去</option>
                    <option value="svg">SVGパス</option>
                  </select>
                </label>
              </div>
              
              <div>
                <label className="block mb-2">
                  イテレーション:
                  <input 
                    type="number" 
                    min="0" 
                    max="9"
                    value={iterations} 
                    onChange={handleIterationsChange}
                    className="ml-2 p-1 border rounded w-16 text-center"
                  />
                  <input
                    type="range"
                    min="0"
                    max="9"
                    value={iterations}
                    onChange={handleIterationsChange}
                    className="ml-2 w-32"
                  />
                </label>
              </div>
              
              <div>
                <label className="block mb-2">
                  サイズ:
                  <input 
                    type="number" 
                    min="128" 
                    max="800"
                    step="64"
                    value={size} 
                    onChange={(e) => setSize(parseInt(e.target.value))}
                    className="ml-2 p-1 border rounded w-20 text-center"
                  />
                </label>
              </div>
              
              <div>
                <label className="block mb-2">
                  色:
                  <input 
                    type="color" 
                    value={color} 
                    onChange={(e) => setColor(e.target.value)}
                    className="ml-2 p-1 border rounded"
                  />
                </label>
              </div>

              <div>
                <label className="block mb-2">
                  背景色:
                  <input 
                    type="color" 
                    value={bgColor} 
                    onChange={(e) => setBgColor(e.target.value)}
                    className="ml-2 p-1 border rounded"
                  />
                </label>
              </div>
            </div>
            
            <div className="mt-4">
              <p className="text-sm text-gray-600">レンダリング時間: {renderTime.toFixed(2)} ms</p>
            </div>
          </div>
        )}
      </div>
      
      {/* 三角形の表示エリア */}
      <div 
        className="p-4 rounded-lg shadow-lg mb-4"
        style={{ backgroundColor: bgColor }}
      >
        <SierpinskiRenderer 
          method={method}
          iterations={iterations}
          size={size}
          color={color}
          bgColor={bgColor}
          onRenderComplete={setRenderTime}
        />
      </div>
      
      {/* 説明 */}
      <div className="w-full max-w-3xl mt-4 p-4 bg-white rounded shadow-md">
        <h2 className="text-xl font-bold mb-2">フラクタル構造について</h2>
        <p className="mb-2">
          シェルピンスキーの三角形は、自己相似性を持つフラクタル図形の代表例です。
          各イテレーションで、三角形を3つの小さな三角形に分割し、中央の三角形を取り除く操作を繰り返します。
        </p>
        <h3 className="text-lg font-semibold mt-4 mb-2">描画方法の説明:</h3>
        <ul className="list-disc pl-5">
          <li className="mb-1"><strong>再帰的描画:</strong> 三角形を再帰的に分割して描画します。各レベルで中央の三角形を除去します。</li>
          <li className="mb-1"><strong>カオスゲーム法:</strong> ランダムな点を選び、頂点との中点に繰り返し移動させることでパターンを生成します。</li>
          <li className="mb-1"><strong>反復的な四角形除去:</strong> 格子状に三角形を配置し、反復的にパターンを生成します。</li>
          <li className="mb-1"><strong>SVGパス:</strong> SVGパスを使って直接描画します。</li>
        </ul>
        
        <h3 className="text-lg font-semibold mt-4 mb-2">性質:</h3>
        <ul className="list-disc pl-5">
          <li className="mb-1">フラクタル次元: 約 1.585 (log(3)/log(2))</li>
          <li className="mb-1">自己相似性: 任意の部分を拡大すると全体と同じパターンが現れます</li>
          <li className="mb-1">イテレーション数を増やすほど、より細かいパターンが現れます</li>
        </ul>
      </div>
    </div>
  );
}

// 異なる方法でシェルピンスキーの三角形を描画するコンポーネント
function SierpinskiRenderer({ method, iterations, size, color, bgColor, onRenderComplete }) {
  const [key, setKey] = useState(0);

  useEffect(() => {
    // パラメータが変更されたときに再レンダリング
    setKey(prev => prev + 1);
  }, [method, iterations, size, color, bgColor]);

  switch (method) {
    case 'recursive':
      return <RecursiveSierpinski 
        key={key}
        iterations={iterations} 
        size={size} 
        color={color}
        onRenderComplete={onRenderComplete}
      />;
    case 'chaos':
      return <ChaosSierpinski 
        key={key}
        iterations={iterations} 
        size={size} 
        color={color}
        onRenderComplete={onRenderComplete}
      />;
    case 'iterative':
      return <IterativeSierpinski 
        key={key}
        iterations={iterations} 
        size={size} 
        color={color}
        onRenderComplete={onRenderComplete}
      />;
    case 'svg':
      return <SvgSierpinski 
        key={key}
        iterations={iterations} 
        size={size} 
        color={color}
        onRenderComplete={onRenderComplete}
      />;
    default:
      return <div>不明な描画方法です</div>;
  }
}

// 再帰的描画による三角形
function RecursiveSierpinski({ iterations, size, color, onRenderComplete }) {
  const canvasRef = useRef(null);
  
  useEffect(() => {
    const startTime = performance.now();
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    
    // キャンバスをクリア
    ctx.clearRect(0, 0, size, size);
    
    // 三角形を描画する関数
    function drawTriangle(x1, y1, x2, y2, x3, y3) {
      ctx.beginPath();
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.lineTo(x3, y3);
      ctx.closePath();
      ctx.fill();
    }
    
    // 再帰的にシェルピンスキーの三角形を描画
    function sierpinski(x1, y1, x2, y2, x3, y3, depth) {
      if (depth === 0) {
        drawTriangle(x1, y1, x2, y2, x3, y3);
        return;
      }
      
      // 各辺の中点を計算
      const x12 = (x1 + x2) / 2;
      const y12 = (y1 + y2) / 2;
      const x13 = (x1 + x3) / 2;
      const y13 = (y1 + y3) / 2;
      const x23 = (x2 + x3) / 2;
      const y23 = (y2 + y3) / 2;
      
      // 3つの小さな三角形を描画(中央は描画しない)
      sierpinski(x1, y1, x12, y12, x13, y13, depth - 1);
      sierpinski(x12, y12, x2, y2, x23, y23, depth - 1);
      sierpinski(x13, y13, x23, y23, x3, y3, depth - 1);
    }
    
    // 描画設定
    ctx.fillStyle = color;
    
    // 初期三角形の頂点
    const h = size * Math.sqrt(3) / 2;
    const x1 = size / 2;
    const y1 = 0;
    const x2 = 0;
    const y2 = h;
    const x3 = size;
    const y3 = h;
    
    // 描画実行
    sierpinski(x1, y1, x2, y2, x3, y3, iterations);
    
    const endTime = performance.now();
    onRenderComplete(endTime - startTime);
  }, [iterations, size, color, onRenderComplete]);
  
  return (
    <canvas
      ref={canvasRef}
      width={size}
      height={size * Math.sqrt(3) / 2}
      className="border border-gray-200"
    />
  );
}

// カオスゲーム法による三角形
function ChaosSierpinski({ iterations, size, color, onRenderComplete }) {
  const canvasRef = useRef(null);
  
  useEffect(() => {
    const startTime = performance.now();
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    
    // キャンバスをクリア
    ctx.clearRect(0, 0, size, size);
    
    // 点を描画する関数
    function drawPoint(x, y) {
      ctx.fillRect(x, y, 1, 1);
    }
    
    // 描画設定
    ctx.fillStyle = color;
    
    // 初期三角形の頂点
    const h = size * Math.sqrt(3) / 2;
    const vertices = [
      { x: size / 2, y: 0 },
      { x: 0, y: h },
      { x: size, y: h }
    ];
    
    // 初期点(ランダム)
    let point = {
      x: Math.random() * size,
      y: Math.random() * h
    };
    
    // カオスゲーム法でポイントを生成
    const pointCount = 5000 * (iterations + 1); // イテレーション数に応じてポイント数を調整
    
    for (let i = 0; i < pointCount; i++) {
      // ランダムに頂点を選択
      const vertex = vertices[Math.floor(Math.random() * 3)];
      
      // 現在地から選んだ頂点への中点を計算
      point = {
        x: (point.x + vertex.x) / 2,
        y: (point.y + vertex.y) / 2
      };
      
      // 最初の20点は初期値の影響を受けるため描画しない
      if (i > 20) {
        drawPoint(point.x, point.y);
      }
    }
    
    const endTime = performance.now();
    onRenderComplete(endTime - startTime);
  }, [iterations, size, color, onRenderComplete]);
  
  return (
    <canvas
      ref={canvasRef}
      width={size}
      height={size * Math.sqrt(3) / 2}
      className="border border-gray-200"
    />
  );
}

// 反復的な四角形除去による三角形
function IterativeSierpinski({ iterations, size, color, onRenderComplete }) {
  const canvasRef = useRef(null);
  
  useEffect(() => {
    const startTime = performance.now();
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    
    // キャンバスをクリア
    ctx.clearRect(0, 0, size, size);
    
    const h = size * Math.sqrt(3) / 2;
    const cellSize = size / Math.pow(2, iterations);
    
    // 描画設定
    ctx.fillStyle = color;
    
    // 格子を基にして描画
    for (let x = 0; x < size; x += cellSize) {
      for (let y = 0; y < h; y += cellSize) {
        // 座標を正規化
        const normX = Math.floor(x / cellSize);
        const normY = Math.floor(y / cellSize);
        
        // 2進数でXORを取り、結果が0のセルを描画
        if ((normX & normY) === 0) {
          ctx.fillRect(x, y, cellSize, cellSize);
        }
      }
    }
    
    const endTime = performance.now();
    onRenderComplete(endTime - startTime);
  }, [iterations, size, color, onRenderComplete]);
  
  return (
    <canvas
      ref={canvasRef}
      width={size}
      height={size * Math.sqrt(3) / 2}
      className="border border-gray-200"
    />
  );
}

// SVGパスによる三角形
function SvgSierpinski({ iterations, size, color, onRenderComplete }) {
  const svgRef = useRef(null);
  const [path, setPath] = useState("");
  
  useEffect(() => {
    const startTime = performance.now();
    
    // シェルピンスキーの三角形のSVGパスを生成
    function generateSierpinskiPath(iterations) {
      // 基本単位のパス
      const baseUnit = (x, y, size) => {
        const h = size * Math.sqrt(3) / 2;
        return `M ${x},${y} L ${x + size / 2},${y + h} L ${x - size / 2},${y + h} Z`;
      };
      
      // 再帰的にパスを生成
      function generatePath(x, y, size, depth) {
        if (depth === 0) {
          return baseUnit(x, y, size);
        }
        
        const newSize = size / 2;
        const h = newSize * Math.sqrt(3) / 2;
        
        let path = generatePath(x, y, newSize, depth - 1);
        path += generatePath(x - newSize / 2, y + h, newSize, depth - 1);
        path += generatePath(x + newSize / 2, y + h, newSize, depth - 1);
        
        return path;
      }
      
      return generatePath(size / 2, 0, size, iterations);
    }
    
    const newPath = generateSierpinskiPath(iterations);
    setPath(newPath);
    
    const endTime = performance.now();
    onRenderComplete(endTime - startTime);
  }, [iterations, size, color, onRenderComplete]);
  
  return (
    <svg
      ref={svgRef}
      width={size}
      height={size * Math.sqrt(3) / 2}
      className="border border-gray-200"
    >
      <path d={path} fill={color} />
    </svg>
  );
}

// 必要なフック
import { useRef } from 'react';

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です