幾何の小物
多角形と矩形を扱う五つの関数である。座標は mm、面積は ㎡ で返る。
import { envelopeGaps, pointInPolygon, polyBounds, polygonAreaM2, rectToPoly } from "@kensnzk/koyu";
ここに判定は一つも無い。「建物が敷地からはみ出しているか」も「外皮に穴があるか」も、ここには答えが無い — それは validate の site.escape と envelope.gap が言う。ここにあるのは、その判定が読む数と形だけである。
polygonAreaM2
function polygonAreaM2(points: Pt[]): number
シューレース公式による多角形の面積を ㎡ で返す。巻き方向を問わない — 符号付き面積の絶対値なので、時計回りでも反時計回りでも同じ値になる。
pointInPolygon
function pointInPolygon(p: Pt, poly: Pt[], eps?: number): boolean
点が多角形の内側にあるか。境界の上は内側扱いである。eps は「辺の上にあるとみなす幅」で、既定は 1mm。
polyBounds
function polyBounds(poly: Pt[]): Rect
頂点列の外接矩形。
rectToPoly
function rectToPoly(r: Rect): Pt[]
矩形を頂点列へ。反時計回りで、(x1,y1) → (x2,y1) → (x2,y2) → (x1,y2) の順である。
この巻き方向は約束である。辺の向きから方角を読む導出 (+x へ進む辺が南、+y が東、−x が北、−y が西) がこの順に依存している。
import { pointInPolygon, polyBounds, polygonAreaM2, rectToPoly } from "@kensnzk/koyu";
const poly = [{ x: 0, y: 0 }, { x: 10000, y: 0 }, { x: 10000, y: 10000 }, { x: 0, y: 10000 }];
console.log(polygonAreaM2(poly), pointInPolygon({ x: 5000, y: 5000 }, poly),
pointInPolygon({ x: 12000, y: 0 }, poly), pointInPolygon({ x: 0, y: 5000 }, poly), polyBounds(poly));
console.log(polygonAreaM2([...poly].reverse()));
console.log(rectToPoly({ x1: 0, y1: 0, x2: 3600, y2: 4000 }));
100 true false true { x1: 0, x2: 10000, y1: 0, y2: 10000 }
100
[
{ x: 0, y: 0 },
{ x: 3600, y: 0 },
{ x: 3600, y: 4000 },
{ x: 0, y: 4000 }
]
三つ目の pointInPolygon は辺のちょうど上の点で、真が返っている。巻き方向を逆にしても面積は変わらない。
envelopeGaps
function envelopeGaps(model: Model, s: Space): Segment[]
空間の外周のうち、何にも面していない区間を返す。他の空間とも、宣言された外部境界とも向かい合っていない縁 — 外皮の穴である。
既定境界は領域を持たない空間との間には導かれないので、外部への境界の書き忘れは黙って壁の不在になる。これを言葉にするための導出である。
長さが SPAN_EPS (1mm) 以下の区間は落とされる。領域を持たない空間、レベルの決まらない空間では空配列が返る。
import { envelopeGaps, parse } from "@kensnzk/koyu";
const g = parse(`grid X 0 3600 7200
grid Y 0 4000
level L1 0 h:2400 slab:150
space /L1/a room X1..X2 Y1..Y2
space /L1/b room X2..X3 Y1..Y2
space /out exterior
boundary /L1/a /out edge:W`);
for (const s of ["/L1/a", "/L1/b"]) {
console.log(s, envelopeGaps(g, g.spaces.get(s)!).map((x) => `${x.edgeOfA} ${x.x1},${x.y1}→${x.x2},${x.y2}`));
}
/L1/a [ 'S 0,0→3600,0', 'N 0,4000→3600,4000' ]
/L1/b [ 'S 3600,0→7200,0', 'E 7200,0→7200,4000', 'N 3600,4000→7200,4000' ]
/L1/a の W 辺は boundary /L1/a /out edge:W が覆い、E 辺は /L1/b が向かい合っているので、残った S と N が穴として出ている。
返るのは Segment — 端点と、水平かどうかと、a 側から見た辺の方角である。長さは持たないので、要るなら端点から測る。
幾何の許容値
「どれだけ違えば別のものか」を決める数は一箇所に集めてある。TOLERANCES を見る。点が辺の上にあるとみなす幅の既定 (POINT_EPS = 1mm) も、外皮の穴の長さの下限 (SPAN_EPS = 1mm) もそこにある。