Browse Source

generate incs for 3d visualizers

Thomas Buck 2 months ago
parent
commit
f2b1925ecb

+ 5
- 2
.github/workflows/docs.yml View File

52
 
52
 
53
       - name: Install dependencies
53
       - name: Install dependencies
54
         run: |
54
         run: |
55
-          sudo add-apt-repository --yes ppa:kicad/kicad-7.0-releases
55
+          sudo add-apt-repository --yes ppa:kicad/kicad-8.0-releases
56
           sudo apt update
56
           sudo apt update
57
-          sudo apt install -y --install-recommends kicad pipx libfuse2 libegl1 poppler-utils
57
+          sudo apt install -y --install-recommends kicad pipx libfuse2 libegl1 poppler-utils openscad zip
58
 
58
 
59
       - name: Install latest mdbook
59
       - name: Install latest mdbook
60
         run: |
60
         run: |
67
       - name: Generate Plots
67
       - name: Generate Plots
68
         run: pcb/generate_plot.sh
68
         run: pcb/generate_plot.sh
69
 
69
 
70
+      - name: Render STLs
71
+        run: 3dprint/generate_stls.sh
72
+
70
       - name: Build Book
73
       - name: Build Book
71
         run: docs/generate_docs.sh build
74
         run: docs/generate_docs.sh build
72
 
75
 

+ 1
- 1
.github/workflows/kicad.yml View File

47
 
47
 
48
       - name: Install dependencies
48
       - name: Install dependencies
49
         run: |
49
         run: |
50
-          sudo add-apt-repository --yes ppa:kicad/kicad-7.0-releases
50
+          sudo add-apt-repository --yes ppa:kicad/kicad-8.0-releases
51
           sudo apt update
51
           sudo apt update
52
           sudo apt install -y --install-recommends kicad
52
           sudo apt install -y --install-recommends kicad
53
           sudo apt-get install -y zip
53
           sudo apt-get install -y zip

+ 26
- 0
.github/workflows/scad.yml View File

1
+# SPDX-FileCopyrightText: 2024 Thomas Buck <thomas@xythobuz.de>
2
+# SPDX-License-Identifier: CERN-OHL-S-2.0+
3
+#
4
+#  ------------------------------------------------------------------------------
5
+# | Copyright (c) 2024 Thomas Buck <thomas@xythobuz.de>                          |
6
+# |                                                                              |
7
+# | This source describes Open Hardware and is licensed under the CERN-OHL-S v2  |
8
+# | or any later version.                                                        |
9
+# |                                                                              |
10
+# | You may redistribute and modify this source and make products using it under |
11
+# | the terms of the CERN-OHL-S v2 (https://ohwr.org/cern_ohl_s_v2.txt)          |
12
+# | or any later version.                                                        |
13
+# |                                                                              |
14
+# | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,          |
15
+# | INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A         |
16
+# | PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 (or any later version)      |
17
+# | for applicable conditions.                                                   |
18
+# |                                                                              |
19
+# | Source location: https://git.xythobuz.de/thomas/drumkit                      |
20
+# |                                                                              |
21
+# | As per CERN-OHL-S v2 section 4, should You produce hardware based on this    |
22
+# | source, You must where practicable maintain the Source Location visible      |
23
+# | on the external case of the Gizmo or other products you make using this      |
24
+# | source.                                                                      |
25
+#  ------------------------------------------------------------------------------
26
+
1
 name: STLs
27
 name: STLs
2
 
28
 
3
 # build for each push and pull request
29
 # build for each push and pull request

+ 46
- 0
docs/generate_docs.sh View File

43
 cp -r ../3dprint/stl src
43
 cp -r ../3dprint/stl src
44
 #echo
44
 #echo
45
 
45
 
46
+INSTL=`ls ../3dprint/stl/*.stl`
47
+
46
 for IN in $INSCH
48
 for IN in $INSCH
47
 do
49
 do
48
     o="src/inc_$IN.md"
50
     o="src/inc_$IN.md"
73
     echo
75
     echo
74
 done
76
 done
75
 
77
 
