commit c47ec0c9322dcf7b565b3fb772f1249e68ed1487
parent 9657828f4d9615d3571384b4f9a52b454eb3ff72
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Wed, 9 Oct 2024 10:48:04 -0700
Implement furniture styles
This is a new feature in the backend to allow for the same type to
have multiple "styles", like round tables.
Diffstat:
11 files changed, 318 insertions(+), 103 deletions(-)
diff --git a/files/floorplans/floorplan/backend.js b/files/floorplans/floorplan/backend.js
@@ -532,7 +532,8 @@ export class FloorplanBackend {
addFurniture(params, id) {
params = params ?? {}
- let f = id ? this.reqObj(id) : {}
+ //let f = id ? this.reqObj(id) : {}
+ let f = {}
if (params.width != undefined) {
f.width = Math.round(params.width)
@@ -559,6 +560,13 @@ export class FloorplanBackend {
f.type = params.type
}
+ if (params.style != undefined) {
+ if (typeof params.style !== "string") {
+ throw new Error(params.style + ": Invalid style")
+ }
+ f.style = params.style
+ }
+
if (f.width == null || f.depth == null || f.type == null) {
throw new Error("Missing required parameters")
}
diff --git a/files/floorplans/floorplan/editor.js b/files/floorplans/floorplan/editor.js
@@ -706,25 +706,29 @@ export class FloorplanEditor {
for (let mid in maps) {
if (maps[mid].furniture_id == id) {
let m = editor.draw.findOneMax(byId(mid))
- if (m != null) {
- m.size(value.width, value.depth)
- m.findOne("title").words(furniture_name(value))
+ if (m == null) {
+ ops.add.furniture_maps(id, editor.backend.cache[id])
+ m = editor.draw.findOneMax(byId(mid))
}
- m.load(`/furniture/${value.type}.svg`)
+ m.size(value.width, value.depth)
+ let t = m.findOne("title")
+ if (t == null) {
+ t = m.element("title")
+ }
+ t.words(furniture_name(value))
+ m.load(furnitureImage(value))
}
}
},
furniture_maps: function(id, value) {
+ let f = editor.backend.reqObj(value.furniture_id)
let fm = editor.draw.findOneMax(byId(id))
if (!fm) {
- let f = editor.backend.reqObj(value.furniture_id)
- fm = editor.layoutG().image(`/furniture/${f.type}.svg`)
+ fm = editor.layoutG().image(furnitureImage(f))
.size(f.width, f.depth)
.attr({ id, preserveAspectRatio: "none" })
- fm.element("title").words(furniture_name(f))
fm.on("error", function() {
if (this.attr("href") === "/furniture/any.svg") {
- etc.error("Unable to load furniture assets")
throw new Error("Unable to load furniture assets")
}
this.load("/furniture/any.svg")
@@ -912,3 +916,7 @@ function furniture_name(f) {
function swingID(id) {
return id + "_swing"
}
+
+function furnitureImage(f) {
+ return `/furniture/${f.type}/${f.style ?? "default"}.svg`
+}
diff --git a/files/floorplans/floorplan/main.js b/files/floorplans/floorplan/main.js
@@ -881,6 +881,13 @@ function furnitureMenuX(editor, pointOrID) {
return i
}
}
+ const styles = function(type) {
+ let styles = ['default']
+ if (editor.furniture_types[type].styles == null) {
+ return styles
+ }
+ return styles.concat(editor.furniture_types[type].styles)
+ }
editor.finishAction()
let p
@@ -923,6 +930,7 @@ function furnitureMenuX(editor, pointOrID) {
let items = [
menuItem("name", "Name", { attributes: { value: params.name ?? "" } }),
menuItem("type", "Type", { break: false, enum: editor.furniture_types, attributes: { value: params.type, required: true } }),
+ menuItem("style", "Style"),
menuItem("variety", "Variety", { enum: editor.furniture_types[params.type].varieties, attributes: { value: editor.varietyFrom(params) } }),
menuItem("width", "Width", { attributes: { value: userLength(editor, params.width), required: true } }),
menuItem("depth", "Depth", { attributes: { value: userLength(editor, params.depth), required: true } }),
@@ -964,26 +972,44 @@ function furnitureMenuX(editor, pointOrID) {
fromVariety(items[keys.type].input.value, ev.target.value)
})
}
+ const newStyle = function() {
+ let typeStyles = styles(params.type)
+ if (typeStyles.length === 1) {
+ items[keys.style].container.classList.add("hidden")
+ } else {
+ let s = menuItem("style", "Style", { enum: typeStyles })
+ items[keys.style].container.replaceWith(makeItem(s))
+ items[keys.style] = s
+ if (params.style != null) {
+ items[keys.style].input.value = params.style
+ }
+ }
+ }
let menu = makeMenu(items)
items[keys.type].input.value = params.type
newVariety(true)
+ newStyle(params.type)
items[keys.type].input.addEventListener("change", function(ev) {
newVariety()
})
menu.addEventListener("change", function(ev) {
handled(ev)
try {
- console.debug("furnitureMenu.change(event)", event.target.name, event.target.value)
- if (event.target.name === "width" || event.target.name === "depth") {
- let u = unitInput(editor, event.target)
+ console.debug("furnitureMenu.change(ev)", ev.target.name, ev.target.value)
+ if (ev.target.name === "width" || ev.target.name === "depth") {
+ let u = unitInput(editor, ev.target)
if (u == undefined) {
return
}
- params[event.target.name] = u
+ params[ev.target.name] = u
items[keys.variety].input.value = editor.varietyFrom(params)
} else {
- params[event.target.name] = event.target.value
+ if (ev.target.name === "style" && ev.target.value === "default") {
+ params[ev.target.name] = null
+ } else {
+ params[ev.target.name] = ev.target.value.length === 0 ? null : ev.target.value
+ }
}
editor.addMappedFurniture(params, id)
}
diff --git a/files/furniture/bed.svg b/files/furniture/bed/default.svg
diff --git a/files/furniture/dresser.svg b/files/furniture/dresser/default.svg
diff --git a/files/furniture/nightstand.svg b/files/furniture/nightstand/default.svg
diff --git a/files/furniture/sofa.svg b/files/furniture/sofa/default.svg
diff --git a/files/furniture/table.svg b/files/furniture/table.svg
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- width="100"
- height="100"
- version="1.1"
- id="svg1"
- sodipodi:docname="table.svg"
- inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs1">
- <inkscape:path-effect
- effect="fillet_chamfer"
- id="path-effect1"
- is_visible="true"
- lpeversion="1"
- nodesatellites_param="F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1"
- radius="10"
- unit="px"
- method="auto"
- mode="F"
- chamfer_steps="1"
- flexible="false"
- use_knot_distance="true"
- apply_no_radius="true"
- apply_with_radius="true"
- only_selected="false"
- hide_knots="false" />
- <inkscape:path-effect
- effect="fillet_chamfer"
- id="path-effect1-6"
- is_visible="true"
- lpeversion="1"
- nodesatellites_param="F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1"
- radius="4"
- unit="px"
- method="auto"
- mode="F"
- chamfer_steps="1"
- flexible="false"
- use_knot_distance="true"
- apply_no_radius="true"
- apply_with_radius="true"
- only_selected="false"
- hide_knots="false" />
- </defs>
- <sodipodi:namedview
- id="namedview1"
- pagecolor="#ffffff"
- bordercolor="#000000"
- borderopacity="0.25"
- inkscape:showpageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- inkscape:deskcolor="#d1d1d1"
- inkscape:zoom="5.77"
- inkscape:cx="49.913345"
- inkscape:cy="50"
- inkscape:window-width="1366"
- inkscape:window-height="749"
- inkscape:window-x="0"
- inkscape:window-y="19"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg1" />
- <path
- x="2"
- y="2"
- width="96"
- height="96"
- style="fill:none;stroke:#000000;stroke-width:4;stroke-linejoin:round;fill-opacity:0"
- id="rect1"
- inkscape:path-effect="#path-effect1"
- sodipodi:type="rect"
- d="M 12,2 H 88 A 10,10 45 0 1 98,12 V 88 A 10,10 135 0 1 88,98 H 12 A 10,10 45 0 1 2,88 V 12 A 10,10 135 0 1 12,2 Z" />
- <path
- x="2"
- y="2"
- width="96"
- height="96"
- style="fill:none;stroke:#000000;stroke-width:4;stroke-linejoin:round"
- id="rect1-0"
- inkscape:path-effect="#path-effect1-6"
- sodipodi:type="rect"
- d="m 6,2 h 88 a 4,4 45 0 1 4,4 v 88 a 4,4 135 0 1 -4,4 H 6 A 4,4 45 0 1 2,94 V 6 A 4,4 135 0 1 6,2 Z"
- transform="matrix(0.86224037,0,0,0.86224037,6.8879813,6.8879813)" />
-</svg>
diff --git a/files/furniture/table/default.svg b/files/furniture/table/default.svg
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="100"
+ height="100"
+ version="1.1"
+ id="svg1"
+ sodipodi:docname="default.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1">
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1"
+ is_visible="true"
+ lpeversion="1"
+ nodesatellites_param="F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1"
+ radius="10"
+ unit="px"
+ method="auto"
+ mode="F"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1-6"
+ is_visible="true"
+ lpeversion="1"
+ nodesatellites_param="F,0,0,1,0,8,0,1 @ F,0,0,1,0,8,0,1 @ F,0,0,1,0,8,0,1 @ F,0,0,1,0,8,0,1"
+ radius="8"
+ unit="px"
+ method="auto"
+ mode="F"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ </defs>
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="4.1"
+ inkscape:cx="50"
+ inkscape:cy="50.121951"
+ inkscape:window-width="1366"
+ inkscape:window-height="749"
+ inkscape:window-x="0"
+ inkscape:window-y="19"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1" />
+ <path
+ x="2"
+ y="2"
+ width="96"
+ height="96"
+ style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:3;stroke-linejoin:round;stroke-dasharray:none"
+ id="rect1"
+ inkscape:path-effect="#path-effect1"
+ sodipodi:type="rect"
+ d="M 12,2 H 88 A 10,10 45 0 1 98,12 V 88 A 10,10 135 0 1 88,98 H 12 A 10,10 45 0 1 2,88 V 12 A 10,10 135 0 1 12,2 Z" />
+ <path
+ x="2"
+ y="2"
+ width="96"
+ height="96"
+ style="fill:none;stroke:#000000;stroke-width:2.31954;stroke-linejoin:round;stroke-dasharray:none"
+ id="rect1-0"
+ inkscape:path-effect="#path-effect1-6"
+ sodipodi:type="rect"
+ d="m 10,2 h 80 a 8,8 45 0 1 8,8 v 80 a 8,8 135 0 1 -8,8 H 10 A 8,8 45 0 1 2,90 V 10 a 8,8 135 0 1 8,-8 z"
+ transform="matrix(0.91539094,0,0,0.91537467,4.2308611,4.2308748)" />
+</svg>
diff --git a/files/furniture/table/round.svg b/files/furniture/table/round.svg
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="100"
+ height="100"
+ version="1.1"
+ id="svg1"
+ sodipodi:docname="round.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1">
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1"
+ is_visible="true"
+ lpeversion="1"
+ nodesatellites_param="F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1"
+ radius="10"
+ unit="px"
+ method="auto"
+ mode="F"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1-6"
+ is_visible="true"
+ lpeversion="1"
+ nodesatellites_param="F,0,0,1,0,4800,0,1 @ F,0,0,1,0,4800,0,1 @ F,0,0,1,0,4800,0,1 @ F,0,0,1,0,4800,0,1"
+ radius="50"
+ unit="in"
+ method="auto"
+ mode="F"
+ chamfer_steps="1"
+ flexible="true"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ </defs>
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="4.1"
+ inkscape:cx="50"
+ inkscape:cy="49.878049"
+ inkscape:window-width="1366"
+ inkscape:window-height="749"
+ inkscape:window-x="0"
+ inkscape:window-y="19"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1" />
+ <ellipse
+ style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:4.0532;stroke-linecap:round;stroke-linejoin:bevel;stroke-dasharray:none"
+ id="path1"
+ cy="50"
+ cx="50"
+ rx="47.973396"
+ ry="47.9734" />
+ <circle
+ style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:1.853;stroke-linecap:round;stroke-linejoin:bevel;stroke-dasharray:none"
+ id="path1-1"
+ cy="50"
+ cx="49.999996"
+ r="43.537704" />
+</svg>
diff --git a/files/furniture/table/semi-circle.svg b/files/furniture/table/semi-circle.svg
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="100"
+ height="52"
+ version="1.1"
+ id="svg1"
+ sodipodi:docname="semi-circle.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1">
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1"
+ is_visible="true"
+ lpeversion="1"
+ nodesatellites_param="F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1 @ F,0,0,1,0,10,0,1"
+ radius="10"
+ unit="px"
+ method="auto"
+ mode="F"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1-6"
+ is_visible="true"
+ lpeversion="1"
+ nodesatellites_param="F,0,0,1,0,4800,0,1 @ F,0,0,1,0,4800,0,1 @ F,0,0,1,0,4800,0,1 @ F,0,0,1,0,4800,0,1"
+ radius="50"
+ unit="in"
+ method="auto"
+ mode="F"
+ chamfer_steps="1"
+ flexible="true"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ </defs>
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="4.1"
+ inkscape:cx="49.756098"
+ inkscape:cy="49.878049"
+ inkscape:window-width="1366"
+ inkscape:window-height="749"
+ inkscape:window-x="0"
+ inkscape:window-y="19"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1" />
+ <path
+ style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
+ id="path1"
+ sodipodi:type="arc"
+ sodipodi:cx="50"
+ sodipodi:cy="2.0265999"
+ sodipodi:rx="47.973396"
+ sodipodi:ry="47.9734"
+ sodipodi:start="0"
+ sodipodi:end="3.1415927"
+ sodipodi:arc-type="slice"
+ d="M 97.973396,2.0265999 A 47.973396,47.9734 0 0 1 73.986698,43.572783 47.973396,47.9734 0 0 1 26.013301,43.572782 47.973396,47.9734 0 0 1 2.0266037,2.0265999 H 50 Z" />
+ <path
+ style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:1.853;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
+ id="path1-1"
+ sodipodi:type="arc"
+ sodipodi:cx="49.999996"
+ sodipodi:cy="2.0265999"
+ sodipodi:rx="43.537704"
+ sodipodi:ry="43.537704"
+ sodipodi:start="0"
+ sodipodi:end="3.1415927"
+ sodipodi:arc-type="slice"
+ d="M 93.537701,2.0265999 A 43.537704,43.537704 0 0 1 71.768848,39.731358 43.537704,43.537704 0 0 1 28.231143,39.731357 43.537704,43.537704 0 0 1 6.4622917,2.0265999 H 49.999996 Z" />
+</svg>