diff --git a/media/attribution.js b/media/attribution.js
index 730a8650..0b1c9e18 100644
--- a/media/attribution.js
+++ b/media/attribution.js
@@ -19870,8 +19870,6 @@ const attributionData = {
{ name: "circle.svg", source: null },
{ name: "pencil.svg", source: null },
{ name: "pebble.svg", source: null },
- { name: "credit-card.svg", source: null },
- { name: "credit-card-vertical.svg", source: null },
{ name: "flagpole.svg", source: null },
{ name: "strand.svg", source: null },
{ name: "sine-wave.svg", source: null },
@@ -20356,6 +20354,16 @@ const attributionData = {
"https://www.homedepot.com/p/L-I-F-Industries-36-in-x-80-in-Gray-Flush-Exit-Left-Hand-Fire-Proof-Steel-Prehung-Commercial-Door-with-Welded-Frame-UWX3680L/202510508"
]
},
+ {
+ prefix: "./media/objects/Cards/",
+ all: null,
+ authors: [
+ "chemicalcrux"
+ ],
+ citations: [
+ "https://en.wikipedia.org/wiki/ISO/IEC_7810"
+ ]
+ },
{
prefix: "./media/real-buildings/",
all: "https://wiki.openstreetmap.org/wiki/Blender",
diff --git a/media/objects/Cards/Credit Card-Back.svg b/media/objects/Cards/Credit Card-Back.svg
new file mode 100644
index 00000000..c2eb204f
--- /dev/null
+++ b/media/objects/Cards/Credit Card-Back.svg
@@ -0,0 +1,51 @@
+
+
diff --git a/media/objects/Cards/Credit Card-Edge.svg b/media/objects/Cards/Credit Card-Edge.svg
new file mode 100644
index 00000000..41127d49
--- /dev/null
+++ b/media/objects/Cards/Credit Card-Edge.svg
@@ -0,0 +1,199 @@
+
+
diff --git a/media/objects/Cards/Credit Card-Front.svg b/media/objects/Cards/Credit Card-Front.svg
new file mode 100644
index 00000000..b0e26c19
--- /dev/null
+++ b/media/objects/Cards/Credit Card-Front.svg
@@ -0,0 +1,229 @@
+
+
diff --git a/presets/objects.js b/presets/objects.js
index 90e94522..f2eb31eb 100644
--- a/presets/objects.js
+++ b/presets/objects.js
@@ -203,6 +203,12 @@ function makeModel(data) {
source: "./media/" + data.kind + "/" + data.name + "/" + form.name + "-" + view.name + ".svg"
}
}
+
+ if (view.bottom)
+ views[viewId].image.bottom = view.bottom
+
+ if (view.extra)
+ views[viewId].image.extra = view.extra
})
});
@@ -415,25 +421,6 @@ function makeObjects() {
)
});
- results.push({
- name: "Credit Card",
- constructor: () => makeObject(
- "Credit Card",
- {
- creditCard: {
- height: math.unit(53.98, "mm"),
- image: { source: "./media/objects/credit-card.svg" },
- name: "Credit card",
- },
- creditCardVertical: {
- height: math.unit(85.60, "mm"),
- image: { source: "./media/objects/credit-card-vertical.svg" },
- name: "Credit card (vertical)",
- },
- }
- )
- });
-
results.push({
name: "Molecular",
constructor: () => makeObject(
@@ -919,6 +906,7 @@ function makeObjects() {
/* ***Straws*** */ results.push(makeModel({"name": "Straws", "kind": "objects", "forms": [{"name": "Normal", "views": [{"name": "Front", "height": 0.2159000039100647}, {"name": "Top", "height": 0.006095999851822853}]}, {"name": "Wide", "views": [{"name": "Front", "height": 0.2159000039100647}, {"name": "Top", "height": 0.008127997629344463}]}, {"name": "Smoothie", "views": [{"name": "Front", "height": 0.2159000039100647}, {"name": "Top", "height": 0.00914399977773428}]}, {"name": "Boba", "views": [{"name": "Front", "height": 0.2159000039100647}, {"name": "Top", "height": 0.012191999703645706}]}]}));
/* ***Coins*** */ results.push(makeModel({"name": "Coins", "kind": "objects", "forms": [{"name": "U.S. Dollar", "views": [{"name": "Top", "height": 0.026492198929190636, "mass": 0.008100000210106373}, {"name": "Side", "height": 0.0020000000949949026, "mass": 0.008100000210106373}]}, {"name": "U.S. Half Dollar", "views": [{"name": "Top", "height": 0.03060699999332428, "mass": 0.011339999735355377}, {"name": "Side", "height": 0.00215000007301569, "mass": 0.011339999735355377}]}, {"name": "U.S. Quarter", "views": [{"name": "Top", "height": 0.024257000535726547, "mass": 0.005669999867677689}, {"name": "Side", "height": 0.0017500000540167093, "mass": 0.005669999867677689}]}, {"name": "U.S. Dime", "views": [{"name": "Top", "height": 0.017906999215483665, "mass": 0.002268000040203333}, {"name": "Side", "height": 0.0013500000350177288, "mass": 0.002268000040203333}]}, {"name": "U.S. Nickel", "views": [{"name": "Top", "height": 0.021208999678492546, "mass": 0.004999999888241291}, {"name": "Side", "height": 0.0019500000635161996, "mass": 0.004999999888241291}]}, {"name": "U.S. Penny", "views": [{"name": "Top", "height": 0.019050000235438347, "mass": 0.0024999999441206455}, {"name": "Side", "height": 0.0015200000489130616, "mass": 0.0024999999441206455}]}, {"name": "UK \u00a35", "views": [{"name": "Top", "height": 0.028400002047419548, "mass": 0.028279999271035194}, {"name": "Side", "height": 0.0028900043107569218, "mass": 0.028279999271035194}]}, {"name": "UK \u00a32", "views": [{"name": "Top", "height": 0.028400002047419548, "mass": 0.012000000104308128}, {"name": "Side", "height": 0.0025000039022415876, "mass": 0.012000000104308128}]}, {"name": "UK \u00a31", "views": [{"name": "Top", "height": 0.023430000990629196, "mass": 0.008750000037252903}, {"name": "Side", "height": 0.0028000001329928637, "mass": 0.008750000037252903}]}, {"name": "UK 50p", "views": [{"name": "Top", "height": 0.027300003916025162, "mass": 0.00800000037997961}, {"name": "Side", "height": 0.0017800000496208668, "mass": 0.00800000037997961}]}, {"name": "UK 20p", "views": [{"name": "Top", "height": 0.021400000900030136, "mass": 0.004999999888241291}, {"name": "Side", "height": 0.0017000001389533281, "mass": 0.004999999888241291}]}, {"name": "UK 10p", "views": [{"name": "Top", "height": 0.024500001221895218, "mass": 0.006500000134110451}, {"name": "Side", "height": 0.0018500001169741154, "mass": 0.006500000134110451}]}, {"name": "UK 5p", "views": [{"name": "Top", "height": 0.018000001087784767, "mass": 0.0032500000670552254}, {"name": "Side", "height": 0.0017000001389533281, "mass": 0.0032500000670552254}]}, {"name": "UK 2p", "views": [{"name": "Top", "height": 0.02590000070631504, "mass": 0.007120000198483467}, {"name": "Side", "height": 0.00203000009059906, "mass": 0.007120000198483467}]}, {"name": "UK 1p", "views": [{"name": "Top", "height": 0.0203000009059906, "mass": 0.0035600000992417336}, {"name": "Side", "height": 0.0016500001074746251, "mass": 0.0035600000992417336}]}, {"name": "Canadian Two Dollar", "views": [{"name": "Top", "height": 0.02800000086426735, "mass": 0.007300000172108412}, {"name": "Side", "height": 0.0018000000854954123, "mass": 0.007300000172108412}]}, {"name": "Canadian Dollar", "views": [{"name": "Top", "height": 0.026500001549720764, "mass": 0.0062699997797608376}, {"name": "Side", "height": 0.0019500007620081306, "mass": 0.0062699997797608376}]}, {"name": "2 Euro", "views": [{"name": "Top", "height": 0.02575000189244747, "mass": 0.008500000461935997}, {"name": "Side", "height": 0.0022000002209097147, "mass": 0.008500000461935997}]}, {"name": "1 Euro", "views": [{"name": "Top", "height": 0.023250000551342964, "mass": 0.007499999832361937}, {"name": "Side", "height": 0.0023300000466406345, "mass": 0.007499999832361937}]}, {"name": "500 Yen", "views": [{"name": "Top", "height": 0.026500001549720764, "mass": 0.0071000000461936}, {"name": "Side", "height": 0.0018100000452250242, "mass": 0.0071000000461936}]}, {"name": "50 Yen", "views": [{"name": "Top", "height": 0.021000003442168236, "mass": 0.004000000189989805}, {"name": "Side", "height": 0.0015100002055987716, "mass": 0.004000000189989805}]}, {"name": "5 Yen", "views": [{"name": "Top", "height": 0.02200000174343586, "mass": 0.0037499999161809683}, {"name": "Side", "height": 0.0015100002055987716, "mass": 0.0037499999161809683}]}]}));
/* ***Doors*** */ results.push(makeModel({"name": "Doors", "kind": "objects", "forms": [{"name": "6 Panel Door", "views": [{"name": "Front", "height": 2.0320000648498535, "mass": 11.793399810791016}, {"name": "Angled", "height": 2.0320000648498535, "mass": 11.793399810791016}, {"name": "Side", "height": 2.0320000648498535, "mass": 11.793399810791016}, {"name": "Top", "height": 0.03492499887943268, "mass": 11.793399810791016}]}, {"name": "French Door", "views": [{"name": "Front", "height": 2.0320000648498535, "mass": 31.75149917602539}, {"name": "Angled", "height": 2.0320000648498535, "mass": 31.75149917602539}, {"name": "Side", "height": 2.0320000648498535, "mass": 31.75149917602539}, {"name": "Top", "height": 0.03492499887943268, "mass": 31.75149917602539}]}, {"name": "Fire Door", "views": [{"name": "Front", "height": 2.0320000648498535, "mass": 54.54545593261719}, {"name": "Angled", "height": 2.0320000648498535, "mass": 54.54545593261719}, {"name": "Side", "height": 2.0320000648498535, "mass": 54.54545593261719}, {"name": "Top", "height": 0.10518216341733932, "mass": 54.54545593261719}]}]}));
+ /* ***Cards*** */ results.push(makeModel({"name": "Cards", "kind": "objects", "forms": [{"name": "Credit Card", "views": [{"name": "Front", "height": 0.053975000977516174, "mass": 11.793399810791016, "extra": 1.0047993079868203, "bottom": 0.00475367924528303}, {"name": "Back", "height": 0.053975000977516174, "mass": 11.793399810791016, "extra": 1.0047993079868203, "bottom": 0.00475367924528303}, {"name": "Edge", "height": 0.0015578659949824214, "mass": 11.793399810791016, "extra": 1.1704167, "bottom": 0.127097594675073}]}]}));
/* ***INSERT HERE*** */
return results;
}
diff --git a/scripts/blender-model.py b/scripts/blender-model.py
index d8e52985..d3251fac 100644
--- a/scripts/blender-model.py
+++ b/scripts/blender-model.py
@@ -12,7 +12,8 @@ VIEW_DATA = {
"Back Angled": [1.5, 1, 2, "Back Angled"],
"Back": [2, 1, 2, "Back"],
"Top": [0, 0, 1, "Top"],
- "Bottom": [0, 2, 1, "Bottom"]
+ "Bottom": [0, 2, 1, "Bottom"],
+ "Bottom Flipped": [2, 2, 1, "Bottom Flipped"],
}
def get_bounds(objects):
@@ -42,6 +43,7 @@ config = json.load(open(json_path.resolve(), encoding="utf-8"))
parent_workdir = config["work-directory"]
c = bpy.data.objects["cam"]
+lineart = bpy.data.objects["lineart"]
c.data.type = "ORTHO"
bpy.data.scenes["Scene"].render.resolution_x = 2000
@@ -65,6 +67,12 @@ default_views = []
for view in mv["MVViews"].split(","):
default_views.append(VIEW_DATA[view.strip()])
+if "MVViewLabels" in mv:
+ for pair in mv["MVViewLabels"].split(","):
+ key, val = pair.split(":")
+ VIEW_DATA[key.strip()][3] = val.strip()
+
+
workdir = pathlib.Path(parent_workdir).joinpath(all_data["name"])
os.makedirs(workdir, exist_ok=True)
@@ -120,9 +128,14 @@ for coll in collections:
if "Mass" in coll:
data["views"][-1]["mass"] = coll["Mass"]
+ lineart.hide_render = False
filename = f"{coll.name}-{angles[3]}.png"
bpy.context.scene.render.filepath = workdir.joinpath(filename).resolve().__str__()
bpy.ops.render.render(write_still = True)
+ lineart.hide_render = True
+ filename = f"{coll.name}-{angles[3]}-noline.png"
+ bpy.context.scene.render.filepath = workdir.joinpath(filename).resolve().__str__()
+ bpy.ops.render.render(write_still = True)
all_data["forms"].append(data)
coll.hide_render = True
diff --git a/scripts/process-model.py b/scripts/process-model.py
index 04998cf2..d4b93549 100644
--- a/scripts/process-model.py
+++ b/scripts/process-model.py
@@ -1,11 +1,12 @@
import sys
import re
import json
-import glob
import os
import subprocess
import pathlib
+from xml.dom import minidom
+
# an affront to god
def combine(base_path, highlight_path, vivid_path, output_path):
base = open(base_path, "r", encoding="utf-8").read()
@@ -57,6 +58,7 @@ for data in all_data["forms"]:
for view in data["views"]:
view_name = view["name"]
input = sourcedir.joinpath(name + "-" + view_name + ".png").__str__()
+ input_noline_raw = sourcedir.joinpath(name + "-" + view_name + "-" + "noline.png").__str__()
result = outputdir.joinpath(name + "-" + view_name + ".svg").__str__()
print(result)
if os.path.exists(result) and os.path.getmtime(input) < os.path.getmtime(result):
@@ -66,19 +68,47 @@ for data in all_data["forms"]:
input_base = sourcedir.joinpath(name + "-" + view_name + "-base.bmp").__str__()
input_highlight = sourcedir.joinpath(name + "-" + view_name + "-highlight.bmp").__str__()
input_vivid = sourcedir.joinpath(name + "-" + view_name + "-vivid.bmp").__str__()
+ input_noline = sourcedir.joinpath(name + "-" + view_name + "-noline.bmp").__str__()
subprocess.run(["magick", "convert", input, base_lut, "-channel", "RGB", "-clut", "-background", "#FFFFFF", "-flatten", input_base], shell=False)
subprocess.run(["magick", "convert", input, highlight_lut, "-channel", "RGB", "-clut", "-background", "#FFFFFF", "-flatten", input_highlight], shell=False)
subprocess.run(["magick", "convert", input, vivid_lut, "-channel", "RGB", "-clut", "-background", "#FFFFFF", "-flatten", input_vivid], shell=False)
+
+ # to correct for extra height from lines
+ subprocess.run(["magick", "convert", input_noline_raw, base_lut, "-channel", "RGB", "-clut", "-background", "#FFFFFF", "-flatten", input_noline], shell=False)
output_base = sourcedir.joinpath(name + "-" + view_name + "-base.svg").__str__()
output_highlight = sourcedir.joinpath(name + "-" + view_name + "-highlight.svg").__str__()
output_vivid = sourcedir.joinpath(name + "-" + view_name + "-vivid.svg").__str__()
+ output_noline = sourcedir.joinpath(name + "-" + view_name + "-noline.svg").__str__()
subprocess.run([POTRACE, input_base, "-b", "svg", "-o", output_base], shell=False)
subprocess.run([POTRACE, input_highlight, "-b", "svg", "-C", "#1a1a1a", "-o", output_highlight], shell=False)
subprocess.run([POTRACE, input_vivid, "-b", "svg", "-C", "#333333", "-o", output_vivid], shell=False)
+ subprocess.run([POTRACE, input_noline, "-b", "svg", "-C", "#333333", "-o", output_noline], shell=False)
+
combine(output_base, output_highlight, output_vivid, result)
+
+ noline_result = sourcedir.joinpath(name + "-" + view_name + "-noline_processed.svg").__str__()
+
+ # we now learn how much height was added by the lineart!
+
+ original_xml = minidom.parse(open(result))
+ noline_xml = minidom.parse(open(noline_result))
+
+ original_height = float(original_xml.childNodes[0].attributes["height"].value[:-2])
+ noline_height = float(noline_xml.childNodes[0].attributes["height"].value[:-2])
+
+ delta = original_height - noline_height
+
+ height = original_height
+ bottom = height - (height - delta / 2)
+ top = height - delta / 2
+
+ view["extra"] = (height - bottom) / (top - bottom)
+ view["bottom"] = bottom / height
+
+ combine(output_noline, output_noline, output_noline, noline_result)
# os.unlink(input_base)
# os.unlink(input_highlight)