78
+plot_3d() {
79
+    echo '<script type="importmap">' >> $1
80
+    echo '    {' >> $1
81
+    echo '        "imports": {' >> $1
82
+    echo '            "three": "https://cdn.jsdelivr.net/npm/three@0.163.0/build/three.module.js",' >> $1
83
+    echo '            "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.163.0/examples/jsm/"' >> $1
84
+    echo '        }' >> $1
85
+    echo '    }' >> $1
86
+    echo '</script>' >> $1
87
+    echo "<p>Status: \"<span id=\"3d_info_$2\">Preparing 3D model...</span>\"</p>" >> $1
88
+    echo "<div id=\"3d_viewer_$2\" style=\"width: 100%; height: 100%; background-color: white; border: 1px solid black;\"></div>" >> $1
89
+    echo '<script type="module">' >> $1
90
+    echo "    var info = document.getElementById(\"3d_info_$2\");" >> $1
91
+    echo "    var view = document.getElementById(\"3d_viewer_$2\");" >> $1
92
+    echo '    view.style.height = Math.floor(view.clientWidth * 0.707) + "px";' >> $1
93
+    echo '    import { init_3d } from "./js/3d.js";' >> $1
94
+    echo "    init_3d(\"$3\", view, info, view.clientWidth, view.clientHeight);" >> $1
95
+    echo '</script>' >> $1
96
+}
97
+
98
+for IN in $INPCB
99
+do
100
+    o="src/inc_$IN.md"
101
+    file="plot/$IN.wrl"
102
+    name=`echo $file | sed "s:plot/::g" | sed 's:.wrl::g'`
103
+    echo "Include for $IN at $o, $file, $name"
104
+    rm -rf $o
105
+
106
+    plot_3d $o $name $file
107
+    echo
108
+done
109
+
110
+for IN in $INSTL
111
+do
112
+    o=`echo $IN | sed "s:../3dprint/stl/:src/inc_:g" | sed "s:.stl:.stl.md:g"`
113
+    file=`echo $IN | sed "s:../3dprint/::g"`
114
+    name=`echo $file | sed "s:stl/::g" | sed 's:.stl::g'`
115
+    echo "Include for $IN at $o, $file, $name"
116
+    rm -rf $o
117
+
118
+    plot_3d $o $name $file
119
+    echo
120
+done
121
+
76
 echo "Generating docs"
122
 echo "Generating docs"
77
 if [ "$1" = "serve" ] ; then
123
 if [ "$1" = "serve" ] ; then
78
     mdbook serve --open
124
     mdbook serve --open

+ 31
- 28
docs/src/3dprint.md View File

2
 
2
 
3
 **TODO** work in progress
3
 **TODO** work in progress
4
 
4
 
5
-<script type="importmap">
6
-    {
7
-        "imports": {
8
-            "three": "https://cdn.jsdelivr.net/npm/three@0.163.0/build/three.module.js",
9
-            "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.163.0/examples/jsm/"
10
-        }
11
-    }
12
-</script>
13
-
14
 ## Actuator All
5
 ## Actuator All
15
 
6
 
