ST_FUNC開発日記

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

断面形状の描画 その2

前回作った断面形状描画部分を拡張しやすいように修正していく。

LineData

LineDataに関しては、x1,y1,x2,y2といちいち書いていくといまいちなので、点データをもたせて、それを結ぶ仕組みとした。

また、多角形のデータを1本1本持たせるのは面倒なので、点の配列をもたせるようにした。

描画時に1本ずつに分離するpointPairsメソッドを作成。

...という演算子?があるようだ。あまり使い慣れないが、うまく使えば便利?

Point

LineDataの点情報。実際の座標情報とスクリーン上の座標情報を両方持たせられるようにした。

ビューポート的な感じ?

DrawingData

linesだけではなくて、width,height,scaleみたいな描画に必要な情報を集約することにした。

オフセットできるように原点データも持たせる。

line情報に加えて、lineで使われているpoint情報もリストとして取得するようにした。(addLineで被りがないようにリストに入れる)

これは、スクリーン座標を作る処理などの効率を上げるためである。

setScreenCoordinateでDrawingDataの情報をもとに、Pointのスクリーン座標を設定する。

ViewMatrixみたいな座標変換行列でやるべきなきもするが、そこまで真面目ではないので簡易的なもの。

setAutoScaleはpoint情報をもとに、いい感じに原点とスケールを設定するもの。この辺もPointを別配列にしたので処理がやりやすい。

ここでも...出てきた。...に投げるときは...を使うといいのね。

おわり

これでだいぶスッキリしたかな?

DrawSection.tsxはこんな感じになった。

import React from "react";
import { SecFlatBar } from "@st-func/st-func-ts";

class Point {
  x: number;
  y: number;
  x_screen: number;
  y_screen: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
    this.x_screen = x;
    this.y_screen = y;
  }
}

class LineData {
  points: Point[];
  constructor(...points: Point[]) {
    this.points = points;
  }
  pointPairs(): [Point, Point][] {
    let result: [Point, Point][] = [];
    for (let i = 0; i < this.points.length - 1; i++) {
      result.push([this.points[i], this.points[i + 1]]);
    }
    return result;
  }
}

export class DrawingData {
  points: Point[] = [];
  lines: LineData[] = [];
  scale: number = 1;
  origin: Point = new Point(0, 0);
  width: number = 500;
  height: number = 500;
  addLine(line: LineData): void {
    this.lines.push(line);
    for (let point of line.points) {
      if (!this.points.includes(point)) {
        this.points.push(point);
      }
    }
  }
  setScreenCoordinate(): void {
    for (let point of this.points) {
      point.x_screen =
        this.origin.x_screen + (point.x - this.origin.x) * this.scale;
      point.y_screen =
        this.origin.y_screen + (point.y - this.origin.y) * this.scale;
    }
  }
  setAutoScale(): void {
    if (this.points.length === 0) {
      return;
    }
    let max_x = Math.max(...this.points.map((point) => point.x));
    let min_x = Math.min(...this.points.map((point) => point.x));
    let max_y = Math.max(...this.points.map((point) => point.y));
    let min_y = Math.min(...this.points.map((point) => point.y));
    let dx = max_x - min_x;
    let dy = max_y - min_y;
    this.origin.x = dx / 2;
    this.origin.y = dy / 2;
    this.origin.x_screen = this.width / 2;
    this.origin.y_screen = this.height / 2;
    if (dx === 0 && dy === 0) {
      this.scale = 1;
    } else {
      let scale_x = Number.MAX_SAFE_INTEGER;
      if (dx !== 0) {
        scale_x = (this.width * 0.95) / dx;
      }
      let scale_y = Number.MAX_SAFE_INTEGER;
      if (dy !== 0) {
        scale_y = (this.height * 0.95) / dy;
      }
      this.scale = Math.min(scale_x, scale_y);
    }
  }
}

interface DrawingProps {
  drawingData: DrawingData;
}

export const Drawing: React.FC<DrawingProps> = ({ drawingData }) => {
  drawingData.setScreenCoordinate();
  return (
    <svg width={drawingData.width} height={drawingData.height}>
      {drawingData.lines.map((line) => {
        return line.pointPairs().map(([point1, point2]) => {
          return (
            <line
              x1={point1.x_screen}
              y1={point1.y_screen}
              x2={point2.x_screen}
              y2={point2.y_screen}
              stroke="black"
            />
          );
        });
      })}
    </svg>
  );
};

export function flatBarDrawing(secFlatBar: SecFlatBar): DrawingData {
  const points: Point[] = [];
  points.push(new Point(0, 0));
  points.push(new Point(secFlatBar.t, 0));
  points.push(new Point(secFlatBar.t, secFlatBar.b));
  points.push(new Point(0, secFlatBar.b));
  points.push(points[0]);
  const result: DrawingData = new DrawingData();
  result.addLine(new LineData(...points));
  return result;
}

断面形状の描画機能でスケール自動調整機能などを追加 · st-func/st-func-web@608cc49 · GitHub