▍AI Dev Lab
RoomWeave

家具の自動配置をつくる:探索と通路判定の設計

家具配置シミュレーションの開発記録。複数の配置候補を探索し、10cm格子と指定通路幅で入口からの到達を確認する設計、探索の限界、操作画面を固めないWorkerの使い方を解説します。

·decision改善·stage公開中

家具の寸法は分かっていても、部屋に置いたときの通り道は想像しにくい。RoomWeaveは、手で家具を全部並べる前に、条件を満たす配置を複数提示する家具配置シミュレーションです。好きな配置を選び、動かして、問題がないかを確かめられます。

見た目は暮らしの設計ノートをイメージしました。白い紙、セージ色の家具、焼き物のような赤茶色、実寸の目盛り。派手な3D表現ではなく、入力と図を隣に置き、比較のための情報を読み取れる密度にしています。見出しには落ち着いた欧文セリフと日本語の組み合わせを使い、操作部は読みやすさを優先しました。

#手動配置だけではない入口

無料の配置ツールは既にあります。スマホ対応や画像保存だけでは選ぶ理由になりません。今回は「自分なら思いつかなかった配置」を先に見せ、その後で調整する順序にしました。

利用者が入力するのは部屋の幅と奥行、入口の壁と位置、家具の外寸、比較したい通路幅です。家具ごとに90度回転を許可するかも選べます。畳数から実寸を推定しないのは、同じ6畳でも部屋の寸法が違うからです。初期値は使い方を試すサンプルで、標準寸法ではありません。

#配置候補を残す探索

家具を長方形とし、大きいものから配置します。各段階では壁沿い、一定間隔の位置、既に置いた家具の辺や通路幅だけ離した位置を候補にします。回転可なら向きを変えた候補も作ります。

まず部屋の外、家具同士の重なり、ドア前の確保範囲との重なりを除外します。残ったものから壁との距離と家具の分散を目安に候補を絞り、次の家具を置きます。これはビームサーチです。全てを組み合わせると候補数が指数的に増えるため、探索幅に上限を持たせています。

単に上位だけ残すと、大きな家具の位置が同じ案ばかりになる問題がありました。そこで最も大きな家具の位置と向きで候補を分け、同じ区分に偏りすぎないよう制限しています。最後にも小さな座標違いを除いて、異なる案を最大3つ残します。3つ見つからない場合は数を水増ししません。

#残したい位置を探索条件にする

全部の家具が毎回動くと、動かせない収納や、気に入ったベッドの位置を残して検討できません。そこで図上の家具を選び、位置と向きを固定して残りだけ再探索できるようにしました。固定座標は部屋の左上を原点にしたcm値で、数値でも変更できます。ここでいう固定は計算上の条件であり、転倒防止の壁固定施工とは別です。

固定した家具を先に配置済み集合へ入れ、残りだけ大きい順に探索します。固定同士の重なり、部屋外、ドア前への侵入は最初に検査します。候補の多様性は「最も大きな可動家具」で判定し、固定家具の位置が同じだからといって探索幅を狭めないようにしました。全家具が固定されている場合は、その1案をそのまま評価します。

図上の移動と回転を止めるだけでは不十分です。評価関数でも固定条件との一致を確認するため、座標を変えたJSONは復元時に拒否します。固定条件は任意項目として追加し、固定情報のない既存のバージョン1データも引き続き読み込めます。

#重ならないだけでは通れない

長方形の重なり判定だけなら、入口から家具へ行けない配置も通ってしまいます。そこで10cm間隔の格子上で、入口からの到達を別に確認します。

指定通路幅の正方形を占有範囲の目安とし、その半分だけ家具や壁から距離を取った位置を通行候補にします。さらに格子の半幅を余白として追加します。中心点だけが通れればよいのではなく、隣接セル間の移動でも余裕を残すためです。これは円形の人体を精密に再現した計算ではなく、保守的な平面モデルです。

