ST_FUNC開発日記

建築構造設計Excelアドイン開発の記録

断面形状の描画 その1

数値を入力しただけだと地味なので、断面形状を描画できるようにしたい。

さっそくChatGPTに相談してみる。

reactのフロントエンドアプリを作っており、図形を描画したい旨をいろいろ聞いてみたら、下記のようなコードを提案された。

import React, { useState } from 'react';

const LineDrawer = () => {
  const [startX, setStartX] = useState(50);
  const [startY, setStartY] = useState(50);
  const [endX, setEndX] = useState(150);
  const [endY, setEndY] = useState(150);
  const [lines, setLines] = useState([]);

  const addLine = () => {
    const newLines = [
      ...lines,
      { startX, startY, endX, endY, id: Date.now() },
    ];
    setLines(newLines);
  };

  return (
    <div>
      <div>
        <label htmlFor="startX">Start X:</label>
        <input
          type="number"
          id="startX"
          value={startX}
          onChange={(e) => setStartX(Number(e.target.value))}
        />
      </div>
      <div>
        <label htmlFor="startY">Start Y:</label>
        <input
          type="number"
          id="startY"
          value={startY}
          onChange={(e) => setStartY(Number(e.target.value))}
        />
      </div>
      <div>
        <label htmlFor="endX">End X:</label>
        <input
          type="number"
          id="endX"
          value={endX}
          onChange={(e) => setEndX(Number(e.target.value))}
        />
      </div>
      <div>
        <label htmlFor="endY">End Y:</label>
        <input
          type="number"
          id="endY"
          value={endY}
          onChange={(e) => setEndY(Number(e.target.value))}
        />
      </div>
      <button onClick={addLine}>Add Line</button>
      <div style={{ position: 'relative', width: '500px', height: '500px', border: '1px solid black' }}>
        {lines.map(line => (
          <svg key={line.id}>
            <line
              x1={line.startX}
              y1={line.startY}
              x2={line.endX}
              y2={line.endY}
              style={{ stroke: 'black', strokeWidth: 2 }}
            />
          </svg>
        ))}
      </div>
    </div>
  );
};

export default LineDrawer;

なるほど。最初はcanvasか何かを使うのかなと思ったけど、コンポーネントsvgを作ってしまえばいいのか。

念のために聞いてみる。

reactで描画を行おうと思います。canvasを使うのとsvgを使うのどちらのほうが良いですか?

canvasSVGのどちらを使うかは、具体的な描画の内容や目的によって異なります。

  • Canvasは、ピクセル単位で描画を行うため、複雑なグラフィックスやアニメーションを作成するのに適しています。例えば、ゲームや高度なグラフの描画に利用されます。一方で、Canvasは描画された要素を構造化していないため、アクセシビリティSEOには不向きです。

  • SVGベクター形式で描画を行うため、拡大縮小しても画質が劣化しません。また、SVG要素はHTML要素として直接記述できるため、テキストベースの編集やスタイリングが容易です。SVGはアニメーションやグラフィックスを描画する際にも利用されます。SEOアクセシビリティにも配慮しやすいです。

どちらを選択するかは、以下のような点を考慮して決定できます:

  1. 描画内容の複雑さ: 複雑なグラフィックスやアニメーションを行う場合はCanvasが適しています。
  2. 拡大縮小の必要性: 描画物を拡大縮小したい場合はSVGが有利です。
  3. 構造化とスタイリング: HTML要素として構造化して扱いたい場合はSVGが使いやすいです。
  4. アクセシビリティSEO: テキストベースの要素として扱いたい、またはSEOアクセシビリティを重視する場合はSVGが適しています。

どちらを選んでも、ReactではCanvasSVGを簡単に扱うことができますので、プロジェクトの要件に合わせて適切なものを選択してください。

そんな複雑な図形は書かないので、svgでいいかな。

ということで最初のコード例を参考に以下の様に構成してみた。

  1. 線の情報のデータクラスを作る
  2. 線の情報をもとにsvgを描画するコンポーネントを作成
  3. 断面情報から線の情報を作る関数を作成
  4. 断面の計算をするタイミングで線の情報をコンポーネントに渡す

以上をもとに下記のようなコンポーネントを作って、作成してみたところ、無事に断面形状が表示された。

DrawSection.tsx

import React from "react";
import { SecFlatBar } from "@st-func/st-func-ts";
import { Unit } from "@st-func/st-func-ts";
export class LineData {
  x1: number;
  y1: number;
  x2: number;
  y2: number;
  constructor(x1: number, y1: number, x2: number, y2: number) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
  }
}
export interface DrawLinesProps {
  width: number;
  height: number;
  lines: LineData[];
}
export const DrawLines: React.FC<DrawLinesProps> = ({
  width,
  height,
  lines,
}) => {
  return (
    <svg width={width} height={height}>
      {lines.map((line) => {
        return (
          <line
            x1={line.x1}
            y1={line.y1}
            x2={line.x2}
            y2={line.y2}
            stroke="black"
          />
        );
      })}
    </svg>
  );
};

export function flatBarLines(secFlatBar: SecFlatBar): LineData[] {
  const lines: LineData[] = [];
  const t = Unit.output(secFlatBar.t, "mm");
  const b = Unit.output(secFlatBar.b, "mm");
  lines.push(new LineData(0, 0, t, 0));
  lines.push(new LineData(t, 0, t, b));
  lines.push(new LineData(t, b, 0, b));
  lines.push(new LineData(0, b, 0, 0));
  return lines;
}

とりあえず作った感なので、次回はもう少しコードをブラッシュアップしていきたい。