Changes
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..f46f57c
--- /dev/null
+++ b/index.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html lang="en-US">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1" />
+ <link rel="stylesheet" href="styles.css" />
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/preact/10.27.1/preact.min.js"
+ integrity="sha512-tJqZlYpxIwLhPlIXYqzFfeEzWsb0eBbDJbAJcnfIhFKkF1pQ6Q5SkB+gFvZi0jt+7BPY69WrWyxBu5PLn1BJpw=="
+ crossorigin="anonymous"
+ referrerpolicy="no-referrer"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/preact/10.27.1/hooks.umd.min.js" integrity="sha512-mODWC9TZn8iSfmbGnNgHErn0TQSFstq5vEBnkKJK7HPDgt9MpTECcYg50whbqW2yAHIYVnvpnW+owcEx/FnR3g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+ <script src="scripts/lib.js"></script>
+ <script src="scripts/tiles.js"></script>
+ <script type="module" defer src="scripts/main.js"></script>
+ <title>My test page</title>
+ </head>
+ <body>
+ <div id="Plan">
+ </div>
+ </body>
+</html>
diff --git a/scripts/lib.js b/scripts/lib.js
new file mode 100644
index 0000000..4e4850e
--- /dev/null
+++ b/scripts/lib.js
@@ -0,0 +1,10 @@
+const h = preact.h;
+const CELL_SIZE = 32;
+const GRID_SIZE_X = 21;
+const GRID_SIZE_Y = 21;
+
+const ASSET_SVG_PROPS = {"width": CELL_SIZE, "height": CELL_SIZE,
+ "version": "1.1"};
+const ASSET_WHITE_BG = h("rect", {"x": 0, "y": 0, "width": CELL_SIZE,
+ "height": CELL_SIZE, "fill": "white",
+ "stroke-width": 0 });
diff --git a/scripts/main.js b/scripts/main.js
new file mode 100644
index 0000000..7a8b9d4
--- /dev/null
+++ b/scripts/main.js
@@ -0,0 +1,93 @@
+function genGrid(x_count, y_count, cell_size)
+{
+ let lines = [];
+ for(let x = 0; x < x_count+1; x++)
+ {
+ lines.push(h("line", {
+ "x1": x * cell_size + 0.5, "x2": x * cell_size + 0.5, "y1": 0.5,
+ "y2": y_count * cell_size + 0.5,
+ }));
+ }
+ for(let y = 0; y < y_count+1; y++)
+ {
+ lines.push(h("line", {
+ "y1": y * cell_size + 0.5, "y2": y * cell_size + 0.5, "x1": 0.5,
+ "x2": x_count * cell_size + 0.5,
+ }));
+ }
+ return h("g", {"stroke": "black", "stroke-width": 1}, lines);
+}
+
+function AssetView({asset_index})
+{
+ return h("div", {"class": "Asset", "draggable": true,
+ "id": `Asset${asset_index}`},
+ h("svg", ASSET_SVG_PROPS, ASSET_WHITE_BG,
+ TILES[asset_index].inner_svg));
+}
+
+function AssetsView()
+{
+ let views = TILES.slice(1).map((t, i) =>
+ h(AssetView, {asset_index: i + 1}));
+ return h("div", {}, views);
+}
+
+function PlanGridView({content})
+{
+ const [mouse_coord, setMouseCoord] = preactHooks.useState(null);
+
+ function onDragOver(e)
+ {
+ e.preventDefault();
+ let b = e.target.getBoundingClientRect();
+ let x = e.clientX - b.left;
+ let y = e.clientY - b.top;
+ setMouseCoord([x, y]);
+ }
+
+ function onDragLeave(e)
+ {
+ setMouseCoord(null);
+ }
+
+ let hilight_box = null;
+ if(mouse_coord !== null)
+ {
+ let cell_x = Math.floor(mouse_coord[0] / (CELL_SIZE + 1));
+ let cell_y = Math.floor(mouse_coord[1] / (CELL_SIZE + 1));
+ hilight_box = h("rect", {
+ "x": cell_x * (CELL_SIZE + 1) + 0.5, "y": cell_y * (CELL_SIZE + 1) + 0.5,
+ "width": CELL_SIZE + 1, "height": CELL_SIZE + 1,
+ "stroke": "#2ed573", "stroke-width": 3, "fill": "transparent"});
+ }
+ let grid_content = content.map((idx, i) => {
+ let cell_x = i % GRID_SIZE_X;
+ let cell_y = Math.floor(i / GRID_SIZE_Y);
+ return h("g", {"transform": `translate(${cell_x * (CELL_SIZE + 1)} \
+${cell_y * (CELL_SIZE + 1)})`}, TILES[idx]);
+ });
+
+ return h("svg", {"width": GRID_SIZE_X * (CELL_SIZE + 1) + 1,
+ "height": GRID_SIZE_Y * (CELL_SIZE + 1) + 1,
+ "version": "1.1", "ondragover": onDragOver,
+ "id": "Grid", "ondragleave": onDragLeave},
+ grid_content, genGrid(GRID_SIZE_X, GRID_SIZE_Y, CELL_SIZE + 1),
+ hilight_box);
+}
+
+function PlanView(props)
+{
+ return h("div", {},
+ h("p", {}, "The plan:"),
+ h(PlanGridView, {}));
+}
+
+function App(props)
+{
+ return h("div", {},
+ h(AssetsView, {}),
+ h(PlanView, {}));
+}
+
+preact.render(h(App, {}), document.getElementById("Plan"));
diff --git a/scripts/tiles.js b/scripts/tiles.js
new file mode 100644
index 0000000..f35887a
--- /dev/null
+++ b/scripts/tiles.js
@@ -0,0 +1,94 @@
+const TILES = [
+ null,
+ {
+ id: "blank",
+ name: "black",
+ inner_svg: null,
+ }, {
+ id: "teleporter",
+ name: "teleporter",
+ inner_svg: h("circle", {
+ "cx": CELL_SIZE * 0.5, "cy": CELL_SIZE * 0.5,
+ "r": (CELL_SIZE - 10) * 0.5, "stroke": "black",
+ "fill": "transparent", "stroke-width": 6,
+ }),
+ }, {
+ id: "fleet_command",
+ name: "fleet command",
+ inner_svg: h("polyline", {
+ "points": `3, ${CELL_SIZE} \
+3, ${CELL_SIZE - 15} \
+13, ${CELL_SIZE - 25} \
+13, ${CELL_SIZE - 5} \
+${CELL_SIZE - 13}, ${CELL_SIZE - 5} \
+${CELL_SIZE - 13}, ${CELL_SIZE - 25} \
+${CELL_SIZE - 3}, ${CELL_SIZE - 15} \
+${CELL_SIZE - 3}, ${CELL_SIZE}`,
+ "fill": "black", "stroke-width": 0,
+ }),
+ }, {
+ id: "scanner",
+ name: "scanner",
+ inner_svg:
+ h(preact.Fragment, {},
+ h("circle", {"cx": CELL_SIZE * 0.5, "cy": CELL_SIZE * 0.5,
+ "r": CELL_SIZE * 0.5 - 2, "fill": "transparent",
+ "stroke": "black", "stroke-width": 2}),
+ h("circle", {"cx": CELL_SIZE * 0.5, "cy": CELL_SIZE * 0.5,
+ "r": CELL_SIZE * 0.25, "fill": "transparent",
+ "stroke": "black", "stroke-width": 2}),
+ h("line", {"x1": 2, "y1": CELL_SIZE * 0.5,
+ "x2": CELL_SIZE - 2, "y2": CELL_SIZE * 0.5,
+ "stroke": "black", "stroke-width": 2}),
+ h("line", {"y1": 2, "x1": CELL_SIZE * 0.5,
+ "y2": CELL_SIZE - 2, "x2": CELL_SIZE * 0.5,
+ "stroke": "black", "stroke-width": 2}),
+ h("line", {"x1": CELL_SIZE * 0.5, "y1": CELL_SIZE * 0.5,
+ "x2": CELL_SIZE * 0.5 + (CELL_SIZE * 0.5 - 2) * Math.cos(Math.PI / 6),
+ "y2": CELL_SIZE * 0.5 - (CELL_SIZE * 0.5 - 2) * Math.sin(Math.PI / 6),
+ "stroke": "black", "stroke-width": 2}),
+ h("circle", {"cx": 22, "cy": 6, "r": 2, "fill": "black",
+ "stroke-width": 0}),
+ h("circle", {"cx": 10, "cy": 22, "r": 4, "fill": "black",
+ "stroke-width": 0}),
+
+ ),
+ }, {
+ id: "extractor",
+ name: "stellar extractor",
+ inner_svg:
+ h(preact.Fragment, {},
+ h("circle", {"cx": CELL_SIZE * 0.5, "cy": 21,
+ "r": 10, "fill": "black", "stroke-width": 0}),
+ h("polygon", {"points": `${CELL_SIZE * 0.5 - 4}, 20 \
+${CELL_SIZE * 0.5 - 4}, 10 \
+${CELL_SIZE * 0.5 - 8}, 10 \
+${CELL_SIZE * 0.5}, 3 \
+${CELL_SIZE * 0.5 + 8}, 10 \
+${CELL_SIZE * 0.5 + 4}, 10 \
+${CELL_SIZE * 0.5 + 4}, 20`,
+ "stroke": "black", "stroke-width": 2,
+ "fill": "white"}),
+ ),
+ }, {
+ id: "storage",
+ name: "storage",
+ inner_svg:
+ h(preact.Fragment, {},
+ h("rect", {"x": 4, "y": 4, "width": CELL_SIZE - 8,
+ "height": CELL_SIZE - 8, "rx": 2, "ry": 2,
+ "fill": "black", "stroke-width": 0}),
+ h("line", {"x1": 4, "y1": 14, "x2": CELL_SIZE - 4, "y2": 14,
+ "stroke": "white", "stroke-width": 2}),
+ h("rect", {"x": CELL_SIZE * 0.5 - 4, "y": 10, "width": 8, "height": 8,
+ "fill": "white", "stroke-width": 0}),
+ ),
+ }, {
+ id: "plant",
+ name: "double cultivation chamber",
+ inner_svg: h("path", {"d": "m3.02 17.7a13 13 0 0013 13 13 13 0 00-13-13m13 13a13 13 0 0013-13 13 13 0 00-13 13m8.66-27.4v7.21a8.66 8.66 0 01-8.66 8.66 8.66 8.66 0 01-8.66-8.66v-7.21c1.07 0 2.12.173 3.12.563.793.332 1.5.821 2.09 1.44l3.43-3.43 3.43 3.43c.591-.622 1.3-1.11 2.09-1.44.996-.388 2.05-.563 3.12-.563z",
+ "fill": "black", "stroke-width": 0}),
+ }
+];
+
+const TILE_MAP = Object.fromEntries(TILES.slice(1).map(t => [t.id, t]));
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..aca9b9b
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,23 @@
+body
+{
+ background-color: #a4b0be;
+}
+
+*
+{
+ box-sizing: border-box;
+}
+
+.Asset
+{
+ display: inline-block;
+ border: solid black 1px;
+ width: 34px;
+ height: 34px;
+}
+
+/* Prevent SVG elements from interferring with drag n drop. */
+#Grid *
+{
+ pointer-events: none;
+}