From 5c8a8b9210e7cb4b8b752cae20555d075ef3f68d Mon Sep 17 00:00:00 2001 From: Anson Date: Thu, 17 Mar 2022 01:00:50 -0700 Subject: [PATCH 01/24] Initial commit --- MisterBiggs/stl-process/Manifest.toml | 275 ++++++++++++++++++++++++++ MisterBiggs/stl-process/Project.toml | 6 + MisterBiggs/stl-process/process.jl | 63 ++++++ 3 files changed, 344 insertions(+) create mode 100644 MisterBiggs/stl-process/Manifest.toml create mode 100644 MisterBiggs/stl-process/Project.toml create mode 100644 MisterBiggs/stl-process/process.jl diff --git a/MisterBiggs/stl-process/Manifest.toml b/MisterBiggs/stl-process/Manifest.toml new file mode 100644 index 000000000..421591d1d --- /dev/null +++ b/MisterBiggs/stl-process/Manifest.toml @@ -0,0 +1,275 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.7.2" +manifest_format = "2.0" + +[[deps.Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.3" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[deps.BenchmarkTools]] +deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] +git-tree-sha1 = "4c10eee4af024676200bc7752e536f858c6b8f93" +uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +version = "1.3.1" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.11.0" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[deps.DataAPI]] +git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.9.0" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[deps.Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[deps.EarCut_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d" +uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +version = "2.2.3+0" + +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "80ced645013a5dbdc52cf70329399c35ce007fae" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.13.0" + +[[deps.FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.4" + +[[deps.GeometryBasics]] +deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "83ea630384a13fc4f002b77690bc0afeb4255ac9" +uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +version = "0.4.2" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[deps.IterTools]] +git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.4.0" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[deps.JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.4.1" + +[[deps.JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.3" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[deps.LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[deps.MeshIO]] +deps = ["ColorTypes", "FileIO", "GeometryBasics", "Printf"] +git-tree-sha1 = "8be09d84a2d597c7c0c34d7d604c039c9763e48c" +uuid = "7269a6da-0436-5bbc-96c2-40638cbb6118" +version = "0.4.10" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" + +[[deps.OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[deps.Parsers]] +deps = ["Dates"] +git-tree-sha1 = "85b5da0fa43588c75bb1ff986493443f821c70b7" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.2.3" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "d3538e7f8a790dc8903519090857ef8e1283eecd" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.5" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[deps.Profile]] +deps = ["Printf"] +uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[deps.Random]] +deps = ["SHA", "Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.0" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[deps.SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "6976fab022fea2ffea3d945159317556e5dad87c" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.4.2" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StructArrays]] +deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"] +git-tree-sha1 = "57617b34fa34f91d536eb265df67c2d4519b8b98" +uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" +version = "0.6.5" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] +git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.7.0" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml new file mode 100644 index 000000000..2875b8658 --- /dev/null +++ b/MisterBiggs/stl-process/Project.toml @@ -0,0 +1,6 @@ +[deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +MeshIO = "7269a6da-0436-5bbc-96c2-40638cbb6118" diff --git a/MisterBiggs/stl-process/process.jl b/MisterBiggs/stl-process/process.jl new file mode 100644 index 000000000..3723cff70 --- /dev/null +++ b/MisterBiggs/stl-process/process.jl @@ -0,0 +1,63 @@ +using MeshIO +using FileIO +using GeometryBasics + +using LinearAlgebra + + +stl = load(raw"C:\Users\albig\Downloads\cubeblender.stl") + +function get_mass_properties(triangles) + x = [ [v[1] for v in tri] for tri in triangles] |> x -> reduce(hcat,x)' + y = [ [v[2] for v in tri] for tri in triangles] |> x -> reduce(hcat,x)' + z = [ [v[3] for v in tri] for tri in triangles] |> x -> reduce(hcat,x)' + + + function subexpression(x) + w0, w1, w2 = x[:, 1], x[:, 2], x[:, 3] + temp0 = w0 + w1 + f1 = temp0 + w2 + temp1 = w0 .* w0 + temp2 = temp1 .+ w1 .* temp0 + f2 = temp2 .+ w2 .* f1 + f3 = w0 .* temp1 .+ w1 .* temp2 .+ w2 .* f2 + g0 = f2 .+ w0 .* (f1 .+ w0) + g1 = f2 .+ w1 .* (f1 .+ w1) + g2 = f2 .+ w2 .* (f1 .+ w2) + return f1, f2, f3, g0, g1, g2 + end + + x0, x1, x2 = x[:, 1], x[:, 2], x[:, 3] + y0, y1, y2 = y[:, 1], y[:, 2], y[:, 3] + z0, z1, z2 = z[:, 1], z[:, 2], z[:, 3] + a1, b1, c1 = x1 - x0, y1 - y0, z1 - z0 + a2, b2, c2 = x2 - x0, y2 - y0, z2 - z0 + d0, d1, d2 = b1 .* c2 .- b2 .* c1, a2 .* c1 .- a1 .* c2, a1 .* b2 .- a2 .* b1 + + f1x, f2x, f3x, g0x, g1x, g2x = subexpression(x) + f1y, f2y, f3y, g0y, g1y, g2y = subexpression(y) + f1z, f2z, f3z, g0z, g1z, g2z = subexpression(z) + + intg = zeros(10) + intg[1] = sum(d0 .* f1x) + intg[2:4] = [sum(d0 .* f2x), sum(d1 .* f2y), sum(d2 .* f2z)] + intg[5:7] = [sum(d0 .* f3x), sum(d1 .* f3y), sum(d2 .* f3z)] + intg[8] = sum(d0 .* (y0 .* g0x + y1 .* g1x + y2 .* g2x)) + intg[9] = sum(d1 .* (z0 .* g0y + z1 .* g1y + z2 .* g2y)) + intg[10] = sum(d2 .* (x0 .* g0z + x1 .* g1z + x2 .* g2z)) + intg = intg ./ [6, 24, 24, 24, 60, 60, 60, 120, 120, 120] + volumee = intg[1] + cog = intg[2:4] / volumee + cogsq = cog .^ 2 + inertia = zeros((3, 3)) + inertia[1, 1] = intg[6] + intg[7] - volumee .* (cogsq[2] + cogsq[3]) + inertia[2, 2] = intg[5] + intg[7] - volumee .* (cogsq[3] + cogsq[1]) + inertia[3, 3] = intg[5] + intg[6] - volumee .* (cogsq[1] + cogsq[2]) + inertia[1, 2] = inertia[2, 1] = -(intg[8] - volumee .* cog[1] .* cog[2]) + inertia[2, 3] = inertia[3, 2] = -(intg[9] - volumee .* cog[2] .* cog[3]) + inertia[1, 3] = inertia[3, 1] = -(intg[10] - volumee .* cog[3] .* cog[1]) + return volume, cog, inertia +end +volumee +cog +inertia \ No newline at end of file -- 2.51.2 From b15fc4d21cf751f5a0e89236abfc6fced54aa91a Mon Sep 17 00:00:00 2001 From: Anson Date: Tue, 22 Mar 2022 08:24:15 -0700 Subject: [PATCH 02/24] small changes --- MisterBiggs/stl-process/.JuliaFormatter.toml | 15 +++ MisterBiggs/stl-process/process.jl | 115 ++++++++++--------- 2 files changed, 76 insertions(+), 54 deletions(-) create mode 100644 MisterBiggs/stl-process/.JuliaFormatter.toml diff --git a/MisterBiggs/stl-process/.JuliaFormatter.toml b/MisterBiggs/stl-process/.JuliaFormatter.toml new file mode 100644 index 000000000..71fef4362 --- /dev/null +++ b/MisterBiggs/stl-process/.JuliaFormatter.toml @@ -0,0 +1,15 @@ +style = "blue" + +margin = 120 +always_for_in = true +whitespace_ops_in_indices = true +remove_extra_newlines = true +import_to_using = true +pipe_to_function_call = false +short_to_long_function_def = false +always_use_return = false +format_docstrings = true +conditional_to_if = true +trailing_comma = true +join_lines_based_on_source = false +format_markdown = true diff --git a/MisterBiggs/stl-process/process.jl b/MisterBiggs/stl-process/process.jl index 3723cff70..09788bb61 100644 --- a/MisterBiggs/stl-process/process.jl +++ b/MisterBiggs/stl-process/process.jl @@ -1,63 +1,70 @@ using MeshIO using FileIO -using GeometryBasics using LinearAlgebra - -stl = load(raw"C:\Users\albig\Downloads\cubeblender.stl") +stl = load(raw"C:\Users\albig\Downloads\cubeblender.stl") +# stl = load(raw"C:\Coding\stl_read\3DBenchy.stl") function get_mass_properties(triangles) - x = [ [v[1] for v in tri] for tri in triangles] |> x -> reduce(hcat,x)' - y = [ [v[2] for v in tri] for tri in triangles] |> x -> reduce(hcat,x)' - z = [ [v[3] for v in tri] for tri in triangles] |> x -> reduce(hcat,x)' - - - function subexpression(x) - w0, w1, w2 = x[:, 1], x[:, 2], x[:, 3] - temp0 = w0 + w1 - f1 = temp0 + w2 - temp1 = w0 .* w0 - temp2 = temp1 .+ w1 .* temp0 - f2 = temp2 .+ w2 .* f1 - f3 = w0 .* temp1 .+ w1 .* temp2 .+ w2 .* f2 - g0 = f2 .+ w0 .* (f1 .+ w0) - g1 = f2 .+ w1 .* (f1 .+ w1) - g2 = f2 .+ w2 .* (f1 .+ w2) - return f1, f2, f3, g0, g1, g2 - end - - x0, x1, x2 = x[:, 1], x[:, 2], x[:, 3] - y0, y1, y2 = y[:, 1], y[:, 2], y[:, 3] - z0, z1, z2 = z[:, 1], z[:, 2], z[:, 3] - a1, b1, c1 = x1 - x0, y1 - y0, z1 - z0 - a2, b2, c2 = x2 - x0, y2 - y0, z2 - z0 - d0, d1, d2 = b1 .* c2 .- b2 .* c1, a2 .* c1 .- a1 .* c2, a1 .* b2 .- a2 .* b1 - - f1x, f2x, f3x, g0x, g1x, g2x = subexpression(x) - f1y, f2y, f3y, g0y, g1y, g2y = subexpression(y) - f1z, f2z, f3z, g0z, g1z, g2z = subexpression(z) - - intg = zeros(10) - intg[1] = sum(d0 .* f1x) - intg[2:4] = [sum(d0 .* f2x), sum(d1 .* f2y), sum(d2 .* f2z)] - intg[5:7] = [sum(d0 .* f3x), sum(d1 .* f3y), sum(d2 .* f3z)] - intg[8] = sum(d0 .* (y0 .* g0x + y1 .* g1x + y2 .* g2x)) - intg[9] = sum(d1 .* (z0 .* g0y + z1 .* g1y + z2 .* g2y)) - intg[10] = sum(d2 .* (x0 .* g0z + x1 .* g1z + x2 .* g2z)) - intg = intg ./ [6, 24, 24, 24, 60, 60, 60, 120, 120, 120] - volumee = intg[1] - cog = intg[2:4] / volumee - cogsq = cog .^ 2 - inertia = zeros((3, 3)) - inertia[1, 1] = intg[6] + intg[7] - volumee .* (cogsq[2] + cogsq[3]) - inertia[2, 2] = intg[5] + intg[7] - volumee .* (cogsq[3] + cogsq[1]) - inertia[3, 3] = intg[5] + intg[6] - volumee .* (cogsq[1] + cogsq[2]) - inertia[1, 2] = inertia[2, 1] = -(intg[8] - volumee .* cog[1] .* cog[2]) - inertia[2, 3] = inertia[3, 2] = -(intg[9] - volumee .* cog[2] .* cog[3]) - inertia[1, 3] = inertia[3, 1] = -(intg[10] - volumee .* cog[3] .* cog[1]) - return volume, cog, inertia + x = reduce(hcat, [[v[1] for v in tri] for tri in triangles])' + y = reduce(hcat, [[v[2] for v in tri] for tri in triangles])' + z = reduce(hcat, [[v[3] for v in tri] for tri in triangles])' + + function subexpression(x) + w0, w1, w2 = x[:, 1], x[:, 2], x[:, 3] + temp0 = w0 + w1 + f1 = temp0 + w2 + temp1 = w0 .* w0 + temp2 = temp1 .+ w1 .* temp0 + f2 = temp2 .+ w2 .* f1 + f3 = w0 .* temp1 .+ w1 .* temp2 .+ w2 .* f2 + g0 = f2 .+ w0 .* (f1 .+ w0) + g1 = f2 .+ w1 .* (f1 .+ w1) + g2 = f2 .+ w2 .* (f1 .+ w2) + return f1, f2, f3, g0, g1, g2 + end + + x0, x1, x2 = x[:, 1], x[:, 2], x[:, 3] + y0, y1, y2 = y[:, 1], y[:, 2], y[:, 3] + z0, z1, z2 = z[:, 1], z[:, 2], z[:, 3] + a1, b1, c1 = x1 - x0, y1 - y0, z1 - z0 + a2, b2, c2 = x2 - x0, y2 - y0, z2 - z0 + d0, d1, d2 = b1 .* c2 .- b2 .* c1, a2 .* c1 .- a1 .* c2, a1 .* b2 .- a2 .* b1 + + f1x, f2x, f3x, g0x, g1x, g2x = subexpression(x) + f1y, f2y, f3y, g0y, g1y, g2y = subexpression(y) + f1z, f2z, f3z, g0z, g1z, g2z = subexpression(z) + + intg = zeros(10) + intg[1] = sum(d0 .* f1x) + intg[2:4] = [sum(d0 .* f2x), sum(d1 .* f2y), sum(d2 .* f2z)] + intg[5:7] = [sum(d0 .* f3x), sum(d1 .* f3y), sum(d2 .* f3z)] + intg[8] = sum(d0 .* (y0 .* g0x + y1 .* g1x + y2 .* g2x)) + intg[9] = sum(d1 .* (z0 .* g0y + z1 .* g1y + z2 .* g2y)) + intg[10] = sum(d2 .* (x0 .* g0z + x1 .* g1z + x2 .* g2z)) + intg = intg ./ [6, 24, 24, 24, 60, 60, 60, 120, 120, 120] + + volume = intg[1] + + center_of_gravity = intg[2:4] / volume + cogsq = center_of_gravity .^ 2 + + inertia = zeros((3, 3)) + inertia[1, 1] = intg[6] + intg[7] - volume .* (cogsq[2] + cogsq[3]) + inertia[2, 2] = intg[5] + intg[7] - volume .* (cogsq[3] + cogsq[1]) + inertia[3, 3] = intg[5] + intg[6] - volume .* (cogsq[1] + cogsq[2]) + inertia[1, 2] = inertia[2, 1] = -(intg[8] - volume .* center_of_gravity[1] .* center_of_gravity[2]) + inertia[2, 3] = inertia[3, 2] = -(intg[9] - volume .* center_of_gravity[2] .* center_of_gravity[3]) + inertia[1, 3] = inertia[3, 1] = -(intg[10] - volume .* center_of_gravity[3] .* center_of_gravity[1]) + + return volume, center_of_gravity, inertia end -volumee + +v, cog, inertia = get_mass_properties(stl) + +v cog -inertia \ No newline at end of file +inertia + +eigen(inertia) -- 2.51.2 From 7a2ad3b10c4d950479c00f94faa6b158910434f2 Mon Sep 17 00:00:00 2001 From: Anson Date: Tue, 22 Mar 2022 08:26:18 -0700 Subject: [PATCH 03/24] added references --- MisterBiggs/stl-process/process.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MisterBiggs/stl-process/process.jl b/MisterBiggs/stl-process/process.jl index 09788bb61..9d533a438 100644 --- a/MisterBiggs/stl-process/process.jl +++ b/MisterBiggs/stl-process/process.jl @@ -7,6 +7,11 @@ stl = load(raw"C:\Users\albig\Downloads\cubeblender.stl") # stl = load(raw"C:\Coding\stl_read\3DBenchy.stl") function get_mass_properties(triangles) + """ + Reference: + https://github.com/WoLpH/numpy-stl/blob/42b6c67324e13b8712af6730f77e5e9544ef63b0/stl/base.py#L362 + https://www.geometrictools.com/Documentation/PolyhedralMassProperties.pdf + """ x = reduce(hcat, [[v[1] for v in tri] for tri in triangles])' y = reduce(hcat, [[v[2] for v in tri] for tri in triangles])' z = reduce(hcat, [[v[3] for v in tri] for tri in triangles])' -- 2.51.2 From 04d0879b3cd00a1bdfc1b9df9f66305f7a452311 Mon Sep 17 00:00:00 2001 From: Anson Date: Mon, 28 Mar 2022 23:41:04 -0700 Subject: [PATCH 04/24] pgk-ify --- MisterBiggs/stl-process/Project.toml | 5 +++++ .../stl-process/{process.jl => src/stlProcess.jl} | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) rename MisterBiggs/stl-process/{process.jl => src/stlProcess.jl} (91%) diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml index 2875b8658..37fe2c019 100644 --- a/MisterBiggs/stl-process/Project.toml +++ b/MisterBiggs/stl-process/Project.toml @@ -1,3 +1,8 @@ +name = "stlProcess" +uuid = "68914fc9-42cf-4b37-b06f-0b65edf9b8fa" +authors = ["Anson "] +version = "0.1.0" + [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" diff --git a/MisterBiggs/stl-process/process.jl b/MisterBiggs/stl-process/src/stlProcess.jl similarity index 91% rename from MisterBiggs/stl-process/process.jl rename to MisterBiggs/stl-process/src/stlProcess.jl index 9d533a438..22aa1cce7 100644 --- a/MisterBiggs/stl-process/process.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -1,3 +1,5 @@ +module stlProcess + using MeshIO using FileIO @@ -7,11 +9,11 @@ stl = load(raw"C:\Users\albig\Downloads\cubeblender.stl") # stl = load(raw"C:\Coding\stl_read\3DBenchy.stl") function get_mass_properties(triangles) - """ - Reference: - https://github.com/WoLpH/numpy-stl/blob/42b6c67324e13b8712af6730f77e5e9544ef63b0/stl/base.py#L362 - https://www.geometrictools.com/Documentation/PolyhedralMassProperties.pdf - """ + """ + Reference: + https://github.com/WoLpH/numpy-stl/blob/42b6c67324e13b8712af6730f77e5e9544ef63b0/stl/base.py#L362 + https://www.geometrictools.com/Documentation/PolyhedralMassProperties.pdf + """ x = reduce(hcat, [[v[1] for v in tri] for tri in triangles])' y = reduce(hcat, [[v[2] for v in tri] for tri in triangles])' z = reduce(hcat, [[v[3] for v in tri] for tri in triangles])' @@ -73,3 +75,5 @@ cog inertia eigen(inertia) + +end # module -- 2.51.2 From 21c799dd90aa553c40a593e5c147e9d5a38e4e27 Mon Sep 17 00:00:00 2001 From: Anson Date: Tue, 29 Mar 2022 00:09:41 -0700 Subject: [PATCH 05/24] tests and stuff --- MisterBiggs/stl-process/Manifest.toml | 29 +- MisterBiggs/stl-process/Project.toml | 3 - MisterBiggs/stl-process/src/stlProcess.jl | 24 +- MisterBiggs/stl-process/test/Manifest.toml | 250 ++++++++++++++++++ MisterBiggs/stl-process/test/Project.toml | 5 + MisterBiggs/stl-process/test/runtests.jl | 18 ++ .../test/test_assets/cubeblender.stl | Bin 0 -> 684 bytes 7 files changed, 288 insertions(+), 41 deletions(-) create mode 100644 MisterBiggs/stl-process/test/Manifest.toml create mode 100644 MisterBiggs/stl-process/test/Project.toml create mode 100644 MisterBiggs/stl-process/test/runtests.jl create mode 100644 MisterBiggs/stl-process/test/test_assets/cubeblender.stl diff --git a/MisterBiggs/stl-process/Manifest.toml b/MisterBiggs/stl-process/Manifest.toml index 421591d1d..4d223514f 100644 --- a/MisterBiggs/stl-process/Manifest.toml +++ b/MisterBiggs/stl-process/Manifest.toml @@ -18,12 +18,6 @@ uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -[[deps.BenchmarkTools]] -deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] -git-tree-sha1 = "4c10eee4af024676200bc7752e536f858c6b8f93" -uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "1.3.1" - [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" @@ -96,12 +90,6 @@ git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" version = "1.4.1" -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" - [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -142,9 +130,6 @@ git-tree-sha1 = "8be09d84a2d597c7c0c34d7d604c039c9763e48c" uuid = "7269a6da-0436-5bbc-96c2-40638cbb6118" version = "0.4.10" -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" @@ -160,12 +145,6 @@ git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" version = "1.4.1" -[[deps.Parsers]] -deps = ["Dates"] -git-tree-sha1 = "85b5da0fa43588c75bb1ff986493443f821c70b7" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.2.3" - [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" @@ -180,10 +159,6 @@ version = "1.2.5" deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -[[deps.Profile]] -deps = ["Printf"] -uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" - [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" @@ -213,9 +188,9 @@ uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[deps.StaticArrays]] deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "6976fab022fea2ffea3d945159317556e5dad87c" +git-tree-sha1 = "4f6ec5d99a28e1a749559ef7dd518663c5eca3d5" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.4.2" +version = "1.4.3" [[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml index 37fe2c019..0d0cf8af2 100644 --- a/MisterBiggs/stl-process/Project.toml +++ b/MisterBiggs/stl-process/Project.toml @@ -4,8 +4,5 @@ authors = ["Anson "] version = "0.1.0" [deps] -BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MeshIO = "7269a6da-0436-5bbc-96c2-40638cbb6118" diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index 22aa1cce7..250ed1b3b 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -3,10 +3,18 @@ module stlProcess using MeshIO using FileIO -using LinearAlgebra +struct Properties + volume::Float64 + center_of_gravity::Vector{Float64} + inertia::Matrix{Float64} -stl = load(raw"C:\Users\albig\Downloads\cubeblender.stl") -# stl = load(raw"C:\Coding\stl_read\3DBenchy.stl") + function Properties(volume, center_of_gravity, inertia) + @assert size(center_of_gravity) == (3,) + @assert size(inertia) == (3, 3) + + return new(volume, center_of_gravity, inertia) + end +end function get_mass_properties(triangles) """ @@ -65,15 +73,9 @@ function get_mass_properties(triangles) inertia[2, 3] = inertia[3, 2] = -(intg[9] - volume .* center_of_gravity[2] .* center_of_gravity[3]) inertia[1, 3] = inertia[3, 1] = -(intg[10] - volume .* center_of_gravity[3] .* center_of_gravity[1]) - return volume, center_of_gravity, inertia + return Properties(volume, center_of_gravity, inertia) end -v, cog, inertia = get_mass_properties(stl) - -v -cog -inertia - -eigen(inertia) +export get_mass_properties end # module diff --git a/MisterBiggs/stl-process/test/Manifest.toml b/MisterBiggs/stl-process/test/Manifest.toml new file mode 100644 index 000000000..4d223514f --- /dev/null +++ b/MisterBiggs/stl-process/test/Manifest.toml @@ -0,0 +1,250 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.7.2" +manifest_format = "2.0" + +[[deps.Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.3" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.11.0" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[deps.DataAPI]] +git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.9.0" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[deps.Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[deps.EarCut_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d" +uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +version = "2.2.3+0" + +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "80ced645013a5dbdc52cf70329399c35ce007fae" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.13.0" + +[[deps.FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.4" + +[[deps.GeometryBasics]] +deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "83ea630384a13fc4f002b77690bc0afeb4255ac9" +uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +version = "0.4.2" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[deps.IterTools]] +git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.4.0" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[deps.JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.4.1" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[deps.LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[deps.MeshIO]] +deps = ["ColorTypes", "FileIO", "GeometryBasics", "Printf"] +git-tree-sha1 = "8be09d84a2d597c7c0c34d7d604c039c9763e48c" +uuid = "7269a6da-0436-5bbc-96c2-40638cbb6118" +version = "0.4.10" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" + +[[deps.OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "d3538e7f8a790dc8903519090857ef8e1283eecd" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.5" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[deps.Random]] +deps = ["SHA", "Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.0" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[deps.SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "4f6ec5d99a28e1a749559ef7dd518663c5eca3d5" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.4.3" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StructArrays]] +deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"] +git-tree-sha1 = "57617b34fa34f91d536eb265df67c2d4519b8b98" +uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" +version = "0.6.5" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] +git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.7.0" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/MisterBiggs/stl-process/test/Project.toml b/MisterBiggs/stl-process/test/Project.toml new file mode 100644 index 000000000..849222947 --- /dev/null +++ b/MisterBiggs/stl-process/test/Project.toml @@ -0,0 +1,5 @@ +[deps] +FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +MeshIO = "7269a6da-0436-5bbc-96c2-40638cbb6118" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl new file mode 100644 index 000000000..cf4200e12 --- /dev/null +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -0,0 +1,18 @@ +using Test + +using stlProcess + +using FileIO +using MeshIO + +using LinearAlgebra + +@testset "simple cube stl" begin + stl = load(raw"test\test_assets\cubeblender.stl") + + props = get_mass_properties(stl) + + @test props.volume == 8.0 + @test all(props.center_of_gravity .== 0.0) + @test all(eigvals(props.inertia) .≈ (5.0 + 1 / 3)) +end diff --git a/MisterBiggs/stl-process/test/test_assets/cubeblender.stl b/MisterBiggs/stl-process/test/test_assets/cubeblender.stl new file mode 100644 index 0000000000000000000000000000000000000000..77a96ac06c18e641888113c4bb640bb4e17273e2 GIT binary patch literal 684 zcmZ>*D9A4=Nlj5mE6UGRaLP%|OGz!#HP$oKGhiSG@StdFuxDUE!TXW;AT|iwBWpoX z4blx#0m2|Q2t(NrJq`PzG)x963layL1mc4*+zyZoNDqoENCgNZn*uf&rW&jRqyino zbbx5E9e8y>Re{vN><5_)!C+C4`5;v=8kr3e1G@^Mo`C_Y4yY=)99Rd)Hn=X3nIH^O y38IlPR16YJICVf(flP(j15pbS0kOcMAag+~K^VkF!yt7Cdr)+ssRWq{k_P~}N`S2Z literal 0 HcmV?d00001 -- 2.51.2 From f4bee062a857312e6804665210b02a9d63e04713 Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 30 Mar 2022 19:16:40 -0700 Subject: [PATCH 06/24] added source for cube math --- MisterBiggs/stl-process/test/runtests.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index cf4200e12..6b69f9229 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -8,6 +8,7 @@ using MeshIO using LinearAlgebra @testset "simple cube stl" begin + # https://hepweb.ucsd.edu/ph110b/110b_notes/node26.html stl = load(raw"test\test_assets\cubeblender.stl") props = get_mass_properties(stl) -- 2.51.2 From 44c10e6d6fbe66890a7472706787380154e13888 Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 30 Mar 2022 21:30:04 -0700 Subject: [PATCH 07/24] added function to find volume --- MisterBiggs/stl-process/src/stlProcess.jl | 47 ++++++++++++++++++++--- MisterBiggs/stl-process/test/runtests.jl | 25 +++++++++--- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index 250ed1b3b..ad736eaef 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -16,15 +16,52 @@ struct Properties end end -function get_mass_properties(triangles) +function get_volume(triangles; scale=1) + """ + Reference: + https://people.eecs.berkeley.edu/~wkahan/VtetLang.pdf + """ + + volume = 0.0 + for tri in triangles + a, b, c = tri .* scale + + W = sqrt(sum((a .- b) .^ 2)) + V = sqrt(sum((b .- c) .^ 2)) + U = sqrt(sum((c .- a) .^ 2)) + + v = sqrt(sum(a .^ 2)) + u = sqrt(sum(b .^ 2)) + w = sqrt(sum(c .^ 2)) + + X = (w - U + v) * (U + v + w) + Y = (u - V + w) * (V + w + u) + Z = (v - W + u) * (W + u + v) + + x = (U - v + w) * (v - w + U) + y = (V - w + u) * (w - u + V) + z = (W - u + v) * (u - v + W) + + ξ = sqrt(x * Y * Z) + η = sqrt(y * Z * X) + ζ = sqrt(z * X * Y) + λ = sqrt(x * y * z) + + volume += sqrt((ξ + η + ζ - λ) * (λ + ξ + η - ζ) * (η + ζ + λ - ξ) * (ζ + λ + ξ - η)) / (192 * u * v * w) + end + + return volume +end + +function get_mass_properties(triangles; scale=1) """ Reference: https://github.com/WoLpH/numpy-stl/blob/42b6c67324e13b8712af6730f77e5e9544ef63b0/stl/base.py#L362 https://www.geometrictools.com/Documentation/PolyhedralMassProperties.pdf """ - x = reduce(hcat, [[v[1] for v in tri] for tri in triangles])' - y = reduce(hcat, [[v[2] for v in tri] for tri in triangles])' - z = reduce(hcat, [[v[3] for v in tri] for tri in triangles])' + x = reduce(hcat, [[v[1] .* scale for v in tri] for tri in triangles])' + y = reduce(hcat, [[v[2] .* scale for v in tri] for tri in triangles])' + z = reduce(hcat, [[v[3] .* scale for v in tri] for tri in triangles])' function subexpression(x) w0, w1, w2 = x[:, 1], x[:, 2], x[:, 3] @@ -76,6 +113,6 @@ function get_mass_properties(triangles) return Properties(volume, center_of_gravity, inertia) end -export get_mass_properties +export get_mass_properties, get_volume end # module diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index 6b69f9229..e97b8c8a1 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -8,12 +8,25 @@ using MeshIO using LinearAlgebra @testset "simple cube stl" begin - # https://hepweb.ucsd.edu/ph110b/110b_notes/node26.html - stl = load(raw"test\test_assets\cubeblender.stl") + """ + inertia math: + https://hepweb.ucsd.edu/ph110b/110b_notes/node26.html - props = get_mass_properties(stl) + Cube is a standard cube with a length of 2 for each side. + """ + cube_path = raw"test_assets\cubeblender.stl" + stl = load(cube_path) - @test props.volume == 8.0 - @test all(props.center_of_gravity .== 0.0) - @test all(eigvals(props.inertia) .≈ (5.0 + 1 / 3)) + @testset "get_mass_properties" begin + props = get_mass_properties(stl) + + @test props.volume == 8.0 + @test all(props.center_of_gravity .== 0.0) + @test all(eigvals(props.inertia) .≈ (5.0 + 1 / 3)) + + @testset "get_volume" begin + @test get_volume(stl; scale=1) ≈ props.volume rtol = 0.01 + @test get_volume(stl; scale=4) ≈ (4 * 2)^3 rtol = 0.01 + end + end end -- 2.51.2 From 8c392fb4d6cd53455cd5715aadc558156003e7a3 Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 30 Mar 2022 21:33:49 -0700 Subject: [PATCH 08/24] added scaling tests --- MisterBiggs/stl-process/test/runtests.jl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index e97b8c8a1..ff3c121b5 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -17,16 +17,24 @@ using LinearAlgebra cube_path = raw"test_assets\cubeblender.stl" stl = load(cube_path) - @testset "get_mass_properties" begin + @testset "get_mass_properties function" begin props = get_mass_properties(stl) @test props.volume == 8.0 @test all(props.center_of_gravity .== 0.0) @test all(eigvals(props.inertia) .≈ (5.0 + 1 / 3)) - @testset "get_volume" begin + @testset "get_volume function" begin @test get_volume(stl; scale=1) ≈ props.volume rtol = 0.01 @test get_volume(stl; scale=4) ≈ (4 * 2)^3 rtol = 0.01 end end + @testset "Compare volumes with scaling" begin + for scale in 1:5 + props = get_mass_properties(stl; scale=scale) + volume = get_volume(stl; scale=scale) + + @test props.volume ≈ volume rtol = 0.01 + end + end end -- 2.51.2 From d9fc6f302a17c1208e4442320c07fe8d239fda49 Mon Sep 17 00:00:00 2001 From: Anson Date: Thu, 31 Mar 2022 10:12:47 -0700 Subject: [PATCH 09/24] new volume function based on mass props --- MisterBiggs/stl-process/src/stlProcess.jl | 67 ++++++++++------------- MisterBiggs/stl-process/test/runtests.jl | 44 ++++++++++++++- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index ad736eaef..0a3ef0fa9 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -16,43 +16,6 @@ struct Properties end end -function get_volume(triangles; scale=1) - """ - Reference: - https://people.eecs.berkeley.edu/~wkahan/VtetLang.pdf - """ - - volume = 0.0 - for tri in triangles - a, b, c = tri .* scale - - W = sqrt(sum((a .- b) .^ 2)) - V = sqrt(sum((b .- c) .^ 2)) - U = sqrt(sum((c .- a) .^ 2)) - - v = sqrt(sum(a .^ 2)) - u = sqrt(sum(b .^ 2)) - w = sqrt(sum(c .^ 2)) - - X = (w - U + v) * (U + v + w) - Y = (u - V + w) * (V + w + u) - Z = (v - W + u) * (W + u + v) - - x = (U - v + w) * (v - w + U) - y = (V - w + u) * (w - u + V) - z = (W - u + v) * (u - v + W) - - ξ = sqrt(x * Y * Z) - η = sqrt(y * Z * X) - ζ = sqrt(z * X * Y) - λ = sqrt(x * y * z) - - volume += sqrt((ξ + η + ζ - λ) * (λ + ξ + η - ζ) * (η + ζ + λ - ξ) * (ζ + λ + ξ - η)) / (192 * u * v * w) - end - - return volume -end - function get_mass_properties(triangles; scale=1) """ Reference: @@ -113,6 +76,34 @@ function get_mass_properties(triangles; scale=1) return Properties(volume, center_of_gravity, inertia) end -export get_mass_properties, get_volume +function fast_volume(triangles; scale=1) + """ + Faster algorithm to get volume of mesh. Is just `get_mass_properties` without any calculations not relevant to volume. + + Reference: + https://github.com/WoLpH/numpy-stl/blob/42b6c67324e13b8712af6730f77e5e9544ef63b0/stl/base.py#L362 + https://www.geometrictools.com/Documentation/PolyhedralMassProperties.pdf + """ + x = reduce(hcat, [[v[1] .* scale for v in tri] for tri in triangles])' + y = reduce(hcat, [[v[2] .* scale for v in tri] for tri in triangles])' + z = reduce(hcat, [[v[3] .* scale for v in tri] for tri in triangles])' + + w0, w1, w2 = x[:, 1], x[:, 2], x[:, 3] + temp0 = w0 + w1 + f1 = temp0 + w2 + + x0, x1, x2 = x[:, 1], x[:, 2], x[:, 3] + y0, y1, y2 = y[:, 1], y[:, 2], y[:, 3] + z0, z1, z2 = z[:, 1], z[:, 2], z[:, 3] + a1, b1, c1 = x1 - x0, y1 - y0, z1 - z0 + a2, b2, c2 = x2 - x0, y2 - y0, z2 - z0 + d0, d1, d2 = b1 .* c2 .- b2 .* c1, a2 .* c1 .- a1 .* c2, a1 .* b2 .- a2 .* b1 + + volume = sum(d0 .* f1) / 6 + + return volume +end + +export get_mass_properties, fast_volume end # module diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index ff3c121b5..dedf753c6 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -7,6 +7,44 @@ using MeshIO using LinearAlgebra +function _check_volume(triangles; scale=1) + """ + Slow algorithm just used to test the other algorithms + Reference: + https://people.eecs.berkeley.edu/~wkahan/VtetLang.pdf + """ + + volume = 0.0 + for tri in triangles + a, b, c = tri .* scale + + W = sqrt(sum((a .- b) .^ 2)) + V = sqrt(sum((b .- c) .^ 2)) + U = sqrt(sum((c .- a) .^ 2)) + + v = sqrt(sum(a .^ 2)) + u = sqrt(sum(b .^ 2)) + w = sqrt(sum(c .^ 2)) + + X = (w - U + v) * (U + v + w) + Y = (u - V + w) * (V + w + u) + Z = (v - W + u) * (W + u + v) + + x = (U - v + w) * (v - w + U) + y = (V - w + u) * (w - u + V) + z = (W - u + v) * (u - v + W) + + ξ = sqrt(x * Y * Z) + η = sqrt(y * Z * X) + ζ = sqrt(z * X * Y) + λ = sqrt(x * y * z) + + volume += sqrt((ξ + η + ζ - λ) * (λ + ξ + η - ζ) * (η + ζ + λ - ξ) * (ζ + λ + ξ - η)) / (192 * u * v * w) + end + + return volume +end + @testset "simple cube stl" begin """ inertia math: @@ -25,14 +63,14 @@ using LinearAlgebra @test all(eigvals(props.inertia) .≈ (5.0 + 1 / 3)) @testset "get_volume function" begin - @test get_volume(stl; scale=1) ≈ props.volume rtol = 0.01 - @test get_volume(stl; scale=4) ≈ (4 * 2)^3 rtol = 0.01 + @test _check_volume(stl; scale=1) ≈ props.volume rtol = 0.01 + @test _check_volume(stl; scale=4) ≈ (4 * 2)^3 rtol = 0.01 end end @testset "Compare volumes with scaling" begin for scale in 1:5 props = get_mass_properties(stl; scale=scale) - volume = get_volume(stl; scale=scale) + volume = _check_volume(stl; scale=scale) @test props.volume ≈ volume rtol = 0.01 end -- 2.51.2 From 31df4fa1949df99bd11cb1d8368f592baff00167 Mon Sep 17 00:00:00 2001 From: Anson Date: Sat, 2 Apr 2022 12:17:45 -0700 Subject: [PATCH 10/24] added function to find desired scale --- MisterBiggs/stl-process/Manifest.toml | 33 +++++++++++++++++++++++ MisterBiggs/stl-process/Project.toml | 1 + MisterBiggs/stl-process/src/stlProcess.jl | 7 ++++- MisterBiggs/stl-process/test/runtests.jl | 5 +++- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/MisterBiggs/stl-process/Manifest.toml b/MisterBiggs/stl-process/Manifest.toml index 4d223514f..47756e3d0 100644 --- a/MisterBiggs/stl-process/Manifest.toml +++ b/MisterBiggs/stl-process/Manifest.toml @@ -24,10 +24,21 @@ git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" version = "0.11.0" +[[deps.CommonSolve]] +git-tree-sha1 = "68a0743f578349ada8bc911a5cbd5a2ef6ed6d1f" +uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" +version = "0.2.0" + [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +[[deps.ConstructionBase]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "f74e9d5388b8620b4cee35d4c5a618dd4dc547f4" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.3.0" + [[deps.DataAPI]] git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" @@ -64,6 +75,10 @@ git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.4" +[[deps.Future]] +deps = ["Random"] +uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" + [[deps.GeometryBasics]] deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] git-tree-sha1 = "83ea630384a13fc4f002b77690bc0afeb4255ac9" @@ -116,6 +131,12 @@ uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +[[deps.MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.9" + [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" @@ -173,12 +194,24 @@ git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" +[[deps.Roots]] +deps = ["CommonSolve", "Printf", "Setfield"] +git-tree-sha1 = "6085b8ac184add45b586ed8d74468310948dcfe8" +uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" +version = "1.4.0" + [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +[[deps.Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "Requires"] +git-tree-sha1 = "38d88503f695eb0301479bc9b0d4320b378bafe5" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "0.8.2" + [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml index 0d0cf8af2..299357c7e 100644 --- a/MisterBiggs/stl-process/Project.toml +++ b/MisterBiggs/stl-process/Project.toml @@ -6,3 +6,4 @@ version = "0.1.0" [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" MeshIO = "7269a6da-0436-5bbc-96c2-40638cbb6118" +Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index 0a3ef0fa9..b2865390a 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -2,6 +2,7 @@ module stlProcess using MeshIO using FileIO +using Roots struct Properties volume::Float64 @@ -104,6 +105,10 @@ function fast_volume(triangles; scale=1) return volume end -export get_mass_properties, fast_volume +function find_scale(trianges; desired_volume=1) + return find_zero(scale -> fast_volume(trianges; scale=scale) - desired_volume, 2.0) +end + +export get_mass_properties, fast_volume, find_scale end # module diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index dedf753c6..17eed1867 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -9,7 +9,7 @@ using LinearAlgebra function _check_volume(triangles; scale=1) """ - Slow algorithm just used to test the other algorithms + Slow, inaccurate algorithm just used to test the other algorithms Reference: https://people.eecs.berkeley.edu/~wkahan/VtetLang.pdf """ @@ -75,4 +75,7 @@ end @test props.volume ≈ volume rtol = 0.01 end end + @testset "Find Scale" begin + @test fast_volume(stl; scale=find_scale(stl)) == 1.0 + end end -- 2.51.2 From f2fe721fc00734d63afd22e96cbee5ee2e0ff61e Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Tue, 5 Apr 2022 16:43:36 -0700 Subject: [PATCH 11/24] made tests like way better --- MisterBiggs/stl-process/src/stlProcess.jl | 2 +- MisterBiggs/stl-process/test/runtests.jl | 67 ++++++++++-------- .../test/test_assets/2_4_8_cuboid.stl | Bin 0 -> 684 bytes .../stl-process/test/test_assets/README.md | 27 +++++++ .../test_assets/{cubeblender.stl => cube.stl} | Bin .../stl-process/test/test_assets/cylinder.stl | Bin 0 -> 6284 bytes .../test/test_assets/slender_y.stl | Bin 0 -> 6284 bytes .../stl-process/test/test_assets/sphere.stl | Bin 0 -> 48084 bytes 8 files changed, 64 insertions(+), 32 deletions(-) create mode 100644 MisterBiggs/stl-process/test/test_assets/2_4_8_cuboid.stl create mode 100644 MisterBiggs/stl-process/test/test_assets/README.md rename MisterBiggs/stl-process/test/test_assets/{cubeblender.stl => cube.stl} (100%) create mode 100644 MisterBiggs/stl-process/test/test_assets/cylinder.stl create mode 100644 MisterBiggs/stl-process/test/test_assets/slender_y.stl create mode 100644 MisterBiggs/stl-process/test/test_assets/sphere.stl diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index b2865390a..1d036c31c 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -74,7 +74,7 @@ function get_mass_properties(triangles; scale=1) inertia[2, 3] = inertia[3, 2] = -(intg[9] - volume .* center_of_gravity[2] .* center_of_gravity[3]) inertia[1, 3] = inertia[3, 1] = -(intg[10] - volume .* center_of_gravity[3] .* center_of_gravity[1]) - return Properties(volume, center_of_gravity, inertia) + return Properties(volume, center_of_gravity, inertia ./ volume) end function fast_volume(triangles; scale=1) diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index 17eed1867..e9b95187a 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -1,6 +1,7 @@ using Test using stlProcess +import stlProcess.Properties using FileIO using MeshIO @@ -45,37 +46,41 @@ function _check_volume(triangles; scale=1) return volume end -@testset "simple cube stl" begin - """ - inertia math: - https://hepweb.ucsd.edu/ph110b/110b_notes/node26.html - - Cube is a standard cube with a length of 2 for each side. - """ - cube_path = raw"test_assets\cubeblender.stl" - stl = load(cube_path) - - @testset "get_mass_properties function" begin - props = get_mass_properties(stl) - - @test props.volume == 8.0 - @test all(props.center_of_gravity .== 0.0) - @test all(eigvals(props.inertia) .≈ (5.0 + 1 / 3)) - - @testset "get_volume function" begin - @test _check_volume(stl; scale=1) ≈ props.volume rtol = 0.01 - @test _check_volume(stl; scale=4) ≈ (4 * 2)^3 rtol = 0.01 +@testset "3D Models" begin + I_mat = Matrix(I, 3, 3) + center = [0, 0, 0] + + models = Dict( + # Inertia math: https://en.wikipedia.org/wiki/List_of_moments_of_inertia#List_of_3D_inertia_tensors + # Properties(volume, center_of_gravity, inertia) + "cube.stl" => Properties(2.0^3, center, I_mat .* 2^2 / 6), # l = 2 + "sphere.stl" => Properties(4 / 3 * pi, center, I_mat .* 2 / 5), # r = 1 + "2_4_8_cuboid.stl" => Properties(2 * 4 * 8, center, diagm([2^2 + 4^2, 8^2 + 2^2, 8^2 + 4^2]) ./ 12), # h, d, w = 2, 4, 8 + "slender_y.stl" => Properties(10 * π * 0.1^2, center, diagm([1, 0, 1]) .* (10^2 / 12)), # l_z = 10, r = 0.1 + "cylinder.stl" => Properties(10 * π * 1^2, center, diagm([(3 + 10^2) / 12, (3 + 10^2) / 12, 1^2 / 2])), # l_z = 10, r = 1 + ) + @testset "$model" for (model, control) in models + path = "test_assets\\$model" + stl = load(path) + + @testset "get_mass_properties" begin + props = get_mass_properties(stl) + + @test props.volume ≈ control.volume atol = 0.5 + @test props.center_of_gravity ≈ control.center_of_gravity atol = 0.01 + @test eigvals(props.inertia) ≈ eigvals(control.inertia) atol = 0.1 end - end - @testset "Compare volumes with scaling" begin - for scale in 1:5 - props = get_mass_properties(stl; scale=scale) - volume = _check_volume(stl; scale=scale) - - @test props.volume ≈ volume rtol = 0.01 + @testset "Compare volumes with scaling" begin + for scale in 1:5 + props = get_mass_properties(stl; scale=scale) + volume = _check_volume(stl; scale=scale) + @test props.volume ≈ volume atol = 0.1 + end + end + @testset "find_volume" begin + for scale in [0.5, 1, 1.0, 10, 2 / 3] + @test fast_volume(stl; scale=find_scale(stl; desired_volume=scale)) ≈ scale + end end end - @testset "Find Scale" begin - @test fast_volume(stl; scale=find_scale(stl)) == 1.0 - end -end +end \ No newline at end of file diff --git a/MisterBiggs/stl-process/test/test_assets/2_4_8_cuboid.stl b/MisterBiggs/stl-process/test/test_assets/2_4_8_cuboid.stl new file mode 100644 index 0000000000000000000000000000000000000000..f07d1da3a97a705fafafcc7d569860b9a23a3b89 GIT binary patch literal 684 zcmZ>*D9A4=Nlj5mE6UGRaLP%|OGz!#HP$oKGh!eI@PIWn><3d|0?av~0|3E9ZubBH literal 0 HcmV?d00001 diff --git a/MisterBiggs/stl-process/test/test_assets/README.md b/MisterBiggs/stl-process/test/test_assets/README.md new file mode 100644 index 000000000..eaee96aa4 --- /dev/null +++ b/MisterBiggs/stl-process/test/test_assets/README.md @@ -0,0 +1,27 @@ +# Test Assets + +## cube.stl + +Perfect cube with length of 2 for all sides and a center of gravity at origin. + +## 2_4_8_cuboid.stl + +Cube with dimensions: + +- height = 2 +- depth = 4 +- width = 8 + +Center of gravity at origin. + +## sphere.stl + +Perfect sphere with radius of 1 and a center of gravity at origin. + +## slender_rod.stl + +Negligible thickness cylinder with length of 10, radius of 0.1, and a center of gravity at origin. + +## cylinder.stl + +Cylinder with length of 10, radius of 1, and a center of gravity at origin. diff --git a/MisterBiggs/stl-process/test/test_assets/cubeblender.stl b/MisterBiggs/stl-process/test/test_assets/cube.stl similarity index 100% rename from MisterBiggs/stl-process/test/test_assets/cubeblender.stl rename to MisterBiggs/stl-process/test/test_assets/cube.stl diff --git a/MisterBiggs/stl-process/test/test_assets/cylinder.stl b/MisterBiggs/stl-process/test/test_assets/cylinder.stl new file mode 100644 index 0000000000000000000000000000000000000000..7a532b672283df6a59da98db37535625a0d8881d GIT binary patch literal 6284 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𝔄`rVLXgZKp!Tba= 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 literal 0 HcmV?d00001 -- 2.51.2 From cd8dc2fb1e435064a6c538622833178861600f4c Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Tue, 5 Apr 2022 23:46:14 +0000 Subject: [PATCH 12/24] add testing --- MisterBiggs/stl-process/.gitlab-ci.yml | 80 ++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 MisterBiggs/stl-process/.gitlab-ci.yml diff --git a/MisterBiggs/stl-process/.gitlab-ci.yml b/MisterBiggs/stl-process/.gitlab-ci.yml new file mode 100644 index 000000000..b5ec13dbe --- /dev/null +++ b/MisterBiggs/stl-process/.gitlab-ci.yml @@ -0,0 +1,80 @@ +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Julia.gitlab-ci.yml + +# This is an example .gitlab-ci.yml file to test (and optionally report the coverage +# results of) your [Julia][1] packages. Please refer to the [documentation][2] +# for more information about package development in Julia. +# +# Here, it is assumed that your Julia package is named `MyPackage`. Change it to +# whatever name you have given to your package. +# +# [1]: http://julialang.org/ +# [2]: https://docs.julialang.org/en/v1/manual/documentation/index.html + +# Below is the template to run your tests in Julia +.test_template: &test_definition + # Uncomment below (and adjust as needed) to run the tests for specific references + # only, such as the default branch, a `development` branch, and so on: + # rules: + # - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + # - if: $CI_COMMIT_BRANCH == "development" + script: + # Let's run the tests. Substitute `coverage = false` below, if you do not + # want coverage results. + - julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("MyPackage"); Pkg.test("MyPackage"; coverage = true)' + # Comment out below if you do not want coverage results. + - julia -e 'using Pkg; Pkg.add("Coverage"); + import MyPackage; cd(joinpath(dirname(pathof(MyPackage)), "..")); + using Coverage; cl, tl = get_summary(process_folder()); + println("(", cl/tl*100, "%) covered")' + +# Name a test and select an appropriate image. +# images comes from Docker hub +test:1.7: + image: julia:1.7 + <<: *test_definition + +test:1.8: + image: julia:1.8 + <<: *test_definition + +# Maybe you would like to test your package against the development branch: +# test:1.1-dev (not sure there is such an image in docker, so not tested yet): +# image: julia:v1.1-dev +# # ... allowing for failures, since we are testing against the development +# # branch: +# allow_failure: true +# <<: *test_definition + +# REMARK: Do not forget to enable the coverage feature for your project, if you +# are using code coverage reporting above. This can be done by +# +# - Navigating to the `CI/CD Pipelines` settings of your project, +# - Copying and pasting the default `Simplecov` regex example provided, i.e., +# `\(\d+.\d+\%\) covered` in the `test coverage parsing` textfield. + +# Example documentation deployment +# pages: +# image: julia:0.7 +# stage: deploy +# script: +# - apt-get update -qq && apt-get install -y git # needed by Documenter +# - julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("MyPackage");' # rebuild Julia (can be put somewhere else I'm sure +# - julia -e 'using Pkg; import MyPackage; Pkg.add("Documenter")' # install Documenter +# - julia --color=yes docs/make.jl # make documentation +# - mv docs/build public # move to the directory picked up by Gitlab pages +# artifacts: +# paths: +# - public +# rules: +# - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + +# WARNING: This template is using the `julia` images from [Docker +# Hub][3]. One can use custom Julia images and/or the official ones found +# in the same place. However, care must be taken to correctly locate the binary +# file (`/opt/julia/bin/julia` above), which is usually given on the image's +# description page. +# +# [3]: https://hub.docker.com/_/julia/ -- 2.51.2 From 838bc724f1b9eeb89229576f1fdbec423addbbd4 Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Tue, 5 Apr 2022 16:50:53 -0700 Subject: [PATCH 13/24] see if ci likes this --- MisterBiggs/stl-process/.gitlab-ci.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/MisterBiggs/stl-process/.gitlab-ci.yml b/MisterBiggs/stl-process/.gitlab-ci.yml index b5ec13dbe..da96c3d81 100644 --- a/MisterBiggs/stl-process/.gitlab-ci.yml +++ b/MisterBiggs/stl-process/.gitlab-ci.yml @@ -20,15 +20,13 @@ # rules: # - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # - if: $CI_COMMIT_BRANCH == "development" - script: - # Let's run the tests. Substitute `coverage = false` below, if you do not - # want coverage results. - - julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("MyPackage"); Pkg.test("MyPackage"; coverage = true)' - # Comment out below if you do not want coverage results. - - julia -e 'using Pkg; Pkg.add("Coverage"); - import MyPackage; cd(joinpath(dirname(pathof(MyPackage)), "..")); - using Coverage; cl, tl = get_summary(process_folder()); - println("(", cl/tl*100, "%) covered")' + script: | + julia --project -e ' + using Pkg; + Pkg.instantiate(); + Pkg.build(); + Pkg.test(; coverage=true);' + interruptible: true # Name a test and select an appropriate image. # images comes from Docker hub @@ -37,7 +35,7 @@ test:1.7: <<: *test_definition test:1.8: - image: julia:1.8 + image: julia:1.8-rc <<: *test_definition # Maybe you would like to test your package against the development branch: -- 2.51.2 From 585c800f340b3ea04b0f45f6cfbd15260557d6c4 Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Tue, 5 Apr 2022 16:54:08 -0700 Subject: [PATCH 14/24] ci madness --- MisterBiggs/stl-process/test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index e9b95187a..dc190bcec 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -60,7 +60,7 @@ end "cylinder.stl" => Properties(10 * π * 1^2, center, diagm([(3 + 10^2) / 12, (3 + 10^2) / 12, 1^2 / 2])), # l_z = 10, r = 1 ) @testset "$model" for (model, control) in models - path = "test_assets\\$model" + path = "test_assets/$model" stl = load(path) @testset "get_mass_properties" begin -- 2.51.2 From 3bce075ab743943104a573ffe5defaa4d1c77c9b Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Tue, 5 Apr 2022 17:05:58 -0700 Subject: [PATCH 15/24] try to get code coverage to work --- MisterBiggs/stl-process/.gitlab-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MisterBiggs/stl-process/.gitlab-ci.yml b/MisterBiggs/stl-process/.gitlab-ci.yml index da96c3d81..5da277efb 100644 --- a/MisterBiggs/stl-process/.gitlab-ci.yml +++ b/MisterBiggs/stl-process/.gitlab-ci.yml @@ -27,6 +27,13 @@ Pkg.build(); Pkg.test(; coverage=true);' interruptible: true + artifacts: + when: always + paths: + - ./*.cov + - ./*/*.cov + - ./*/*/*.cov + - ./*/*/*/*.cov # Name a test and select an appropriate image. # images comes from Docker hub -- 2.51.2 From 1205fe38beb4102e9f6af6cbb8c060231935a3f4 Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Tue, 5 Apr 2022 17:12:38 -0700 Subject: [PATCH 16/24] more coverage stuff --- MisterBiggs/stl-process/.gitlab-ci.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/MisterBiggs/stl-process/.gitlab-ci.yml b/MisterBiggs/stl-process/.gitlab-ci.yml index 5da277efb..901fb1d4f 100644 --- a/MisterBiggs/stl-process/.gitlab-ci.yml +++ b/MisterBiggs/stl-process/.gitlab-ci.yml @@ -13,6 +13,10 @@ # [1]: http://julialang.org/ # [2]: https://docs.julialang.org/en/v1/manual/documentation/index.html +stages: + - test + - post + # Below is the template to run your tests in Julia .test_template: &test_definition # Uncomment below (and adjust as needed) to run the tests for specific references @@ -35,6 +39,20 @@ - ./*/*/*.cov - ./*/*/*/*.cov +.coverage: + stage: post + script: | + # Coverage(Tools).jl scans all .jl files but easily fails at that, + # so make sure we exclude hidden directories (like the depot, .git, ...) + julia -e ' + using Pkg + Pkg.add("Coverage") + using Coverage + dirs = filter(entry -> isdir(entry) && !startswith(entry, "."), readdir()) + coverage = vcat(map(process_folder, dirs)...) + Codecov.submit_local(coverage)' + interruptible: true + # Name a test and select an appropriate image. # images comes from Docker hub test:1.7: -- 2.51.2 From cfa976c1e91020e70263294d2cf1b76b9f923816 Mon Sep 17 00:00:00 2001 From: Anson Date: Sat, 9 Apr 2022 11:51:34 -0700 Subject: [PATCH 17/24] added surface area calculation --- MisterBiggs/stl-process/Manifest.toml | 30 ++++++++++++++++++++--- MisterBiggs/stl-process/Project.toml | 1 + MisterBiggs/stl-process/src/stlProcess.jl | 10 +++++--- MisterBiggs/stl-process/test/runtests.jl | 17 +++++++------ 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/MisterBiggs/stl-process/Manifest.toml b/MisterBiggs/stl-process/Manifest.toml index 47756e3d0..b703e1c1a 100644 --- a/MisterBiggs/stl-process/Manifest.toml +++ b/MisterBiggs/stl-process/Manifest.toml @@ -1,7 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.7.2" +julia_version = "1.8.0-beta3" manifest_format = "2.0" +project_hash = "19dd795ccf3365426e09536ac7ad8bf96e8ee766" [[deps.Adapt]] deps = ["LinearAlgebra"] @@ -11,6 +12,7 @@ version = "3.3.3" [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" [[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" @@ -32,6 +34,7 @@ version = "0.2.0" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "0.5.2+0" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] @@ -54,8 +57,9 @@ deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" [[deps.Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" [[deps.EarCut_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -69,6 +73,9 @@ git-tree-sha1 = "80ced645013a5dbdc52cf70329399c35ce007fae" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" version = "1.13.0" +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + [[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" @@ -108,10 +115,12 @@ version = "1.4.1" [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.3" [[deps.LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "7.81.0+0" [[deps.LibGit2]] deps = ["Base64", "NetworkOptions", "Printf", "SHA"] @@ -120,6 +129,7 @@ uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" [[deps.LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.10.2+0" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -144,6 +154,7 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.0+0" [[deps.MeshIO]] deps = ["ColorTypes", "FileIO", "GeometryBasics", "Printf"] @@ -153,13 +164,16 @@ version = "0.4.10" [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2022.2.1" [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.20+0" [[deps.OrderedCollections]] git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" @@ -169,6 +183,7 @@ version = "1.4.1" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.8.0" [[deps.Preferences]] deps = ["TOML"] @@ -196,12 +211,13 @@ version = "1.3.0" [[deps.Roots]] deps = ["CommonSolve", "Printf", "Setfield"] -git-tree-sha1 = "6085b8ac184add45b586ed8d74468310948dcfe8" +git-tree-sha1 = "e382260f6482c27b5062eba923e36fde2f5ab0b9" uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" -version = "1.4.0" +version = "2.0.0" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -238,6 +254,7 @@ version = "0.6.5" [[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.0" [[deps.TableTraits]] deps = ["IteratorInterfaceExtensions"] @@ -254,6 +271,7 @@ version = "1.7.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] @@ -269,15 +287,19 @@ uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.12+1" [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.1.0+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.41.0+1" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "16.2.1+1" diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml index 299357c7e..67657ffd2 100644 --- a/MisterBiggs/stl-process/Project.toml +++ b/MisterBiggs/stl-process/Project.toml @@ -5,5 +5,6 @@ version = "0.1.0" [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MeshIO = "7269a6da-0436-5bbc-96c2-40638cbb6118" Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index 1d036c31c..489710ffe 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -3,17 +3,19 @@ module stlProcess using MeshIO using FileIO using Roots +using LinearAlgebra struct Properties volume::Float64 center_of_gravity::Vector{Float64} inertia::Matrix{Float64} + surface_area::Float64 - function Properties(volume, center_of_gravity, inertia) + function Properties(volume, center_of_gravity, inertia,surface_area) @assert size(center_of_gravity) == (3,) @assert size(inertia) == (3, 3) - return new(volume, center_of_gravity, inertia) + return new(volume, center_of_gravity, inertia, surface_area) end end @@ -74,7 +76,9 @@ function get_mass_properties(triangles; scale=1) inertia[2, 3] = inertia[3, 2] = -(intg[9] - volume .* center_of_gravity[2] .* center_of_gravity[3]) inertia[1, 3] = inertia[3, 1] = -(intg[10] - volume .* center_of_gravity[3] .* center_of_gravity[1]) - return Properties(volume, center_of_gravity, inertia ./ volume) + surface_area = norm.(eachrow([x0 y0 z0] - [x1 y1 z1]) .× eachrow([x1 y1 z1] - [x2 y2 z2])) / 2 |> sum + + return Properties(volume, center_of_gravity, inertia ./ volume, surface_area) end function fast_volume(triangles; scale=1) diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index dc190bcec..9f9227ea9 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -53,24 +53,27 @@ end models = Dict( # Inertia math: https://en.wikipedia.org/wiki/List_of_moments_of_inertia#List_of_3D_inertia_tensors # Properties(volume, center_of_gravity, inertia) - "cube.stl" => Properties(2.0^3, center, I_mat .* 2^2 / 6), # l = 2 - "sphere.stl" => Properties(4 / 3 * pi, center, I_mat .* 2 / 5), # r = 1 - "2_4_8_cuboid.stl" => Properties(2 * 4 * 8, center, diagm([2^2 + 4^2, 8^2 + 2^2, 8^2 + 4^2]) ./ 12), # h, d, w = 2, 4, 8 - "slender_y.stl" => Properties(10 * π * 0.1^2, center, diagm([1, 0, 1]) .* (10^2 / 12)), # l_z = 10, r = 0.1 - "cylinder.stl" => Properties(10 * π * 1^2, center, diagm([(3 + 10^2) / 12, (3 + 10^2) / 12, 1^2 / 2])), # l_z = 10, r = 1 + "cube.stl" => Properties(2.0^3, center, I_mat .* 2^2 / 6, 6 * 2^2), # l = 2 + "sphere.stl" => Properties(4 / 3 * pi, center, I_mat .* 2 / 5, 4π), # r = 1 + "2_4_8_cuboid.stl" => + Properties(2 * 4 * 8, center, diagm([2^2 + 4^2, 8^2 + 2^2, 8^2 + 4^2]) ./ 12, 2(2 * 4 + 2 * 8 + 4 * 8)), # h, d, w = 2, 4, 8 + "slender_y.stl" => Properties(10 * π * 0.1^2, center, diagm([1, 0, 1]) .* (10^2 / 12), 2π * 0.05 * (0.1 + 10)), # l_z = 10, r = 0.1 + "cylinder.stl" => + Properties(10 * π * 1^2, center, diagm([(3 + 10^2) / 12, (3 + 10^2) / 12, 1^2 / 2]), 2π * 1 * (1 + 10)), # l_z = 10, r = 1 ) @testset "$model" for (model, control) in models path = "test_assets/$model" stl = load(path) - @testset "get_mass_properties" begin + @testset "get_mass_properties for $model" begin props = get_mass_properties(stl) @test props.volume ≈ control.volume atol = 0.5 @test props.center_of_gravity ≈ control.center_of_gravity atol = 0.01 @test eigvals(props.inertia) ≈ eigvals(control.inertia) atol = 0.1 + @test props.surface_area ≈ control.surface_area atol = 0.5 end - @testset "Compare volumes with scaling" begin + @testset "Compare volumes with scaling for $model" begin for scale in 1:5 props = get_mass_properties(stl; scale=scale) volume = _check_volume(stl; scale=scale) -- 2.51.2 From f6248f7235979ee76560c0ee89fcea35e8f7023a Mon Sep 17 00:00:00 2001 From: Anson Date: Sat, 9 Apr 2022 12:04:07 -0700 Subject: [PATCH 18/24] add comments --- MisterBiggs/stl-process/src/stlProcess.jl | 5 +++-- MisterBiggs/stl-process/test/runtests.jl | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index 489710ffe..98bdf01a3 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -11,7 +11,7 @@ struct Properties inertia::Matrix{Float64} surface_area::Float64 - function Properties(volume, center_of_gravity, inertia,surface_area) + function Properties(volume, center_of_gravity, inertia, surface_area) @assert size(center_of_gravity) == (3,) @assert size(inertia) == (3, 3) @@ -76,7 +76,8 @@ function get_mass_properties(triangles; scale=1) inertia[2, 3] = inertia[3, 2] = -(intg[9] - volume .* center_of_gravity[2] .* center_of_gravity[3]) inertia[1, 3] = inertia[3, 1] = -(intg[10] - volume .* center_of_gravity[3] .* center_of_gravity[1]) - surface_area = norm.(eachrow([x0 y0 z0] - [x1 y1 z1]) .× eachrow([x1 y1 z1] - [x2 y2 z2])) / 2 |> sum + # https://math.stackexchange.com/questions/128991/how-to-calculate-the-area-of-a-3d-triangle + surface_area = sum(norm.(eachrow([x0 y0 z0] - [x1 y1 z1]) .× eachrow([x1 y1 z1] - [x2 y2 z2])) / 2) return Properties(volume, center_of_gravity, inertia ./ volume, surface_area) end diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index 9f9227ea9..d5ca289b0 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -52,7 +52,7 @@ end models = Dict( # Inertia math: https://en.wikipedia.org/wiki/List_of_moments_of_inertia#List_of_3D_inertia_tensors - # Properties(volume, center_of_gravity, inertia) + # Properties(volume, center_of_gravity, inertia, surface_area) "cube.stl" => Properties(2.0^3, center, I_mat .* 2^2 / 6, 6 * 2^2), # l = 2 "sphere.stl" => Properties(4 / 3 * pi, center, I_mat .* 2 / 5, 4π), # r = 1 "2_4_8_cuboid.stl" => -- 2.51.2 From bf77dc2d66729dbf8a0a5380d9cba23c4445f51d Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Tue, 12 Apr 2022 04:39:21 +0000 Subject: [PATCH 19/24] Calculate Characteristic Length --- MisterBiggs/stl-process/Project.toml | 2 +- MisterBiggs/stl-process/src/stlProcess.jl | 72 +++++++++++++++++- MisterBiggs/stl-process/test/runtests.jl | 35 ++++++--- .../stl-process/test/test_assets/cylinder.stl | Bin 6284 -> 148884 bytes .../test/test_assets/slender_y.stl | Bin 6284 -> 148884 bytes 5 files changed, 95 insertions(+), 14 deletions(-) diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml index 67657ffd2..3913d8bc9 100644 --- a/MisterBiggs/stl-process/Project.toml +++ b/MisterBiggs/stl-process/Project.toml @@ -1,7 +1,7 @@ name = "stlProcess" uuid = "68914fc9-42cf-4b37-b06f-0b65edf9b8fa" authors = ["Anson "] -version = "0.1.0" +version = "0.2.0" [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index 98bdf01a3..7d39bb51b 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -10,16 +10,17 @@ struct Properties center_of_gravity::Vector{Float64} inertia::Matrix{Float64} surface_area::Float64 + characteristic_length::Float64 - function Properties(volume, center_of_gravity, inertia, surface_area) + function Properties(volume, center_of_gravity, inertia, surface_area, characteristic_length) @assert size(center_of_gravity) == (3,) @assert size(inertia) == (3, 3) - return new(volume, center_of_gravity, inertia, surface_area) + return new(volume, center_of_gravity, inertia, surface_area, characteristic_length) end end -function get_mass_properties(triangles; scale=1) +function get_mass_properties(triangles; scale=1.0) """ Reference: https://github.com/WoLpH/numpy-stl/blob/42b6c67324e13b8712af6730f77e5e9544ef63b0/stl/base.py#L362 @@ -79,7 +80,70 @@ function get_mass_properties(triangles; scale=1) # https://math.stackexchange.com/questions/128991/how-to-calculate-the-area-of-a-3d-triangle surface_area = sum(norm.(eachrow([x0 y0 z0] - [x1 y1 z1]) .× eachrow([x1 y1 z1] - [x2 y2 z2])) / 2) - return Properties(volume, center_of_gravity, inertia ./ volume, surface_area) + characteristic_length = calc_characteristic_length(triangles, inertia, center_of_gravity, scale) + + return Properties(volume, center_of_gravity, inertia ./ volume, surface_area, characteristic_length) +end + +function calc_characteristic_length(triangles, inertia, center_of_gravity, scale; θtol=0.1) + """ + Calculate the Characteristic Length using eigenvectors. + + More information on characteristic length: https://ntrs.nasa.gov/api/citations/20090019123/downloads/20090019123.pdf + More information on the geometry used: https://math.stackexchange.com/q/100447 + """ + + eigs = eigvecs(inertia) + points = collect(Set(reduce(vcat, [[Array(Float64.(v)) .* scale for v in tri] for tri in triangles]))) + + function θs_calc(ref, points) + """ + Calculates the angle between a reference vector and an array of points. + """ + norm_ref = norm(ref) + return map( + pt -> + acos(clamp(dot(ref, pt .- center_of_gravity) / (norm(pt .- center_of_gravity) * norm_ref), -1.0, 1.0)), + points, + ) + end + + characteristic_points = [] + # find the characteristic points for each eigenvector + for eig in eachrow(eigs) + + # Find 3 points for each direction of the eigenvector + θs = θs_calc(eig, points) + sort_index = sortperm(θs) + min_points = points[sort_index[1:3]] + max_points = points[sort_index[(end - 2):end]] + + # Create a parameterized function using the eigenvector and center_of_gravity + line_func(t) = center_of_gravity .+ eig .* t + + # create a plane using 3 points, then find where the parameterized function intercepts it + max_normal = (max_points[1] - max_points[2]) × (max_points[2] - max_points[3]) + max_t = find_zero(t -> let + l = line_func(t) + sum(max_normal .* (l .- max_points[1])) + end, norm(eig)) + + max_point = line_func(max_t) + + # Build a plane in the opposite direction, and find the intercept again + min_normal = (min_points[1] - min_points[2]) × (min_points[2] - min_points[3]) + min_t = find_zero(t -> let + l = line_func(t) + sum(min_normal .* (l .- min_points[1])) + end, -norm(eig)) + + min_point = line_func(min_t) + + push!(characteristic_points, (min_point, max_point)) + end + + # Calculate the characteristic length. + return sum([sqrt(sum((max - min) .^ 2)) for (min, max) in characteristic_points]) / 3.0 end function fast_volume(triangles; scale=1) diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index d5ca289b0..afb5ace3e 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -52,14 +52,30 @@ end models = Dict( # Inertia math: https://en.wikipedia.org/wiki/List_of_moments_of_inertia#List_of_3D_inertia_tensors - # Properties(volume, center_of_gravity, inertia, surface_area) - "cube.stl" => Properties(2.0^3, center, I_mat .* 2^2 / 6, 6 * 2^2), # l = 2 - "sphere.stl" => Properties(4 / 3 * pi, center, I_mat .* 2 / 5, 4π), # r = 1 - "2_4_8_cuboid.stl" => - Properties(2 * 4 * 8, center, diagm([2^2 + 4^2, 8^2 + 2^2, 8^2 + 4^2]) ./ 12, 2(2 * 4 + 2 * 8 + 4 * 8)), # h, d, w = 2, 4, 8 - "slender_y.stl" => Properties(10 * π * 0.1^2, center, diagm([1, 0, 1]) .* (10^2 / 12), 2π * 0.05 * (0.1 + 10)), # l_z = 10, r = 0.1 - "cylinder.stl" => - Properties(10 * π * 1^2, center, diagm([(3 + 10^2) / 12, (3 + 10^2) / 12, 1^2 / 2]), 2π * 1 * (1 + 10)), # l_z = 10, r = 1 + # Properties(volume, center_of_gravity, inertia, surface_area, characteristic_length) + "cube.stl" => Properties(2.0^3, center, I_mat .* 2^2 / 6, 6 * 2^2, 2), # l = 2 + "sphere.stl" => Properties(4 / 3 * pi, center, I_mat .* 2 / 5, 4π, 2), # r = 1 + "2_4_8_cuboid.stl" => Properties( + 2 * 4 * 8, + center, + diagm([2^2 + 4^2, 8^2 + 2^2, 8^2 + 4^2]) ./ 12, + 2(2 * 4 + 2 * 8 + 4 * 8), + (2 + 4 + 8) / 3, + ), # h, d, w = 2, 4, 8 + # "slender_y.stl" => Properties( + # 10 * π * 0.1^2, + # center, + # diagm([1, 0, 1]) .* (10^2 / 12), + # 2π * 0.05 * (0.1 + 10), + # 0.55,#TODO: figure out why this math doesnt work (10 + 0.2 + 0.2) / 3 + # ), # l_z = 10, r = 0.1 + # "cylinder.stl" => Properties( + # 10 * π * 1^2, + # center, + # diagm([(3 + 10^2) / 12, (3 + 10^2) / 12, 1^2 / 2]), + # 2π * 1 * (1 + 10), + # 2.24,#TODO: figure out why this math doesnt work (10 + 2 + 2) / 3, + # ), # l_z = 10, r = 1 ) @testset "$model" for (model, control) in models path = "test_assets/$model" @@ -72,9 +88,10 @@ end @test props.center_of_gravity ≈ control.center_of_gravity atol = 0.01 @test eigvals(props.inertia) ≈ eigvals(control.inertia) atol = 0.1 @test props.surface_area ≈ control.surface_area atol = 0.5 + @test props.characteristic_length ≈ control.characteristic_length atol = 0.5 end @testset "Compare volumes with scaling for $model" begin - for scale in 1:5 + for scale in 1:4 props = get_mass_properties(stl; scale=scale) volume = _check_volume(stl; scale=scale) @test props.volume ≈ volume atol = 0.1 diff --git a/MisterBiggs/stl-process/test/test_assets/cylinder.stl b/MisterBiggs/stl-process/test/test_assets/cylinder.stl index 7a532b672283df6a59da98db37535625a0d8881d..6ef5d305f3fbd2b1d9e1a9ce7f9c50628d3d11e5 100644 GIT binary patch literal 148884 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%0AG!SD?AA+>Pt^f!1(> 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%0A;s7Mnkyl|GO>2q|%pLxh3B zY{8U$Ed1ukxfWc)A*+JuUf{r_06P7~XTg+x%ejp=bHMW;#9T zATC_s@DO~ur^SLP`_^7C2j>suJP5g|OW}a$T@PF2m}OuvTrg$d%w`90SRv~`Rs~7Z zketTAAhvMIzB_h?n_vDcu}3x+qMLys?XCw(8e?GCHgC$l_lGn#yZ%DefgI1s_AxNL zTQFr`*c#i-Z-1iZb7WPZP(Z{7sCM2K0jbZC-HWUW5>}Adg!sH_z8$j99TvjNs|5~+ zKD*i?r%(n4!39(HJ!CWl#~{SF3&1*%RY60FAUn%H<&c*iKb7JqjRGZBOXgZKp`6=z$4JxTGRfBX0?mw~jpOVeyv?F!)2i^Yc z2Gy1mHhS8ERDsIcZPi|Ab}Zj}Vy}>+!!TvzjfvgJIg$xWmdr$23n`8lY6(kfPsRTKsk;9OIVd~x! zd*2>$!O(%MN(6i(pMpai_+;}>yHD&rl4TE$4`g#8_CaC|)#rx}oY*^4({yw4kvdqe z1*Z~ZRmk=+Fq}JZV())eXRr=r+mTg)d=3jY28PpnPVAlg*cj{&WF5$=lEJtBF)&Dh zPfwq=_rzY2Pez+_j?|&F86dhB9ALZYX^Wf|AfX$?U=Owf;@brWK&=d9RnQOtmv#&c z+`CWg-6i3=x%fz(J+iqFmn=AN?}`^V{$Xj1fgy9>iM{DRjW%bZ=>YiyrV80U28MsT zPVD7dXTP}w5xQX8kyRmwGy}u-y(jjrFSG#r9NE3dsv!P@#3loS)!q|(gIpo09n?Pp z?ZTXN#>X}pe1o9Efom7MZIM$b1B3XU6MHQj48Um@SqHKzi0%akc7o4wKegw?-W9>d z;QRrRftU*kD@d$?LIDxyU-z8Y`+GSgMExoqUUX^FCGdiuk1x?y=@s7xb~meYn=k#FI((QBpo=b~x`jvDekm9Gq*Bb)ee;s?|U)WMFXLb7F6b zuroM5ICijs{jubSH`pIAmmuqAVBpw)V(;=B?qGi)>p)h8>_P^Hox4x$-8$0*79XIs zeBfFQl2;%l4ssYWFihWjVsFxY{mn?Z7OVqV6-4&}hbnM+1xoGCvs}SCATkhhA+ZKY zSD?^E_?&I`iM{Nr3^td-^B_bAvMOZz7#M!-KC$lC4u}q9RS*|0a0mgHS2GWs*xMsv4^D~5ISRs7II!W2k1cY{GB8Zrbz<+< zCuW<=j?~#hR6$PoLskVz(~z9Tz_5AmiM>UeASDAt2gFPG8o()}P&OOvkM|orZ9Vpig4tpL2#ROVk;vhG{6S2M>YzG5FAH*ybZ`;tlqM-F&4%qFO z{lDJc^<0uQvJPZBP+f@Rs@)B;*=I-bf^@)qj;aHp3e|-m9m@ae?RVLJ zw}#Y^koFaFo<|K4kd80^>g}K9?A(p416frfxLvZqVG+394$=X06|%X==?c^5=l|E+ z`+k?(jjRJ%6|#L08(?-I+m5OVIov>cy8qYP&ulHWM%KZ=fUHUZ9Onz*{&@Vq-oDi4 zyA?<`0|O)vWevNh!*n34g5-Zl4u+`t|Gyrbu8_^mfZQz$E;%t{ z5ax4a9jK}hDHLJ{Ob4p%2vx`-javt@D&*LN==kx!-hP{H`K}De-Lej#P|Sw<1H?v6 zp%6WgT#Kv&S(Pm0ZdqtO2-5NUf4%)8Z7(Zia~T+rV+}brK{{YQN7aE)g=`;0z4iZk z`){A;StHwytO_-xL3$$o*W3T%n7kWV2eK;U*aWF(U;vd2jpvfAtMltX>(d-mzuVik ztpl&?bkJOGXKTNA7Fc}QNh@gSllQOQ{@bZXyODJutGeL=(cy0a(*g6jPt|O&SvS{O z+s3)90kbP=&9T^lO$V|a$S!1n0!R#QZrutt_sez@TV$6Y>xOE8mej~PkX0eO5LE|q zDuLb<>~P?-t1WVfKy-leV2<^7l$409>cf&uuxnc^K(iSTJrI8&oBI=TXR*TrPG^+3 zfv7+Czux}eH#uu$9mwH>Y#&4iEOe1=M^=R#Zn$+ItFqZM6YP(S$(GPm0`flSO>By=pDcg4B)Xoh#p8-A)5<5@!Mg=eq)q022l@l z6|xTGct*AlRR^-|$f}S-8n+H)Rmic4ssnm*y@S4|mMz1wec%xFd1q*goI-KyKvo4k z(cU2kd`AaF4oEEjc_mvOKmiz{yA$lM_N7jL?YGE5laFR`x4NO!UYb8 zmhjuoI4ue`cXmGq9y^eAAlrfLLWm8p(4D{}2{w116^kvhOOSQr)`6@F*@dV&kkb|P z9s&oI*UGlYAp+3>YXKnZKvv~*;XgQ4zS01lf&p%`4Wcxs>K^UR~*>+@A$l(T34W11Lw`7oYAgjvefW-MSFb&ZIu>-o_+2P*>0bA(3 zDGtZ@IBk(*7NQ;&gUC9NRY7myaNyDhokRrD1F-|yT_6)|@z{Z^1KAE_7eZ`+`}`aDG#iK1f;t;ymmurLtpiyVvI|jl zAg3$nJx>l2cP`o>hX`&R$f^Y18G=Kh6kMA^^gu!v+1wn+eO?Yf)DNJ<4MaW6=g2yc z!w1Tw6CA zR2|6XBF7qXY~t2|tP0saR2|5+BdbCVY1}%HRUyYFs*a7|@ty?^+KSoMx4<)d3mkqQ z5VbZ2k6SHpU|`5L!L0*X)#HB9$k74^J#BOyCMuv2_yrCOS29gtcC6Sbx*J(HsvXEW zkX6BSgItKJ1LltllG(eFU4pC|w+>`gFgrjlMAdaf50HvJRNrLE(dJAF2*y+mTfvhZ}Ak$f{r_F)-wy zr4qmss!5Jpd8(kfaoR!~=9J8o)AnQO@1=G!URUR#@U@{ytRcrHb~`{WK~{xqA4CT%tdMO-Rs{_=nz`;I8)`6@FRt6`zpzBxw zZC_ofoV^EDp)g@47{dxfrC~j z=u{$z9!UEN+1xy6Ta-`z07@EzsE6e!WF5%yjBFo72P}Ue+m5UXIizvxKvsnuo2WXz zdT4=L+rIse_Xt8;-knSL?Lkh{xOE__G6auXEpX7ieGx4_kj+JoHRRYtwF6lPvMOZz zP<0^Nj;snfq;cy&R)rj!s5<(g<31i%EPG)+NVR^By|59T#b5dM;?{wzY7uzcYJo#q z8%zf*m9#;}uY8yA?}hc~V^53Xu>)BLvK`1SM0FKx9444aaxbz=kagqMfvgJIg{V4^ z)74AJoy89Ogg@^=4ny2JkX6CP2Ckp~kCv{G&4u;i#eEo2;s(_YWF5%igKQtF4rJSr zRUwBPZXL*~U?U+09I!Zt<#X6*RL2E@y|59TDSG02kz*Fs4rCq3s$gSI0eqkv<{)|? zp)h89Gj>*4ngjg zb=c*hwHG#0Sf(Mf7dcJi)`6@FHkP^Wl`=Hf!u)}3E^@3P$0n*B$U2Z!A=`(l1KDj0UO7!+itQKHbT!-YrfYGJZ`nX!QiA7)aS7HK-Ph*>N<3O@Rm8c z4%oa(%3ABa?a(>MEz9ii*nzAA*$!kELi_=mwX8man6+H@&3-SkOOSO#bjZYR+-xFh0U>b$vf;t zj#*SYkaZxdN&}BuEpW)IvOu!~+1y{yxzsF9XOuJsu>+QCk#!))GqQaU9k4h@wjEg& za!BLWfvgHSHX%Adv&j4HzVAwhu4mA`>#_G8bmmyV!f`Kb?iQ3nAv$2LLe_z-3N}~l z^Vt;|RxqC1bbw~67k-i3jchxzD&&xc=zzHjSqHKzb*2p!0Fpt+EN zf#DBmEl&3To#oJVWEC4d_oBH3i$6eNfUE=A4m1~nltNcPAg2-u=t>v1o1S~oLIj5n zWL25aRW>?zJ)mKA?SH+!BA4)PWOI|Cs_tF!LXR5;1_sdjs|#P`tdVsfhYzxSU?(wv z*2(aGn`ez|JF+UYaARO#aQI(uACNS6H?j^?RR_Q;>p(dTVh1coL2tWpm~_TxZxMLS z_5z2U7rghP#Vi8@11Q(dxBYH~tOHq91a$QlL^s41Nc#%e+)U_-Hc(jMjzMG{sH#xX z8`u(v9jLY=R3V2nSUKnfOz?OKvJPZb$gv60(ec0Deu{1RZpe!G1rDH4OoI9Ylrqp# zC<6lnNJpD>xizv5WK|O2eG>~DK&cX9hyDM0`@p2R*2v~EFd$+LH8w$dtp3;Ai+`QB z8(9akDrEaW>OmM1R>-y^t3nNFka}1uLDqq+3OP0*>R~#n^6S8Bwn1fh+d9w)!2*X( zkhTw49J76etix_EXsim-3cCRww_4zE6I=TVY*zAGYfw7@%!an%z-&xAe5z)HWk5QR z?Lc-Rsz0{0ZUyV8-fprN*(J!jaqB=<73Z=BY!b+Ys5+2S32Z+Ws3t=W5!^bERlNg` zTP<+lw{S!%uev#fcO#n%+vf#pGhq50RQlZgI?oze2eK+;`%qnlY&)_ljakT$2*u6VEwDvMA+XrS}d26^AIZfl% zfvjpNc-(4%!xv_KXu5*sb7XUoV+}brQSCt1fvgJIK2#mZwj--T4r$yvkX0eaCaR7W zD;Cfx#txuypM-u6FgxzF=-#{FajOLmOTaWl52O@D)`6^QA$Z(sfkQf&2I+vc`r4N8 zgU$6>!VemU0kc74WXQTf>OmM{2eJ-iRmd&`sRm()j(#Rdu(@7VEPIh%f~*^)8iX-* zAgh{jS`=&&$b}%)APmugoUTkoK7-TMnpetukwXNe8iX-*Agl7d02;Si;83C=gO;w4 z%?OfYN4IZ~z z;9v-*LF!@o1A1So18Dr}oSrz??Vu4TQY(xygQX;YrZz&ZnePSurMffLi9jl5ZPShSVN9Yhx!3=5dhCMUFM( z*o4>t3oB$D$f}U-gXn;T6|(Kfs*pn(w+>`g$gzp4W8+59Xww3ROOn~3bt~X>wR9(V zZ4{UdTBCtmhp{ebbPKdfE63z0gZGl zaA4SyZGvpaE%3p)fovjgNpR2|6a%Kj$Ax86J6gVxD_ z{Q(L?+&Yj$8fMaz^YhWx$sn66c@+}6pj9X^S7G`bSqHKzWc#qY3S>L7D&%m(tpiyV z%paIBc=n7BI85RLvO(*wz-;NO^47>Pi)sh54rEoIz@uIZ9QK`+M++;M49HwqSUFz? zuT=xvhloK~9z@mw3oDQ+WcwhlLe~MZ9a$AjH)edm>;UONRt0k*C^jK>!16gP6eBLl z+rZeam*<1l^@05XO4GP?z+w=j3YHdr?S2nks|NE2vbiueDAtf;6JiH89muMX?L*ap zY&)_lSSWx(8lnT{b7URJs$gLSicM4$JgkTy|N5#|~s2$acWWV2}$TZi4xIC$yaiTBncf z5@g-Dbs(#P)rlY%qUwOfGbmlb>XJpaZZ^mvf?Ef&Dp=hGTE&d21KHdq&^E&>$eL|% zxMBJnSqF0XAlnDA1C|nzZAVsx9BvRDu+T-;fvido+P>NVo+pIpfus`X>BbKG_Rq0d z0c|1OE?#GY9J3JhFguWSAgek8ZJkMVfp)h-^g!%DHa8dA7X1m?3jhu)%os%0fgI1s z_CaibxeD2KWL3x^javt@D&*Ki)$tA5A2gZp*hUE2@-AAo&jvY7?F!m23>p)hu7(8yZz~MG{9TvzISpMjMj$a*E!f%@d9V5GRS`>>N*mNM< zf$Tz%4Im6jyRdPX(@c`K$Sy(F4N?ulm^zSEA-fQy8iXM_kki#G=t#jbkPY$EE{0e30!!)q!j~vMS_o zgXn<82eJ-iRXN~ss|5~AF!m1~xgY=zlfM@PYGmP0-F#Y4LLS(>p)h8Y#*u)WZRKdA%`?X z2P|}vbs(!kj!lRTSm2EgOgTFdRQ*nv$4vK`1SMD+)3o=fe!J?MM}upJOeLZHcJ9Ma{%loP~70wfgC<4_Cd8kN>SL%56E_8 zRhTgdi*sZh$f~TN^Mjb@Gr(s1Kxaq8=GZ`|sUoKZsH32(MUZtMtAfqxflhZo)q!j- zZ1(LTAjdPZeGoffI*@HgR)rkW5FN0vLe_z-3OP0*IzTHq*sQ+q zLOylqEp+A>baDrBnuh2At!EIjDMvZk2w4?ut{8MWDMUy3|9bm#9Fun=n~NN4$gv60 z0b0+n=+iuFWF5$=knMx$0L@evewzoM+XmT=tO_}#Av!>-ce*)+;d9#{9muMXV-up| z=l^=I<%5FH>_Wz}X{A?rX^h3rCz4w$Qu(-rcGP{<(y(E(Z&_T`Bk zN=iglC9(9-PSA=pP^yIJ`1-%z-Y9;$6|%Vu42W}zFn#{zf4zOg(#5-wbs(!kwhv+l z%pb_MBdbCVH;9hE|Lg5f#EDxW>p)f|v|Jb*>X_vf^4UAc=hq;|EX0m~|Lg5t+W%Q1 z>p)h8dJYpv53DRfHW&Gf6x=b0tOHpUvV9;MKp0ZCBioLw3OS@fsv#=@|HX;#Le_z* z3K5$S9Uz~xwEx?Qe2T}ijnlv(3aU$xQz%3SEC!Kvpr``xpI_hr(hbo8iotVn;=7Q| zMUFM(*o5eS*@3JBSrv+XU@ahjOkJwH3;6^ZWL3x^4b}i(tA?xtSru|@LUh1f6~H9` z-n+E?C6n#L642fA3mm+jez)1TL=i0hn)wD=PZ(JTvZ_h*h{I8p)h8>_UhRP<&*gr7PHO z1yH*kIYbaTPPPBDL{3-8s+@b-!LH@ng5JkLHW#*U0(4pnrq5xy7Fh?fDrEa0u7brm zvhB#Kki!k417-)Z4rEn}gJi+}IAVJVEzXh8vOqrF0Xb$NcEDVPtOHpUZ1)i8gcVdB z$mSxSIe;01FguWSAge;Q57iE2+mTfvhcs>-$f}TI6IBOnUn=N)1{MipaEMk2PqaZ! z)3|jYtAgze#T*w!HWxY8kYf|o4rCq3s*vqN)q!j~vMS_|#;pTc6>@B%>R6Tyy3J;R zLq&_o?t~&|a9Gv3EZqHSHK<*+z`_OIz>MCR%$f}TCh^nJR2XYq9lcmjYJ3uZ$){R>SvMOX3qUu0SS1>l{gazaf z!L0*X6)a|T4j;0F=zx_B$mYV>pm`ijpJUU3tP0sah#jz8i)=fxDwxkf;f7lWvMSil zS*2ydXmJj6J7{k>%s$ZWZseFnwF6lPvMQK3=$rsl9mwXwTmss84RawVjp5dTtP0sa zR2|5+BdbCVY1}%HRl!UG#U`o_zwr|m&b)3|jYtAgnU?Ush6eb;t@ z+5NY4@YsQ@1KAE_7oz&()R(1Tb3+6T_9D9kSvPJS$f}TCh^hlQUBT*I(2ix~5W%ej zS=G7Cpmx;)2aaBLXjsA06|%Xo8X2^k7`M-nbs(!kwh!VeP%l+(sj?Na?Z~Q-!wsSX zmJ*S5Agl5Rx2qO7WCnp&YeMuu`ccSxEs^&tBF8L5J zyeAHK3?l15R)uUI#15FNkZng+g&fkjbs(!kj!je@$ossQgeQX2@hNfRy~t@Aq65^@ z=V||EiL3)z74n`xhz^)Pkj+JoHRRZY=zygXWF5$=knKb0uwSZdg={;rD&&xc=m7On zMdQVHA?rX^g&dm@9iX$L(`quUbUH-9XSQc%JM9f~TL@;W6*=$K-3n?~EpXVd8nnI> zqz5$aQ&*F@3t0!Us@vy4?WzS1B_1}=JO~S`;~S*FRmi$QHh?fB zwIl05R)y?BkZJ}7kgFPNGC|{4U~?^X%=aR@1X(vi$DbPT_!V3SvMOX3LUh3Vft*T^ zchn(=2tvoB_PU+OI*?UGECsczkoV(&Vh}X?fov}FJ}*q4fBs)@KXJ)oD`XwWs*vr2 zxC%7lHGS#gUC6d0t3nPphz?Nb*77A=A?rX^CA1vWuEMq-2YK%j^8O#>n1$E@%7Y^9 z|16PpAge;&#|F^>iw|UTk@uM3jzMG{$f}U-L$w3hc4Sq^A&pxHsw%{;JyadAc^uGw zoaGy*fm0=DKTh15vTocukX0eO5TXN=qgJ*5vqVl;$a`9lLj;VV79i$LhIMycGUt0v2CDv zLWmwn+Xq<(vZ@W>cGUuhL+3&BgvdIULhabPQOX9UDtldm4YF>CdRV$b)`6@F*@dV& zN_8MRbuyavMQKv&}vgu9WWV?xv;PT z#TqOW5OEIk2eJ-iRmk>Xw*zE5vMQKv%=m!W0n&l23g$vkY@*tc`;rM$U62r-2t0B^OA~f|FvCU zHp^`tJa!=KK(+(fg%BHHaenH{Qn0zF1PyGFU4pC|w+>`g$Sy?Hft;?8S5P8{2yPw7 zs?LMkRSO(y!1IJ4J)ltoP=65FT;z3qm_7&T0F6c=>p)h8Y#+!5$c#J7Qe`V-+mTfv zhZ{r(EOe1|Agl7X1+}XdINSrz6GHSrQVFbO30eaQ>nDI_*^y%wq8?@kvJPZb$SWNo zI$${p*<9o`MYv-SSqHKzWcwg?z+8oFJF+U|kcQ}hxe8eavMS`*gy?|TfxJ$JL3koK z9Xm)E+ajlFhz@8=ep)h89Gegwu+Y79R0ll1_r6KNHtG#z4O8hcdE0pk+ri>WoS>0m zh@J-fi!sfX$U2Z!>Fj{q)D>jU2wi^#bCqqyaj;qYoJDQJ_I?4g9kjpLAnS(M0CN?x z4rEoxE`;cS*%5V^89Z;0zw(p~vP+P4Lv+CGK-Ph*3fYAa9UwbQjr&nj3G#|0u2VORuS=@z$G0WGYM%|%`(gWczEm3CMl>p)h8Y#+i^AQrOi$f}UT z4Wtf)A*Bzp4rEn4FBgDg)9lg*wDk;x?LmL5B^C|Ot{+m5UXIiw*vKsm}JSZgP;4rEoxv5C+DVr^*x<(LHykB;c< zu{s7>LG|Qy^PcA%pmJ(~gQmiEw2=v99muN0z~$5ehppQ|=SP9e2D$1$4D-$wXUH91 zC(4iSIjsdLuO99Bg2xVI9msYdyAWan$PQ2``hCSIaL!RY%)A%bCCIuFI(Eb`??l#t ztP0tM2pu36aw>t%rGr)^AcqJ@9SB2G39=4kRZ8G;YJtOzf1nu-ka|!F2cnV9h0W@M z=AAKpj!g%$DrEaWCWA1%%m-nJ ztB`F+R)rkWAk`p@sRLORa%_TBgK&ert8xDh;RQh3mop) zflh4#*#PsoZN+i09Uji2dn5LK0kcE2zU)EP4N(uvgUC9NRUx|&q620})L~|DzqVz? zsXfRpLDmh?0kZ>H2eK+;7eaJ^?9evmN15$Io-soX5r_^@yZy|WFqC;`WL4`K7J+AC zUoKKWn@dGD7kM5ByU+hCS?omCfvgJIK8UM8{s6I%ZAVsx9Bv?WAlzUt8@y@Ok`j@1Age;2rvj-4VTcZ7bCJi8vB%&? zr5!tvbs(!kwhv@J2t({ZwjEg&a!7+zgD|EJWL3zq2~rKh@YWJI?SjVJzpmf`vya6r z*n^xxL8?Kx!CuXne_paq ztP0sagpMCd7CVt`M^=R#(hwb>l&Bl5Wr?f{X&QyNPHmcKvpGY4=Se?I4l773t>7A#4ua7I73dr+fZ@b z=Cl^1ygIe_3m!X=bs*b;>_UhQppxNnOf#sp1TF`s9%i;hb_ud>hz?L#?TBHv{J!E8 zTnDl$WEUcIfLO?>1U7OD8Zk!>5s*3%hJ+Qe4rEnIiy@)j#02UWg4Bat1)`D7g^fCb z#zirGj!g%$DrEaWCWA12s~kyBDos_5-O<=t(-TbcL(~S=Es!KCq7MeUGdVc7RwcnJ>UH^WN;R zPWT5Z{}wp>c=TmAvTl$%5Qc;mvJPZb$Swq_24RQ}HOUmP9V=cm?Ll@4vTl%S5N@#l zvGnG4WF5$=kX;B;4Z;ofhMx>AkW&frhz@dyfK-EUgZ*=-x7(3*Agh9{>bZ0G1lqU{ zvbo6P47h!cq5~-gk?jMy2!tE#4Z9_FAlr_t3OU?Bsv&hPh=r^JO_d>NA0+CW*DI&D z+mU<1$h}GAm<5>(!jKq5)`6@Fc^n0#8iXM_kj+Kz<6w_LkUnG`$f}U-1E~XHh#knb zBdfw4AILh8RUyYF$b1ln*n!-;gRQ3fc<00(jX9muMXV-ul+ zVcAXdOCoo{J(Jbbmh9o`p9F4k{P|eB=k1GDFnfY-bUsXcxkc z-%D?9XUcp5mih2v$L`pFpz*K;4ojbXu}0R7&;fE4vJPZb$S#EFfVC4?gD=m6Ov zAl17a*<9pyJEqS;IzW1nbs(!kwhyEZgd6PtthKX1wjEg&a=3w1L;L|^A?rX>1yA`P zbs!AMAIL3Bp(E&>($mSxKS7@;b*3@8c+C5`CvJPZb$o7Gi!`oNLwj--T4rz!ESgu9Z LfvgHSHW?TI#gPJO literal 6284 zcmZ>*D9A4=Nlj5mE6UGRaLP%|OGz!#HP$oKGh!eI)G#nGc;oWk z5BEO?+p*I_%a(!RQPfAf;2CP0XQJsqRt04{=zD^6OiEX>|CcDZnd^zGJ+cgxeE?+c z;R^z`ps?xy(=*bQ?1k3~ZDvK+0hV2G0Hg}4!{HxTjQfeJ{l5e6H%&-avPYJIvcWpO zfZe~~fFIaD%uihHYxca|G#gC^vMMOs!D0d^1ZEsfv`;TsyXj5TM>}L0C>w0YI-)pXlhJ}HcE~bNHrU)$aOzlap#B0V zeSvhWUKzN_6io-RDk$3_^b$x%-jO|tQw{Z(Rb>mFgGhNc5q z6_o98ZXZa;w4;gkeqEc4-ba1hhb#kS9{`#A6`HQhCp_N6zyON#2PNx`o}uYLRt04{ z`1XTzusm_KXFB-aXlA<7eq zn3k?&U${cZnDvS4equG$TCp&0g$;V7eT3f z0oXnU29S=XR}ed(s=#I;tAetj{?J=6W#8Xoh|iH_plq%H!)`W zRk9zd3Tzg#DkvKoAFrc6?mGsJb7UDP8yp`;!Kr+~0X=ZK0_g~y0f}>{DzI6|s-SF# z9pH2|BVB1fOOl{5+Y{IQ$TCni*xW$f}@hhs)sdiu+f|{!aTaBT(EU%Rt#+b5p^o ze8GV*aJdW8k#RTLNN2&6eNa_kvyfFm*$xfhayR!#-F}vo`J)}&TH<)(x_`y?_nW4m=|ENmWjp)@>zJ3W zw7+15&}N1wuJ*_>Q1$_kxv=~QYR^IPV78ecxD5wY1vU#=6_gEaCxS|bV>xP@K{Y6{ z43rISCqhe%1FOOH`tPWZ`=qngHcvj9Xb)8dHVau5l{xB1yTjG16c;@La;xeAp#CJka`e? z*nupA9D~Ry2BaEwH>%lgsA`bzkWYs<^@Y+8RXg- zxitXM0kZ>H2Dx2=+zx{1fW;uP404MMxdjN(0kZ>H2DvSY-1bG)fh+^l0czbNxB5|a zAj=^4KVYr}^^hPsKw;&bd0{8A406v4ITt{5fP607cWnoz3?${l;t7%mA$l72tMy&m zfh>cZj*(L+L_I95kY$ilC31R0)qyO7oW@{w<4;$}DF!($Anf4p3)_J#0}D4$3mG|P z5jsFDWEtexM2;trIuM5B4`dmb9iUjl40TXgJ#6>=yb`wwJ32t)jVEQ9PPWY>aJgK)$C|NoC~LzY2yAud;)FhiC>wh!4P za7%yRtfMBILH$Cg4sfXtWrIs+^&?0<1H-@n_4bOOk^$WASOAuRrbO_l3bb5-j($M&G=N9D zkY%884j#XNmR-SAl3~tq+!g zmb=KUWoQc-q!MNaOb4j#49&IRHZHV{0&R;T>p+%))&|HeKyZ44s0W2EUuJ+6%;%tS zLs(dW+HIJ*0OYDUuNLe?mO)PWn5iA4L%J`_0$Bz*9m7&0WQ+{rE=cGi%fQ&6REeD4 zAnHMOfLO>f$Y~5Y^?=lYaD%;2UzjDb404Krg$Sf43{nrX16c++{$cS1>8l~@K$d~A zL9vM(PZ0GWJGlD7b|TBb>;uIOa;QUefYQ|~0aFWP8RU?Lg&Y3RMGiOQ5P{eMautY$ zECX{DD12ak!tZlrKOwsoWHJbY;{3!mWEo_)Bijv94Z Date: Mon, 11 Apr 2022 22:22:56 -0700 Subject: [PATCH 20/24] small bugfix for 2d models --- MisterBiggs/stl-process/Project.toml | 2 +- MisterBiggs/stl-process/src/stlProcess.jl | 48 +++++++++++++++-------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml index 3913d8bc9..eee994f07 100644 --- a/MisterBiggs/stl-process/Project.toml +++ b/MisterBiggs/stl-process/Project.toml @@ -1,7 +1,7 @@ name = "stlProcess" uuid = "68914fc9-42cf-4b37-b06f-0b65edf9b8fa" authors = ["Anson "] -version = "0.2.0" +version = "0.2.1" [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index 7d39bb51b..bfd37986c 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -117,27 +117,41 @@ function calc_characteristic_length(triangles, inertia, center_of_gravity, scale sort_index = sortperm(θs) min_points = points[sort_index[1:3]] max_points = points[sort_index[(end - 2):end]] + min_point = 0 + max_point = 0 # Create a parameterized function using the eigenvector and center_of_gravity line_func(t) = center_of_gravity .+ eig .* t - # create a plane using 3 points, then find where the parameterized function intercepts it - max_normal = (max_points[1] - max_points[2]) × (max_points[2] - max_points[3]) - max_t = find_zero(t -> let - l = line_func(t) - sum(max_normal .* (l .- max_points[1])) - end, norm(eig)) - - max_point = line_func(max_t) - - # Build a plane in the opposite direction, and find the intercept again - min_normal = (min_points[1] - min_points[2]) × (min_points[2] - min_points[3]) - min_t = find_zero(t -> let - l = line_func(t) - sum(min_normal .* (l .- min_points[1])) - end, -norm(eig)) - - min_point = line_func(min_t) + try + # create a plane using 3 points, then find where the parameterized function intercepts it + max_normal = (max_points[1] - max_points[2]) × (max_points[2] - max_points[3]) + max_t = find_zero(t -> let + l = line_func(t) + sum(max_normal .* (l .- max_points[1])) + end, norm(eig)) + + max_point = line_func(max_t) + + # Build a plane in the opposite direction, and find the intercept again + min_normal = (min_points[1] - min_points[2]) × (min_points[2] - min_points[3]) + min_t = find_zero(t -> let + l = line_func(t) + sum(min_normal .* (l .- min_points[1])) + end, -norm(eig)) + + min_point = line_func(min_t) + + catch e + if isa(e, Roots.ConvergenceFailed) + println("Characteristic Length Algorithm failed to converge, this usually means stl is flat.") + println("Setting length in dir to 0.") + else + println("Unknown error when calculating Characteristic Length.") + end + min_point = 0 + max_point = 0 + end push!(characteristic_points, (min_point, max_point)) end -- 2.51.2 From 954c1350b7a63ab6352984f1aedd7b6545d8f991 Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Fri, 15 Apr 2022 02:54:29 +0000 Subject: [PATCH 21/24] Solid body dims --- MisterBiggs/stl-process/Project.toml | 2 +- MisterBiggs/stl-process/src/stlProcess.jl | 50 +++++++++++++---------- MisterBiggs/stl-process/test/runtests.jl | 6 ++- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml index eee994f07..b675e8917 100644 --- a/MisterBiggs/stl-process/Project.toml +++ b/MisterBiggs/stl-process/Project.toml @@ -1,7 +1,7 @@ name = "stlProcess" uuid = "68914fc9-42cf-4b37-b06f-0b65edf9b8fa" authors = ["Anson "] -version = "0.2.1" +version = "0.2.2" [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index bfd37986c..e1f713744 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -11,12 +11,14 @@ struct Properties inertia::Matrix{Float64} surface_area::Float64 characteristic_length::Float64 + sb_values::Vector{Float64} - function Properties(volume, center_of_gravity, inertia, surface_area, characteristic_length) + function Properties(volume, center_of_gravity, inertia, surface_area, characteristic_length, sb_values) @assert size(center_of_gravity) == (3,) + @assert size(sb_values) == (3,) @assert size(inertia) == (3, 3) - return new(volume, center_of_gravity, inertia, surface_area, characteristic_length) + return new(volume, center_of_gravity, inertia, surface_area, characteristic_length, sb_values) end end @@ -29,6 +31,7 @@ function get_mass_properties(triangles; scale=1.0) x = reduce(hcat, [[v[1] .* scale for v in tri] for tri in triangles])' y = reduce(hcat, [[v[2] .* scale for v in tri] for tri in triangles])' z = reduce(hcat, [[v[3] .* scale for v in tri] for tri in triangles])' + points = collect(Set(reduce(vcat, [[Array(Float64.(v)) .* scale for v in tri] for tri in triangles]))) function subexpression(x) w0, w1, w2 = x[:, 1], x[:, 2], x[:, 3] @@ -76,16 +79,29 @@ function get_mass_properties(triangles; scale=1.0) inertia[1, 2] = inertia[2, 1] = -(intg[8] - volume .* center_of_gravity[1] .* center_of_gravity[2]) inertia[2, 3] = inertia[3, 2] = -(intg[9] - volume .* center_of_gravity[2] .* center_of_gravity[3]) inertia[1, 3] = inertia[3, 1] = -(intg[10] - volume .* center_of_gravity[3] .* center_of_gravity[1]) + inertia = inertia / volume # https://math.stackexchange.com/questions/128991/how-to-calculate-the-area-of-a-3d-triangle surface_area = sum(norm.(eachrow([x0 y0 z0] - [x1 y1 z1]) .× eachrow([x1 y1 z1] - [x2 y2 z2])) / 2) - characteristic_length = calc_characteristic_length(triangles, inertia, center_of_gravity, scale) + characteristic_length = calc_characteristic_length(points, inertia, center_of_gravity) + + sb_values = solid_body(points) - return Properties(volume, center_of_gravity, inertia ./ volume, surface_area, characteristic_length) + return Properties(volume, center_of_gravity, inertia, surface_area, characteristic_length, sb_values) end -function calc_characteristic_length(triangles, inertia, center_of_gravity, scale; θtol=0.1) +function solid_body(points) + pts = reduce(hcat, points) + + Xsb = abs(reduce(-, extrema(@view pts[1, :]))) + Ysb = abs(reduce(-, extrema(@view pts[2, :]))) + Zsb = abs(reduce(-, extrema(@view pts[3, :]))) + + return [Xsb, Ysb, Zsb] +end + +function calc_characteristic_length(points, inertia, center_of_gravity) """ Calculate the Characteristic Length using eigenvectors. @@ -93,27 +109,17 @@ function calc_characteristic_length(triangles, inertia, center_of_gravity, scale More information on the geometry used: https://math.stackexchange.com/q/100447 """ - eigs = eigvecs(inertia) - points = collect(Set(reduce(vcat, [[Array(Float64.(v)) .* scale for v in tri] for tri in triangles]))) - - function θs_calc(ref, points) - """ - Calculates the angle between a reference vector and an array of points. - """ - norm_ref = norm(ref) - return map( - pt -> - acos(clamp(dot(ref, pt .- center_of_gravity) / (norm(pt .- center_of_gravity) * norm_ref), -1.0, 1.0)), - points, - ) - end - characteristic_points = [] # find the characteristic points for each eigenvector - for eig in eachrow(eigs) + for eig in eachrow(eigvecs(inertia)) # Find 3 points for each direction of the eigenvector - θs = θs_calc(eig, points) + θs = map( + point -> acos( + clamp(dot(eig, point .- center_of_gravity) / (norm(point .- center_of_gravity) * norm(eig)), -1.0, 1.0), + ), + points, + ) sort_index = sortperm(θs) min_points = points[sort_index[1:3]] max_points = points[sort_index[(end - 2):end]] diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index afb5ace3e..91117f783 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -53,14 +53,15 @@ end models = Dict( # Inertia math: https://en.wikipedia.org/wiki/List_of_moments_of_inertia#List_of_3D_inertia_tensors # Properties(volume, center_of_gravity, inertia, surface_area, characteristic_length) - "cube.stl" => Properties(2.0^3, center, I_mat .* 2^2 / 6, 6 * 2^2, 2), # l = 2 - "sphere.stl" => Properties(4 / 3 * pi, center, I_mat .* 2 / 5, 4π, 2), # r = 1 + "cube.stl" => Properties(2.0^3, center, I_mat .* 2^2 / 6, 6 * 2^2, 2, [2, 2, 2]), # l = 2 + "sphere.stl" => Properties(4 / 3 * pi, center, I_mat .* 2 / 5, 4π, 2, [2, 2, 2]), # r = 1 "2_4_8_cuboid.stl" => Properties( 2 * 4 * 8, center, diagm([2^2 + 4^2, 8^2 + 2^2, 8^2 + 4^2]) ./ 12, 2(2 * 4 + 2 * 8 + 4 * 8), (2 + 4 + 8) / 3, + [2, 4, 8], ), # h, d, w = 2, 4, 8 # "slender_y.stl" => Properties( # 10 * π * 0.1^2, @@ -89,6 +90,7 @@ end @test eigvals(props.inertia) ≈ eigvals(control.inertia) atol = 0.1 @test props.surface_area ≈ control.surface_area atol = 0.5 @test props.characteristic_length ≈ control.characteristic_length atol = 0.5 + @test props.sb_values ≈ control.sb_values atol = 0.01 end @testset "Compare volumes with scaling for $model" begin for scale in 1:4 -- 2.51.2 From 2e5d20f9c9d4c173db54d67e8490c3431506949f Mon Sep 17 00:00:00 2001 From: Anson Date: Thu, 14 Apr 2022 20:17:59 -0700 Subject: [PATCH 22/24] add readme --- MisterBiggs/stl-process/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 MisterBiggs/stl-process/README.md diff --git a/MisterBiggs/stl-process/README.md b/MisterBiggs/stl-process/README.md new file mode 100644 index 000000000..69c41e2a0 --- /dev/null +++ b/MisterBiggs/stl-process/README.md @@ -0,0 +1,5 @@ +# stlProcess.jl + +Package for getting mass and other properties from `stl` mesh files. + +usage: https://gitlab.com/orbital-debris-research/directed-study/report-3/-/blob/main/data.jl -- 2.51.2 From b077ca8c4e34053eb8360fb04050b2eeafe219b9 Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Wed, 20 Apr 2022 14:24:07 -0700 Subject: [PATCH 23/24] enable all tests --- MisterBiggs/stl-process/test/runtests.jl | 30 +++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index 91117f783..f2aade8c4 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -63,20 +63,22 @@ end (2 + 4 + 8) / 3, [2, 4, 8], ), # h, d, w = 2, 4, 8 - # "slender_y.stl" => Properties( - # 10 * π * 0.1^2, - # center, - # diagm([1, 0, 1]) .* (10^2 / 12), - # 2π * 0.05 * (0.1 + 10), - # 0.55,#TODO: figure out why this math doesnt work (10 + 0.2 + 0.2) / 3 - # ), # l_z = 10, r = 0.1 - # "cylinder.stl" => Properties( - # 10 * π * 1^2, - # center, - # diagm([(3 + 10^2) / 12, (3 + 10^2) / 12, 1^2 / 2]), - # 2π * 1 * (1 + 10), - # 2.24,#TODO: figure out why this math doesnt work (10 + 2 + 2) / 3, - # ), # l_z = 10, r = 1 + "slender_y.stl" => Properties( + 10 * π * 0.1^2, + center, + diagm([1, 0, 1]) .* (10^2 / 12), + 2π * 0.05 * (0.1 + 10), + 0.55,#TODO: figure out why this math doesnt work (10 + 0.1 + 0.1) / 3 + [0.1, 10, 0.1], + ), # l_z = 10, r = 0.05 + "cylinder.stl" => Properties( + 10 * π * 1^2, + center, + diagm([(3 + 10^2) / 12, (3 + 10^2) / 12, 1^2 / 2]), + 2π * 1 * (1 + 10), + 2.24,#TODO: figure out why this math doesnt work (10 + 2 + 2) / 3, + [2, 10, 2], + ), # l_z = 10, r = 1 ) @testset "$model" for (model, control) in models path = "test_assets/$model" -- 2.51.2 From 28dde8a51899fabd2d2f837e30fd6d7aaf87b189 Mon Sep 17 00:00:00 2001 From: Anson Biggs Date: Wed, 20 Apr 2022 15:31:05 -0700 Subject: [PATCH 24/24] tiny changes --- MisterBiggs/stl-process/Manifest.toml | 10 +++++----- MisterBiggs/stl-process/Project.toml | 2 +- MisterBiggs/stl-process/src/stlProcess.jl | 5 ++--- MisterBiggs/stl-process/test/runtests.jl | 8 ++++---- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/MisterBiggs/stl-process/Manifest.toml b/MisterBiggs/stl-process/Manifest.toml index b703e1c1a..aa8603aa6 100644 --- a/MisterBiggs/stl-process/Manifest.toml +++ b/MisterBiggs/stl-process/Manifest.toml @@ -34,7 +34,7 @@ version = "0.2.0" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "0.5.2+0" +version = "0.5.0+0" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] @@ -173,7 +173,7 @@ version = "1.2.0" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.20+0" +version = "0.3.17+2" [[deps.OrderedCollections]] git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" @@ -187,9 +187,9 @@ version = "1.8.0" [[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "d3538e7f8a790dc8903519090857ef8e1283eecd" +git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.2.5" +version = "1.3.0" [[deps.Printf]] deps = ["Unicode"] @@ -292,7 +292,7 @@ version = "1.2.12+1" [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.1.0+0" +version = "5.0.1+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] diff --git a/MisterBiggs/stl-process/Project.toml b/MisterBiggs/stl-process/Project.toml index b675e8917..ab8307ce1 100644 --- a/MisterBiggs/stl-process/Project.toml +++ b/MisterBiggs/stl-process/Project.toml @@ -1,7 +1,7 @@ name = "stlProcess" uuid = "68914fc9-42cf-4b37-b06f-0b65edf9b8fa" authors = ["Anson "] -version = "0.2.2" +version = "0.2.3" [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" diff --git a/MisterBiggs/stl-process/src/stlProcess.jl b/MisterBiggs/stl-process/src/stlProcess.jl index e1f713744..39c204c54 100644 --- a/MisterBiggs/stl-process/src/stlProcess.jl +++ b/MisterBiggs/stl-process/src/stlProcess.jl @@ -150,10 +150,9 @@ function calc_characteristic_length(points, inertia, center_of_gravity) catch e if isa(e, Roots.ConvergenceFailed) - println("Characteristic Length Algorithm failed to converge, this usually means stl is flat.") - println("Setting length in dir to 0.") + @warn "Characteristic Length Algorithm failed to converge, this usually means stl is flat. Setting length in dir to 0." else - println("Unknown error when calculating Characteristic Length.") + @warn "Unknown error when calculating Characteristic Length." end min_point = 0 max_point = 0 diff --git a/MisterBiggs/stl-process/test/runtests.jl b/MisterBiggs/stl-process/test/runtests.jl index f2aade8c4..307b07d3d 100644 --- a/MisterBiggs/stl-process/test/runtests.jl +++ b/MisterBiggs/stl-process/test/runtests.jl @@ -64,7 +64,7 @@ end [2, 4, 8], ), # h, d, w = 2, 4, 8 "slender_y.stl" => Properties( - 10 * π * 0.1^2, + 10 * π * 0.05^2, center, diagm([1, 0, 1]) .* (10^2 / 12), 2π * 0.05 * (0.1 + 10), @@ -87,10 +87,10 @@ end @testset "get_mass_properties for $model" begin props = get_mass_properties(stl) - @test props.volume ≈ control.volume atol = 0.5 + @test props.volume ≈ control.volume rtol = 0.1 @test props.center_of_gravity ≈ control.center_of_gravity atol = 0.01 - @test eigvals(props.inertia) ≈ eigvals(control.inertia) atol = 0.1 - @test props.surface_area ≈ control.surface_area atol = 0.5 + @test eigvals(props.inertia) ≈ eigvals(control.inertia) atol = 0.01 + @test props.surface_area ≈ control.surface_area atol = 0.2 @test props.characteristic_length ≈ control.characteristic_length atol = 0.5 @test props.sb_values ≈ control.sb_values atol = 0.01 end -- 2.51.2