入口付近の通れる点を起点に、上下左右へ幅優先探索を行います。到達点の集合を図の緑色で表示し、各家具の周囲へ近づけるかを確認します。表示する面積は到達できる中心位置の格子面積で、家具を除いた床面積とは異なります。似た名前の指標を混同しないことが大切です。

#使用面と椅子・引き出しの余白

家具の背面へしか近づけない配置や、机の前に椅子を引けない配置を避けるため、使用面と余白を追加しました。家具1点につき1面を選び、実測した必要寸法を入力します。使用面の辺全体に長方形の余白を作り、部屋外・他家具・ドア前との重なりを除外します。余白同士も共有しない保守的な条件です。図では青い太線が使用面、青い領域が使用余白です。

寸法交換だけでは0度と180度の違いを表せません。そこで時計回りの4方向を持ち、使用面、手動回転、固定条件、探索、SVGを一貫して回します。余白指定のない自動探索は従来の0/90度の候補を維持します。保存形式はバージョン2にし、旧バージョン1は向きを0/90度として読み込み、未指定の使用面や余白を勝手に追加しません。

余白が空いていても、入口から背面へしか行けなければ不成立です。指定面に正対する到達点を確認し、使用面への経路が塞がった候補は探索の途中でも除外します。後で家具を追加しても塞がった経路は開かないためです。

無印良品のテーブル選び案内は、着座、椅子を引く動作、後方の通行を分けて説明しています。必要性の根拠として参照しましたが、掲載寸法を全家具へ一律に適用していません。画面の60・80cmは操作用の入力例で、安全基準や推奨値ではありません。

#開閉範囲と使い勝手の限界

従来方式と旧保存データは入口幅を一辺とする正方形を維持します。新しく内開き・外開き・引戸/扉なしを選ぶと、入口前の余白と扉の開閉範囲を分けます。入口余白は入口幅×(指定通路幅+10cm)。内開きでは入口幅を扉長さとする90度の扇形も確保します。四方の壁、入口の始点・終点の蝶番を同じ座標変換で扱います。

扇形の外接正方形で判定すると、円弧の外の家具まで拒否します。そこで家具の矩形を蝶番を原点とした局所座標へ移し、四分円との最短距離を検査します。家具本体、使用余白、柱、固定家具、探索候補、復元後の評価に同じ関数を使います。入口余白は引戸や外開きでも消しません。

扇形は家具を置かない範囲ですが、人が通るときの障害物とは別です。通路は90度全開時の扉を厚さ2cmの矩形として避けます。開閉中の移動を検証するものではなく、扉長さと厚さも近似です。外開きの室外障害物、引戸の収納先、枠・取手・実測の厚さは未判定。図とSVGには扇形、蝶番、全開時の扉を残し、保存はバージョン4にします。旧版へ開閉設定だけ混ぜたJSONは拒否し、旧1〜3は未指定条件を補わず読み込みます。

指定面へ到達し、指定した長方形が空くことと、家具が快適に使えることは別です。通路計算は引き出しを閉じ、余白に椅子などが置かれていない状態です。開いたまま・着席中・ドア開閉中の通行、テーブルの四方など複数面の使用は未判定です。窓、家具の高さ、コンセントも含めていません。利用者に見えない制約を勝手に推測して「最適」と呼ぶことは避けました。

#柱は家具の到達数に含めない

柱や動かせない設備を、家具と別の固定障害物として最大8点入力できるようにしました。部屋の左上を原点に、X・Yと実測した幅・奥行を指定します。家具本体だけでなく、使用余白の矩形も障害物を避けます。通れる範囲は障害物にも指定通路幅と格子の余裕を加えて判定します。柱へ到達する必要はなく、家具の点数・到達数を水増ししません。

候補位置には障害物の辺も加え、位置固定した家具が柱に重なる場合は探索前に知らせます。部屋外、ドア前、障害物同士の重なりは入力時に拒否します。障害物の編集で古い配置案は消え、新しい条件で再探索します。図では灰色の斜線として表し、SVGにも名前・位置・寸法を残します。