16
-<p>Status: "<span id="3d_info">Preparing 3D model...</span>"</p>
17
-<div id="3d_viewer" style="width: 100%; height: 100%; background-color: white; border: 1px solid black;"></div>
7
+{{#include inc_actuator_all.stl.md}}
8
+
9
+## Actuator Cap
18
 
10
 
19
-<script type="module">
20
-    var info = document.getElementById('3d_info');
21
-    var view = document.getElementById('3d_viewer');
22
-    view.style.height = Math.floor(view.clientWidth * 0.707) + "px";
11
+{{#include inc_actuator_cap.stl.md}}
23
 
12
 
24
-    import { init_3d } from "./js/3d.js";
25
-    init_3d('stl/actuator_all.stl', view, info, view.clientWidth, view.clientHeight);
26
-</script>
13
+## Actuator Hammer
27
 
14
 
28
-## Actuator Cap
15
+{{#include inc_actuator_hammer.stl.md}}
16
+
17
+## Actuator Spool
18
+
19
+{{#include inc_actuator_spool.stl.md}}
20
+
21
+## Tamb Mount All Visualize
22
+
23
+{{#include inc_tamb_mount_all_visualize.stl.md}}
24
+
25
+## Tamb Mount Inner
26
+
27
+{{#include inc_tamb_mount_inner.stl.md}}
28
+
29
+## Tamb Mount Outer
30
+
31
+{{#include inc_tamb_mount_outer.stl.md}}
32
+
33
+## Enclosure Bottom
34
+
35
+{{#include inc_enclosure_bottom.stl.md}}
36
+
37
+## Enclosure Top
29
 
38
 
30
-<p>Status: "<span id="3d_info2">Preparing 3D model...</span>"</p>
31
-<div id="3d_viewer2" style="width: 100%; height: 100%; background-color: white; border: 1px solid black;"></div>
39
+{{#include inc_enclosure_top.stl.md}}
32
 
40
 
33
-<script type="module">
34
-    var info = document.getElementById('3d_info2');
35
-    var view = document.getElementById('3d_viewer2');
36
-    view.style.height = Math.floor(view.clientWidth * 0.707) + "px";
41
+## Enclosure Faceplate
37
 
42
 
38
-    import { init_3d } from "./js/3d.js";
39
-    init_3d('stl/actuator_cap.stl', view, info, view.clientWidth, view.clientHeight);
40
-</script>
43
+{{#include inc_enclosure_faceplate.stl.md}}

+ 110
- 109
docs/src/js/3d.js View File

1
+/*
2
+ * 3d.js
3
+ *
4
+ * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
5
+ */
6
+
1
 import * as THREE from 'three';
7
 import * as THREE from 'three';
2
 import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
8
 import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3
 import { STLLoader } from 'three/addons/loaders/STLLoader.js'
9
 import { STLLoader } from 'three/addons/loaders/STLLoader.js'
4
 import { VRMLLoader } from 'three/addons/loaders/VRMLLoader.js';
10
 import { VRMLLoader } from 'three/addons/loaders/VRMLLoader.js';
5
 
11
 
12
+// https://wejn.org/2020/12/cracking-the-threejs-object-fitting-nut/
13
+function fitCameraToCenteredObject(camera, object, offset, orbitControls ) {
14
+    const boundingBox = new THREE.Box3();
15
+    boundingBox.setFromObject( object );
16
+
17
+    var size = new THREE.Vector3();
18
+    boundingBox.getSize(size);
19
+
20
+    // figure out how to fit the box in the view:
21
+    // 1. figure out horizontal FOV (on non-1.0 aspects)
22
+    // 2. figure out distance from the object in X and Y planes
23
+    // 3. select the max distance (to fit both sides in)
24
+    //
25
+    // The reason is as follows:
26
+    //
27
+    // Imagine a bounding box (BB) is centered at (0,0,0).
28
+    // Camera has vertical FOV (camera.fov) and horizontal FOV
29
+    // (camera.fov scaled by aspect, see fovh below)
30
+    //
31
+    // Therefore if you want to put the entire object into the field of view,
32
+    // you have to compute the distance as: z/2 (half of Z size of the BB
33
+    // protruding towards us) plus for both X and Y size of BB you have to
34
+    // figure out the distance created by the appropriate FOV.
35
+    //
36
+    // The FOV is always a triangle:
37
+    //
38
+    //  (size/2)
39
+    // +--------+
40
+    // |       /
41
+    // |      /
42
+    // |     /
43
+    // | F° /
44
+    // |   /
45
+    // |  /
46
+    // | /
47
+    // |/
48
+    //
49
+    // F° is half of respective FOV, so to compute the distance (the length
50
+    // of the straight line) one has to: `size/2 / Math.tan(F)`.
51
+    //
52
+    // FTR, from https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
53
+    // the camera.fov is the vertical FOV.
54
+
55
+    const fov = camera.fov * ( Math.PI / 180 );
56
+    const fovh = 2*Math.atan(Math.tan(fov/2) * camera.aspect);
57
+    let dx = size.z / 2 + Math.abs( size.x / 2 / Math.tan( fovh / 2 ) );
58
+    let dy = size.z / 2 + Math.abs( size.y / 2 / Math.tan( fov / 2 ) );
59
+    let cameraZ = Math.max(dx, dy);
60
+
61
+    // offset the camera, if desired (to avoid filling the whole canvas)
62
+    if( offset !== undefined && offset !== 0 ) cameraZ *= offset;
63
+
64
+    camera.position.set( 0, 0, cameraZ );
65
+
66
+    // set the far plane of the camera so that it easily encompasses the whole object
67
+    const minZ = boundingBox.min.z;
68
+    const cameraToFarEdge = ( minZ < 0 ) ? -minZ + cameraZ : cameraZ - minZ;
69
+
70
+    camera.far = cameraToFarEdge * 3;
71
+    camera.updateProjectionMatrix();
72
+
73
+    if ( orbitControls !== undefined ) {
74
+        // set camera to rotate around the center
75
+        orbitControls.target = new THREE.Vector3(0, 0, 0);
76
+
77
+        // prevent camera from zooming out far enough to create far plane cutoff
78
+        orbitControls.maxDistance = cameraToFarEdge * 2;
79
+    }
80
+}
81
+
6
 export function init_3d(path, container, status, div_width, div_height) {
82
 export function init_3d(path, container, status, div_width, div_height) {
7
     const width = div_width;
83
     const width = div_width;
8
     const height = div_height;
84
     const height = div_height;
17
 
93
 
18
     container.appendChild( renderer.domElement );
94
     container.appendChild( renderer.domElement );
19
 
95
 
20
-    const light = new THREE.DirectionalLight( 0xffffff, 0.5 );
21
-    light.position.set(0, 1, 0);
22
-    scene.add(light);
23
-
24
-    const light42 = new THREE.DirectionalLight( 0xffffff, 0.5 );
25
-    light42.position.set(1, 0, 0);
26
-    scene.add(light42);
27
-
28
-    const light23 = new THREE.DirectionalLight( 0xffffff, 0.5 );
29
-    light23.position.set(0, 0, 1);
30
-    scene.add(light23);
31
-
32
-    const lightb = new THREE.DirectionalLight( 0xffffff, 0.5 );
33
-    lightb.position.set(0, -1, 0);
34
-    scene.add(lightb);
35
-
36
-    const light42b = new THREE.DirectionalLight( 0xffffff, 0.5 );
37
-    light42b.position.set(-1, 0, 0);
38
-    scene.add(light42b);
39
-
40
-    const light23b = new THREE.DirectionalLight( 0xffffff, 0.5 );
41
-    light23b.position.set(0, 0, -1);
42
-    scene.add(light23b);
96
+    const controls = new OrbitControls( camera, renderer.domElement );
97
+    controls.enableDamping = true;
43
 
98
 
44
-    const light2 = new THREE.AmbientLight(0x101010);
45
-    light2.position.set(100, 100, 100);
46
-    scene.add(light2);
99
+    if (path.endsWith(".stl")) {
100
+        const light_amb = new THREE.AmbientLight(0x424242);
101
+        scene.add(light_amb);
102
+
103
+        for (const i of [1, -1]) {
104
+            for (const j of [0, 1, 2]) {
105
+                const light = new THREE.DirectionalLight(0xffffff, 0.5);
106
+                light.position.set(i * (j == 0 ? 1 : 0),
107
+                                   i * (j == 1 ? 1 : 0),
108
+                                   i * (j == 2 ? 1 : 0));
109
+                scene.add(light);
110
+            }
111
+        }
47
 
112
 
48
-    const material = new THREE.MeshStandardMaterial();
49
-    //material.roughness = 0.42;
113
+        const material = new THREE.MeshStandardMaterial();
114
+        //material.roughness = 0.75;
50
 
115
 
51
-    if (path.endsWith(".stl")) {
52
         const loader = new STLLoader();
116
         const loader = new STLLoader();
53
         loader.load(
117
         loader.load(
54
-            //'plot/actuator_all.stl',
55
             path,
118
             path,
56
             function (geometry) {
119
             function (geometry) {
57
                 const mesh = new THREE.Mesh(geometry, material);
120
                 const mesh = new THREE.Mesh(geometry, material);
58
                 scene.add(mesh);
121
                 scene.add(mesh);
122
+                fitCameraToCenteredObject(camera, scene, 0, controls);
59
             },
123
             },
60
             (xhr) => {
124
             (xhr) => {
61
                 const s = (xhr.loaded / xhr.total) * 100 + '% loaded';
125
                 const s = (xhr.loaded / xhr.total) * 100 + '% loaded';
68
             }
132
             }
69
         );
133
         );
70
     } else if (path.endsWith(".wrl")) {
134
     } else if (path.endsWith(".wrl")) {
135
+        const light_amb = new THREE.AmbientLight(0xffffff);
136
+        scene.add(light_amb);
137
+
71
         const loader = new VRMLLoader();
138
         const loader = new VRMLLoader();
72
         loader.load(
139
         loader.load(
73
-            //'plot/drumkit.kicad_pcb.wrl',
74
-            //'plot/dispensy.wrl',
75
             path,
140
             path,
76
             function (object) {
141
             function (object) {
77
                 scene.add(object);
142
                 scene.add(object);
78
-        });
143
+                fitCameraToCenteredObject(camera, scene, 0, controls);
144
+            },
145
+            (xhr) => {
146
+                const s = (xhr.loaded / xhr.total) * 100 + '% loaded';
147
+                console.log(s);
148
+                status.textContent = s;
149
+            },
150
+            (error) => {
151
+                console.log(error);
152
+                status.textContent = error;
153
+            }
154
+        );
79
     } else {
155
     } else {
80
         const s = "error: unknown filetype for " + path;
156
         const s = "error: unknown filetype for " + path;
81
         console.log(s);
157
         console.log(s);
82
         status.textContent = s;
158
         status.textContent = s;
83
     }
159
     }
84
 
160
 
85
-    const controls = new OrbitControls( camera, renderer.domElement );
86
-    controls.enableDamping = true;
87
-
88
-    // https://wejn.org/2020/12/cracking-the-threejs-object-fitting-nut/
89
-    const fitCameraToCenteredObject = function (camera, object, offset, orbitControls ) {
90
-        const boundingBox = new THREE.Box3();
91
-        boundingBox.setFromObject( object );
92
-
93
-        var middle = new THREE.Vector3();
94
-        var size = new THREE.Vector3();
95
-        boundingBox.getSize(size);
96
-
97
-        // figure out how to fit the box in the view:
98
-        // 1. figure out horizontal FOV (on non-1.0 aspects)
99
-        // 2. figure out distance from the object in X and Y planes
100
-        // 3. select the max distance (to fit both sides in)
101
-        //
102
-        // The reason is as follows:
103
-        //
104
-        // Imagine a bounding box (BB) is centered at (0,0,0).
105
-        // Camera has vertical FOV (camera.fov) and horizontal FOV
106
-        // (camera.fov scaled by aspect, see fovh below)
107
-        //
108
-        // Therefore if you want to put the entire object into the field of view,
109
-        // you have to compute the distance as: z/2 (half of Z size of the BB
110
-        // protruding towards us) plus for both X and Y size of BB you have to
111
-        // figure out the distance created by the appropriate FOV.
112
-        //
113
-        // The FOV is always a triangle:
114
-        //
115
-        //  (size/2)
116
-        // +--------+
117
-        // |       /
118
-        // |      /
119
-        // |     /
120
-        // | F° /
121
-        // |   /
122
-        // |  /
123
-        // | /
124
-        // |/
125
-        //
126
-        // F° is half of respective FOV, so to compute the distance (the length
127
-        // of the straight line) one has to: `size/2 / Math.tan(F)`.
128
-        //
129
-        // FTR, from https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
130
-        // the camera.fov is the vertical FOV.
131
-
132
-        const fov = camera.fov * ( Math.PI / 180 );
133
-        const fovh = 2*Math.atan(Math.tan(fov/2) * camera.aspect);
134
-        let dx = size.z / 2 + Math.abs( size.x / 2 / Math.tan( fovh / 2 ) );
135
-        let dy = size.z / 2 + Math.abs( size.y / 2 / Math.tan( fov / 2 ) );
136
-        let cameraZ = Math.max(dx, dy);
137
-
138
-        // offset the camera, if desired (to avoid filling the whole canvas)
139
-        if( offset !== undefined && offset !== 0 ) cameraZ *= offset;
140
-
141
-        camera.position.set( 0, 0, cameraZ );
142
-
143
-        // set the far plane of the camera so that it easily encompasses the whole object
144
-        const minZ = boundingBox.min.z;
145
-        const cameraToFarEdge = ( minZ < 0 ) ? -minZ + cameraZ : cameraZ - minZ;
146
-
147
-        camera.far = cameraToFarEdge * 3;
148
-        camera.updateProjectionMatrix();
149
-
150
-        if ( orbitControls !== undefined ) {
151
-            // set camera to rotate around the center
152
-            orbitControls.target = new THREE.Vector3(0, 0, 0);
153
-
154
-            // prevent camera from zooming out far enough to create far plane cutoff
155
-            orbitControls.maxDistance = cameraToFarEdge * 2;
156
-        }
157
-    };
158
-
159
-    //camera.position.z = 50;
160
-    fitCameraToCenteredObject(camera, scene, 0, controls)
161
+    camera.position.z = 50;
161
 
162
 
162
     function render() {
163
     function render() {
163
         renderer.render(scene, camera);
164
         renderer.render(scene, camera);

+ 1
- 20
docs/src/pcb1_pcb.md View File

36
 
36
 
37
 ## 3D PCB Layout
37
 ## 3D PCB Layout
38
 
38
 
39
-<p>Status: "<span id="3d_info">Preparing 3D model...</span>"</p>
40
-<div id="3d_viewer" style="width: 100%; height: 100%; background-color: white; border: 1px solid black;"></div>
41
-
42
-<script type="importmap">
43
-    {
44
-        "imports": {
45
-            "three": "https://cdn.jsdelivr.net/npm/three@0.163.0/build/three.module.js",
46
-            "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.163.0/examples/jsm/"
47
-        }
48
-    }
49
-</script>
50
-
51
-<script type="module">
52
-    var info = document.getElementById('3d_info');
53
-    var view = document.getElementById('3d_viewer');
54
-    view.style.height = Math.floor(view.clientWidth * 0.707) + "px";
55
-
56
-    import { init_3d } from "./js/3d.js";
57
-    init_3d('plot/drumkit.kicad_pcb.wrl', view, info, view.clientWidth, view.clientHeight);
58
-</script>
39
+{{#include inc_drumkit.kicad_pcb.md}}

Loading…
Cancel
Save