Skip to content

Commit 83791c7

Browse files
committed
adding metaball table example
1 parent cccd882 commit 83791c7

File tree

3 files changed

+318
-0
lines changed

3 files changed

+318
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<html>
2+
<head>
3+
<meta charset="utf-8">
4+
<title>Metaball Table</title>
5+
<style>
6+
body { margin: 0; }
7+
canvas { width: 100%; height: 100%; }
8+
input {width: 90%;}
9+
#container {position: relative;}
10+
#container canvas, #overlay { position: absolute;}
11+
#overlay {z-index: 1; width: 100%}
12+
13+
#loader {
14+
border: 5px solid #f3f3f3; /* Light grey */
15+
border-top: 5px solid #3d3d3d; /* Grey */
16+
border-radius: 50%;
17+
width: 40px;
18+
height: 40px;
19+
animation: spin 1s linear infinite;
20+
position: absolute;
21+
top: 50%;
22+
left: 50%;
23+
z-index: 2;
24+
}
25+
26+
@keyframes spin {
27+
0% { transform: rotate(0deg); }
28+
100% { transform: rotate(360deg); }
29+
}
30+
</style>
31+
</head>
32+
<body>
33+
<div id="loader"></div>
34+
<div id="container">
35+
<div id="overlay">
36+
<div>
37+
<input type="range" id="dimension" name="dimension" min="350" max="1000" value="800" step="10">
38+
<label for="dimension">Dimension</label>
39+
</div>
40+
<div>
41+
<input type="range" id="height" name="height" min="350" max="1000" value="350" step="1">
42+
<label for="height">Height</label>
43+
</div>
44+
</div>
45+
</div>
46+
<script type="module" src="./script.js"></script>
47+
</body>
48+
</html>
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
/* eslint no-undef: "off", no-unused-vars: "off" */
2+
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js'
3+
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js'
4+
import { Rhino3dmLoader } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/loaders/3DMLoader.js'
5+
import rhino3dm from 'https://cdn.jsdelivr.net/npm/[email protected]/rhino3dm.module.js'
6+
7+
// set up loader for converting the results to threejs
8+
const loader = new Rhino3dmLoader()
9+
loader.setLibraryPath( 'https://cdn.jsdelivr.net/npm/[email protected]/' )
10+
11+
const definition = 'metaballTable.gh'
12+
13+
// setup input change events
14+
const dimension_slider = document.getElementById( 'dimension' )
15+
dimension_slider.addEventListener( 'mouseup', onSliderChange, false )
16+
dimension_slider.addEventListener( 'touchend', onSliderChange, false )
17+
const height_slider = document.getElementById( 'height' )
18+
height_slider.addEventListener( 'mouseup', onSliderChange, false )
19+
height_slider.addEventListener( 'touchend', onSliderChange, false )
20+
21+
let points = []
22+
points.push( "{\"X\":"+279+",\"Y\":"+218+",\"Z\":"+0+"}")
23+
points.push( "{\"X\":"+-181+",\"Y\":"+218+",\"Z\":"+0+"}")
24+
points.push( "{\"X\":"+49+",\"Y\":"+-212+",\"Z\":"+0+"}")
25+
26+
let rhino, doc
27+
28+
rhino3dm().then(async m => {
29+
console.log('Loaded rhino3dm.')
30+
rhino = m // global
31+
32+
init()
33+
compute()
34+
})
35+
36+
/**
37+
* Call appserver
38+
*/
39+
async function compute(){
40+
41+
// initialise 'data' object that will be used by compute()
42+
const data = {
43+
definition: definition,
44+
inputs: {
45+
'dimension': dimension_slider.valueAsNumber,
46+
'height': height_slider.valueAsNumber,
47+
'points': points
48+
}
49+
}
50+
51+
console.log(data.inputs)
52+
53+
const request = {
54+
'method':'POST',
55+
'body': JSON.stringify(data),
56+
'headers': {'Content-Type': 'application/json'}
57+
}
58+
59+
try {
60+
const response = await fetch('/solve', request)
61+
62+
if(!response.ok)
63+
throw new Error(response.statusText)
64+
65+
const responseJson = await response.json()
66+
collectResults(responseJson)
67+
68+
} catch(error){
69+
console.error(error)
70+
}
71+
}
72+
73+
/**
74+
* Parse response
75+
*/
76+
function collectResults(responseJson) {
77+
78+
const values = responseJson.values
79+
80+
console.log(values)
81+
82+
// clear doc
83+
try {
84+
if( doc !== undefined)
85+
doc.delete()
86+
} catch {}
87+
88+
//console.log(values)
89+
doc = new rhino.File3dm()
90+
91+
// for each output (RH_OUT:*)...
92+
for ( let i = 0; i < values.length; i ++ ) {
93+
// ...iterate through data tree structure...
94+
for (const path in values[i].InnerTree) {
95+
const branch = values[i].InnerTree[path]
96+
// ...and for each branch...
97+
for( let j = 0; j < branch.length; j ++) {
98+
// ...load rhino geometry into doc
99+
const rhinoObject = decodeItem(branch[j])
100+
if (rhinoObject !== null) {
101+
// console.log(rhinoObject)
102+
doc.objects().add(rhinoObject, null)
103+
}
104+
}
105+
}
106+
}
107+
108+
if (doc.objects().count < 1) {
109+
console.error('No rhino objects to load!')
110+
showSpinner(false)
111+
return
112+
}
113+
114+
// hack (https://github.com/mcneel/rhino3dm/issues/353)
115+
//doc.objects().addSphere(new rhino.Sphere([0,0,0], 0.001), null)
116+
117+
// load rhino doc into three.js scene
118+
const buffer = new Uint8Array(doc.toByteArray()).buffer
119+
loader.parse( buffer, function ( object )
120+
{
121+
// clear objects from scene
122+
scene.traverse(child => {
123+
if (!child.isLight) {
124+
scene.remove(child)
125+
}
126+
})
127+
128+
///////////////////////////////////////////////////////////////////////
129+
130+
// render wireframe mesh
131+
object.traverse(child => {
132+
if (child.isLine) {
133+
if (child.userData.attributes.geometry.userStringCount > 0) {
134+
//console.log(child.userData.attributes.geometry.userStrings[0][1])
135+
const col = child.userData.attributes.geometry.userStrings[0][1]
136+
const threeColor = new THREE.Color( "rgb(" + col + ")")
137+
const mat = new THREE.LineBasicMaterial({color:threeColor})
138+
child.material = mat
139+
}
140+
}
141+
}, false)
142+
143+
///////////////////////////////////////////////////////////////////////
144+
145+
146+
147+
148+
// add object graph from rhino model to three.js scene
149+
scene.add( object )
150+
151+
// hide spinner and enable download button
152+
showSpinner(false)
153+
//downloadButton.disabled = false
154+
155+
// zoom to extents
156+
zoomCameraToSelection(camera, controls, scene.children)
157+
})
158+
}
159+
160+
/**
161+
* Attempt to decode data tree item to rhino geometry
162+
*/
163+
function decodeItem(item) {
164+
const data = JSON.parse(item.data)
165+
if (item.type === 'System.String') {
166+
// hack for draco meshes
167+
try {
168+
return rhino.DracoCompression.decompressBase64String(data)
169+
} catch {} // ignore errors (maybe the string was just a string...)
170+
} else if (typeof data === 'object') {
171+
return rhino.CommonObject.decode(data)
172+
}
173+
return null
174+
}
175+
176+
/**
177+
* Called when a slider value changes in the UI. Collect all of the
178+
* slider values and call compute to solve for a new scene
179+
*/
180+
function onSliderChange () {
181+
// show spinner
182+
showSpinner(true)
183+
compute()
184+
}
185+
186+
/**
187+
* Shows or hides the loading spinner
188+
*/
189+
function showSpinner(enable) {
190+
if (enable)
191+
document.getElementById('loader').style.display = 'block'
192+
else
193+
document.getElementById('loader').style.display = 'none'
194+
}
195+
196+
// BOILERPLATE //
197+
198+
var scene, camera, renderer, controls
199+
200+
function init () {
201+
202+
// Rhino models are z-up, so set this as the default
203+
THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 0, 1 );
204+
205+
scene = new THREE.Scene()
206+
scene.background = new THREE.Color(1,1,1)
207+
camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 1, 1000 )
208+
camera.position.x = 50
209+
camera.position.y = 50
210+
camera.position.z = 50
211+
212+
renderer = new THREE.WebGLRenderer({antialias: true})
213+
renderer.setPixelRatio( window.devicePixelRatio )
214+
renderer.setSize( window.innerWidth, window.innerHeight )
215+
document.body.appendChild(renderer.domElement)
216+
217+
controls = new OrbitControls( camera, renderer.domElement )
218+
219+
window.addEventListener( 'resize', onWindowResize, false )
220+
221+
animate()
222+
}
223+
224+
var animate = function () {
225+
requestAnimationFrame( animate )
226+
renderer.render( scene, camera )
227+
}
228+
229+
function onWindowResize() {
230+
camera.aspect = window.innerWidth / window.innerHeight
231+
camera.updateProjectionMatrix()
232+
renderer.setSize( window.innerWidth, window.innerHeight )
233+
animate()
234+
}
235+
236+
/**
237+
* Helper function that behaves like rhino's "zoom to selection", but for three.js!
238+
*/
239+
function zoomCameraToSelection( camera, controls, selection, fitOffset = 1.2 ) {
240+
241+
const box = new THREE.Box3();
242+
243+
for( const object of selection ) {
244+
if (object.isLight) continue
245+
box.expandByObject( object );
246+
}
247+
248+
const size = box.getSize( new THREE.Vector3() );
249+
const center = box.getCenter( new THREE.Vector3() );
250+
251+
const maxSize = Math.max( size.x, size.y, size.z );
252+
const fitHeightDistance = maxSize / ( 2 * Math.atan( Math.PI * camera.fov / 360 ) );
253+
const fitWidthDistance = fitHeightDistance / camera.aspect;
254+
const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance );
255+
256+
const direction = controls.target.clone()
257+
.sub( camera.position )
258+
.normalize()
259+
.multiplyScalar( distance );
260+
controls.maxDistance = distance * 10;
261+
controls.target.copy( center );
262+
263+
camera.near = distance / 100;
264+
camera.far = distance * 100;
265+
camera.updateProjectionMatrix();
266+
camera.position.copy( controls.target ).sub(direction);
267+
268+
controls.update();
269+
270+
}

src/files/metaballTable.gh

20.7 KB
Binary file not shown.

0 commit comments

Comments
 (0)