障害物入りの保存はバージョン3です。バージョン1・2も引き続き読み込め、存在しなかった柱を勝手に追加しません。復元時も衝突・経路を検査します。入力した矩形だけを扱う平面モデルで、曲線・L字型や立体の高さを推定するものではありません。

候補が出ない場合も、置けない証明ではありません。探索位置と幅を制限しているため、探索外には解が残っている可能性があります。家具が部屋の幅・奥行を超える場合や合計面積が床を超える場合の明らかな不一致とは、メッセージを分けています。

#操作画面を固めない

探索はWeb Workerで動かします。計算中でも入力を変更でき、そのときは古いWorkerを終了して結果を破棄します。古い条件の計算が遅れて戻り、新しい条件の図に混ざるのを防ぐ設計です。ページを離れるときもWorkerを終了します。

調整はSVGとPointer Eventsを使います。画面上のポインタ座標をSVGの座標系へ逆変換すると、画面幅が変わっても実寸に対応した移動ができます。Pointer Captureで家具の外に指が出てもドラッグを続けられ、キャンセルやCapture解除で移動状態を終了します。キーボードでは5cmずつ動かせます。

回転や移動後は同じ判定関数で再評価し、警告のある配置では保存を無効にします。条件入力を変えると過去の配置を消して再探索を促します。寸法だけ変えて古い位置を残すと、違う条件の結果を正しい案と誤認するためです。

#保存にも同じ検証を使う

ブラウザには明示的な保存操作で1件保存します。保存できない環境ではJSONへの書き出しを案内し、失敗を黙って成功扱いしません。JSONには版番号、部屋、家具、配置を含め、読み込み時に型・値域・重複ID・実寸との一致・配置の判定を確認します。

SVG画像にも家具名と寸法を含めていますが、画像を紙に印刷したときの倍率は保証しません。これは寸法表示を読む検討図であり、そのまま実寸の型紙になる出力ではありません。

スマホで見返したり家族へ送る用途にはPNG保存を追加しました。現在のSVGをCanvasへ描画するため、L字の欠け、柱、ドア、使用余白、家具名・寸法と表示中の通れる範囲を同じ図として残します。外開き扉を含むviewBoxの縦横比を維持し、長辺2400px・最大576万画素に制限します。背景は透明にせず図と同じ生成りにしています。

生成は15秒で打ち切り、失敗を成功扱いしません。配置や条件・表示を変えた場合とページ離脱では中断し、生成元のURLを解放します。PNG・SVGの画像から配置を編集し直せるわけではないので、再開には端末保存かJSONが必要です。ダウンロード開始は端末への保存完了を保証せず、保存先・共有方法はブラウザに依存します。

#出力の検証と今後

#別案を残す名前付き保存

従来の1件保存とは別のキーで、名前付きの配置を最大10件保存します。名前・ID・更新時刻と配置データを検証し、重複や未知の形式を黙って読み飛ばしません。以前の1件は明示的に取り込み、原文を変更しません。個別のJSONは従来と同じ編集再開用の形式です。

開く・上書き・削除では影響を確認してから実行します。書き込み直前に原文を比較して別タブの変更を検出し、再読み込みを促します。ただしlocalStorageの比較と書き込みは不可分な操作ではなく、完全な同時更新対策ではありません。端末間の同期もありません。

破損した一覧は空の一覧へ自動変換せず、原文バックアップと名前付き一覧だけの初期化を提供します。容量不足や保存制限で書き込みに失敗した場合は成功と表示せず、JSON保存を案内します。ブラウザのデータ削除で保存は失われるため、大事な配置はJSONも手元に残す必要があります。

検証では表示だけでなく、生成した各配置の外周、家具同士の分離、ドア前、表示された到達点の占有範囲を調べます。入口を上下左右に変えた条件、回転不可、家具8点、明らかな寸法超過、保存データの破損も対象です。ブラウザ側では実際のWorkerから案を作り、件数表示、キーボード調整、保存と復元、画像書き出し、スマホ幅での表示を確認します。

4方向の余白を独立した座標計算で検算し、余白の部屋外・衝突・指定面への到達不可、180度の固定、旧保存の互換も調べます。柱への衝突と使用余白の侵入、通路を分断する障害物、実際の探索結果と保存復元も検査します。立体化は未対応です。

#L字型の部屋を床形状として扱う

外周の幅・奥行から1つの角を矩形で欠くL字型を追加しました。左上・右上・右下・左下から選び、欠けの実寸を入力します。欠けを柱として数えるのではなく、家具・使用余白・固定位置・通路の占有範囲が入れない部屋の外として扱います。床面積の検査も外周の長方形から欠けを差し引きます。家具が細い腕に収まっても、指定幅で入口から到達できなければ警告になります。

入口は残っている外側4壁と、欠けに沿う内側の横壁・縦壁から選べます。内側入口の位置はその短い壁の左端・上端から測ります。壁の原点・壁沿いの方向・室内方向を一つの座標系にまとめ、入口余白、蝶番の四分円、全開時の扉、経路の起点、画面とSVGで共用しています。入口の確保範囲や内開きの扇形が欠けに重なる入力は拒否し、内側入口では全開位置や余白が室外に出る入力も拒否します。外開きの室外障害物は判定しません。複数の欠け、自由な多角形には対応しません。

図の外周は6頂点の多角形になり、欠けの寸法と入口をSVGに残します。内側入口入りは保存バージョン6、外側入口のL字はバージョン5、旧1〜4の長方形保存は引き続き読み込めます。読み込み後も配置と経路を同じ条件で検査します。長方形へ切り替えると内側壁がなくなるため、入口を下の壁・左端から40cmへ変更したことを画面で伝え、旧配置を破棄します。

[ ./next_action ]

読んだら、 RoomWeave を実際に動かす。

この開発ログは RoomWeave をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
ShadowTwin

立体パズルの別解を作る:同じ影と最少の積み木

ShadowTwinの開発記録。3方向の図から別々の最少配置を2つ探す仕組み、5問の独立全探索、スマホの横並び比較、保存・再編集と入力検査を解説します。実照明の計算や能力診断ではありません。

read log →
SlitCard

スリットアニメーションを作る:4コマの合成と透明原稿の設計

SlitCardの開発記録。4コマの帯を交互に合成し、すき間と黒帯で別のコマを見せる仕組みを解説。同縮尺の台紙・透明シートSVG、50mm確認線、手動プレビュー・初期非表示、再編集と実出力検査を整理します。

read log →
WindowLight

日当たりシミュレーションを作る:窓の光を床へ投影する幾何モデル

WindowLightの開発記録。太陽位置近似から窓の4隅を床へ投影し、凸多角形を部屋で切り取る設計を解説。国立天文台の16時刻との限定照合、大気差・低高度・実際の採光との違い、SVGと設定の再編集を整理します。

read log →
GridLights

電源構成の比較を作る:割合と実量を100個の電球で読み解く

電源構成比較GridLightsの開発記録。12か国の年次発電データを最大剰余法で100個の電球に変換し、割合とTWhを比較する設計を解説。欠損を0補完しない7分類、予想と実値、出典付きSVGと実データ検証を整理します。

read log →
SoundLoom

音のお絵かきを作る:実WAVから絵を測り直す設計

音のお絵かきの開発記録。描画を正弦波のPCMとWAVへ変換し、保存音声の周波数をGoertzel法で測って比較する仕組みを解説。音量・測定窓・量子化の限界と、編集・離脱時の古い結果を抑止する検証を整理します。

read log →
MazeLoom

宝探し迷路を作る:寄り道まで含めた最短ルートの探索

宝探し迷路の開発記録。通路を木として生成し、現在地と3つの宝の取得状態を組み合わせて最短ルートを探す仕組みを解説。問題コードの版固定、画面と解答付きPDFの一致、手数と印刷の検証を整理します。

read log →