Tue, 04 Apr 2017 00:47:22 +0200
web viewer 3d finished
cylindertransport-web.py | file | annotate | diff | comparison | revisions | |
cylindertransport.py | file | annotate | diff | comparison | revisions | |
data.py | file | annotate | diff | comparison | revisions | |
stlviewer.css | file | annotate | diff | comparison | revisions | |
stlviewer.html | file | annotate | diff | comparison | revisions | |
stlviewer.js | file | annotate | diff | comparison | revisions |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cylindertransport-web.py Tue Apr 04 00:47:22 2017 +0200 @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +import cgi, data, json, config +from cylindertransport import CylinderSpacerCalculator + +# enable debugging +import cgitb, sys +sys.stderr = sys.stdout +#cgitb.enable() + + +def do_action(args): + if args["action"].value == "calculate": + cylinders = args.getlist("cylinders[]") + calc = CylinderSpacerCalculator(cylinders) + calc.calculate() + print json.dumps({ + "objects": calc.circles, + "scale3d": config.SCALE3D + }) + else: + print "unknown Action %s" % args["action"].value + + + + + + +print("Content-Type: text/html;charset=utf-8") +print("") + +args = cgi.FieldStorage() +if "action" in args: + do_action(args) +else: + # display the html content + content = open("stlviewer.html", "r").read() + cyls = "" + for cyl in sorted(data.CYLINDER.keys()): + cyls += "<li key=\"%s\" weight=\"%s\">%s</li>" % ( + cyl, data.CYLINDER[cyl][3], data.CYLINDER[cyl][4]); + content = content.replace("<!-- PLACEHOLDER CYLINDERS -->", cyls) + + print content
--- a/cylindertransport.py Mon Apr 03 20:04:15 2017 +0200 +++ b/cylindertransport.py Tue Apr 04 00:47:22 2017 +0200 @@ -52,30 +52,36 @@ x_2 = offset(r_2, i) posx = (x_1 + x_2) - (r_1 + r_2) if posx >= self.space_min: - print "// %s Pipe (%.1fmm), Cylinder spacing: %imm" % ( - pipe[0], pipe[1], posx) - return [r_1, i, r_2, pipe[0]] + #print "// %s Pipe (%.1fmm), Cylinder spacing: %imm" % ( + # pipe[0], pipe[1], posx) + return [r_1, i, r_2, pipe] print "// Abort: no suitable pipe found" sys.exit(1) - def _circle(self, posx, radius, txt="", size=1.0): + def _circle(self, posx, radius, data, cylinder = ""): """ Push the circle definition for later rendering """ + if cylinder == "": + size = 0.5 + txt = data[0] + else: + size = 1.0 + txt = "Tank " + cylinder self.circles.append([ - posx, radius, txt, size + posx, radius, txt, size, data, cylinder ]) - def _calculate(self): + def calculate(self): """ Calculate all cylinder and spacer circles """ # first bottle spacer r_1 = CYLINDER[self.cylinders[0]][0] / 2 r_2 = CYLINDER[self.cylinders[1]][0] / 2 - r_1, r_2, r_3, label = self.calc_min(r_1, r_2) + r_1, r_2, r_3, data = self.calc_min(r_1, r_2) posx = self.margin + r_2 # start offset x - self._circle(posx, r_2, label, 0.5) + self._circle(posx, r_2, data) self.scad["spacer"] += "spacer(%f, %f, %f, %f);\n" % ( posx * SCALE3D, r_2 * SCALE3D, r_3 * SCALE3D, CYLINDER[self.cylinders[0]][1] * SCALE3D) @@ -84,16 +90,17 @@ for i in range(0, len(self.cylinders) - 1): r_1 = CYLINDER[self.cylinders[i]][0] / 2 r_2 = CYLINDER[self.cylinders[i+1]][0] / 2 - r_1, r_2, r_3, label = self.calc_min(r_1, r_2) + r_1, r_2, r_3, data = self.calc_min(r_1, r_2) # draw cylinder - self._circle(posx, r_1, "Tank " + self.cylinders[i]) + self._circle(posx, r_1, + CYLINDER[self.cylinders[i]], self.cylinders[i]) self.scad["cylinder"] += "tank(%f, %f, %f);\n" % ( posx * SCALE3D, r_1 * SCALE3D, CYLINDER[self.cylinders[i]][1] * SCALE3D) sx1 = posx + r_1 posx += offset(r_1, r_2) # draw right spacer - self._circle(posx, r_2, label, 0.5) + self._circle(posx, r_2, data) self.scad["spacer"] += "spacer(%f, %f, %f, %f);\n" % ( posx * SCALE3D, r_2 * SCALE3D, r_1 * SCALE3D, CYLINDER[self.cylinders[i]][1] * SCALE3D) @@ -101,7 +108,8 @@ sx2 = posx - r_3 if i == (len(self.cylinders) - 2): # draw last bottle - self._circle(posx, r_3, "Tank " + self.cylinders[i + 1]) + self._circle(posx, r_3, + CYLINDER[self.cylinders[i + 1]], self.cylinders[i + 1]) self.scad["cylinder"] += "tank(%f, %f, %f);\n" % ( posx * SCALE3D, r_3 * SCALE3D, CYLINDER[self.cylinders[i + 1]][1] * SCALE3D) @@ -110,7 +118,7 @@ self.spacings.append([sx1, sx2]) # last bottle spacer pipe - self._circle(posx, r_2, label, 0.5) + self._circle(posx, r_2, data) self.scad["spacer"] += "spacer(%f, %f, %f, %f);\n" % ( posx * SCALE3D, r_2 * SCALE3D, r_3 * SCALE3D, CYLINDER[self.cylinders[-1]][1] * SCALE3D) @@ -129,11 +137,11 @@ """ Start the calculation and return rendered PIL image object """ - self.width = self._calculate() + self.width = self.calculate() image = Image.new('1', (self.width, 250)) # create new image draw = ImageDraw.Draw(image) # draw calculated circles - for posx, radius, txt, size in self.circles: + for posx, radius, txt, size, data, cylinder in self.circles: draw.arc([posx - radius, self.margin, \ posx + radius, 2 * radius + self.margin], \ 0, 360, 'white') @@ -189,7 +197,8 @@ if (options.scad != ""): with open(options.scad, "w") as fd: fd.write(worker.scad["tmpl"]) - fd.write("translate([%f,0,0]) {\n" % (((worker.width - 2 * worker.margin) / -2)*worker.scale3d)) # center the object + fd.write("translate([%f,0,0]) {\n" % ( + ((worker.width - 2 * worker.margin) / -2) * SCALE3D)) # center the object fd.write(worker.scad["cylinder"]) fd.write(worker.scad["spacer"]) fd.write("}\n")
--- a/data.py Mon Apr 03 20:04:15 2017 +0200 +++ b/data.py Tue Apr 04 00:47:22 2017 +0200 @@ -1,16 +1,18 @@ +# -*- coding: UTF-8 -*- + # Durchmesser, Laenge, Volumen und Gewicht der verfuegbaren Tauchflaschen CYLINDER = { - "03" : [100, 515, 3, 4.7], - "05" : [140, 466, 5, 5.7], - "07" : [140, 605, 7, 8.8], - "08" : [171, 490, 8, 10.4], - "10" : [171, 595, 10, 12.4], - "12S" : [204, 550, 12, 15.4], - "12L" : [171, 690, 12, 14.5], - "15" : [204, 640, 15, 18.1], - "16" : [204, 670, 16, 19.5], - "18" : [204, 710, 18, 20.5], - "20" : [204, 810, 20, 22.0], + "03" : [100, 515, 3, 4.7, "Stahl, 3ℓ"], + "05" : [140, 466, 5, 5.7, "Stahl, 5ℓ"], + "07" : [140, 605, 7, 8.8, "Stahl, 7ℓ"], + "08" : [171, 490, 8, 10.4, "Stahl, 8ℓ"], + "10" : [171, 595, 10, 12.4, "Stahl, 10ℓ"], + "12S" : [204, 550, 12, 15.4, "Stahl, 12ℓ kurz"], + "12L" : [171, 690, 12, 14.5, "Stahl, 12ℓ lang"], + "15" : [204, 640, 15, 18.1, "Stahl, 15ℓ"], + "16" : [204, 670, 16, 19.5, "Stahl, 16ℓ"], + "18" : [204, 710, 18, 20.5, "Stahl, 18ℓ"], + "20" : [204, 810, 20, 22.0, "Stahl, 20ℓ"], } # Standard Rohrdurchmesser (Mr. Baumarkt)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stlviewer.css Tue Apr 04 00:47:22 2017 +0200 @@ -0,0 +1,54 @@ + body { + font-family: Monospace; + background-color: #000000; + margin: 0px; + overflow: hidden; + } + + #info { + color: #fff; + position: absolute; + top: 10px; + width: 100%; + text-align: center; + z-index: 100; + display:block; + + } + + a { color: skyblue } + .button { background:#999; color:#eee; padding:0.2em 0.5em; cursor:pointer } + .highlight { background:orange; color:#fff; } + + span { + display: inline-block; + width: 60px; + float: left; + text-align: center; + } + + + #sortable1, #sortable2 { + border: 1px solid #eee; + width: 142px; + min-height: 20px; + list-style-type: none; + margin: 0; + padding: 5px 0 0 0; + float: left; + margin-right: 10px; + } + #sortable1 li, #sortable2 li { + margin: 0 5px 5px 5px; + padding: 5px; + font-size: 1.2em; + width: 120px; + background: rgba(0,0,0,0.5); + } + +.listcontainer { + width: 157px; + height: 300px; + overflow-y: scroll; + overflow-x: hidden; +} \ No newline at end of file
--- a/stlviewer.html Mon Apr 03 20:04:15 2017 +0200 +++ b/stlviewer.html Tue Apr 04 00:47:22 2017 +0200 @@ -4,50 +4,43 @@ <title>ScubaTools Object Viewer</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> - <style> - body { - font-family: Monospace; - background-color: #000000; - margin: 0px; - overflow: hidden; - } - #info { - color: #fff; - position: absolute; - top: 10px; - width: 100%; - text-align: center; - z-index: 100; - display:block; - - } + <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" /> + <link rel="stylesheet" href="stlviewer.css" /> + <script src="https://code.jquery.com/jquery-1.12.4.js"></script> + <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> - a { color: skyblue } - .button { background:#999; color:#eee; padding:0.2em 0.5em; cursor:pointer } - .highlight { background:orange; color:#fff; } + <script src="https://threejs.org/build/three.js"></script> + <script src="https://threejs.org/examples/js/loaders/STLLoader.js"></script> + <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script> - span { - display: inline-block; - width: 60px; - float: left; - text-align: center; - } - - </style> </head> <body> <div id="info"> + <div class="listcontainer"> + <h2>Available:</h2> + <ul id="sortable1"> + <!-- PLACEHOLDER CYLINDERS --> + </ul> + </div> + <div class="listcontainer"> + <h2>Selected:</h2> + <ul id="sortable2"> + + </ul> + </div> + <a href="https://neo-soft.org" target="_blank">NeoSoft</a> ScubaTools - <a href="https://threejs.org/" target="_blank">three.js</a> - STL loader by <a href="https://github.com/aleeper" target="_blank">aleeper</a>. <br/> Left mouse: rotate camera, right mouse: move camera, middle mouse or wheel: zoom + + </div> - <script src="https://threejs.org/build/three.js"></script> - <script src="https://threejs.org/examples/js/loaders/STLLoader.js"></script> - <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script> + <script src="stlviewer.js"></script> + <script> var container; var camera, cameraTarget, scene, renderer; @@ -55,127 +48,6 @@ init(); animate(); - function init() { - container = document.createElement( 'div' ); - document.body.appendChild( container ); - - camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 15 ); - camera.position.set( 3, 0.35, 3 ); - - cameraTarget = new THREE.Vector3( 0, -0.25, 0 ); - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog( 0x72645b, 2, 15 ); - - var controls = new THREE.OrbitControls(camera); - - // Ground - - var plane = new THREE.Mesh( - new THREE.PlaneBufferGeometry( 40, 40 ), - new THREE.MeshPhongMaterial( { color: 0x999999, specular: 0x101010 } ) - ); - plane.rotation.x = -Math.PI/2; - plane.position.y = -0.5; - scene.add( plane ); - - plane.receiveShadow = true; - - // ASCII file - - var loader = new THREE.STLLoader(); - loader.load( './test.stl', function ( geometry ) { - - var material = new THREE.MeshPhongMaterial( { color: 0xff5533, specular: 0x111111, shininess: 200 } ); - var mesh = new THREE.Mesh( geometry, material ); - - mesh.position.set( 0, -0.25, 0 ); - mesh.rotation.set( 0, Math.PI / 2, 0 ); - mesh.scale.set( 0.01, 0.01, 0.01 ); - - mesh.castShadow = true; - //mesh.receiveShadow = true; - - scene.add( mesh ); - - } ); - - // Lights - - scene.add( new THREE.HemisphereLight( 0x443333, 0x111122 ) ); - - addShadowedLight( 1, 1, 1, 0xffffff, 1.35 ); - //addShadowedLight( 0.5, 1, -1, 0xffaa00, 1 ); - // renderer - - renderer = new THREE.WebGLRenderer( { antialias: true } ); - renderer.setClearColor( scene.fog.color ); - renderer.setPixelRatio( window.devicePixelRatio ); - renderer.setSize( window.innerWidth, window.innerHeight ); - - renderer.gammaInput = true; - renderer.gammaOutput = true; - - renderer.shadowMap.enabled = true; - renderer.shadowMap.renderReverseSided = false; - - container.appendChild( renderer.domElement ); - - window.addEventListener( 'resize', onWindowResize, false ); - - } - - function addShadowedLight( x, y, z, color, intensity ) { - - var directionalLight = new THREE.DirectionalLight( color, intensity ); - directionalLight.position.set( x, y, z ); - scene.add( directionalLight ); - - directionalLight.castShadow = true; - - var d = 1; - directionalLight.shadow.camera.left = -d; - directionalLight.shadow.camera.right = d; - directionalLight.shadow.camera.top = d; - directionalLight.shadow.camera.bottom = -d; - - directionalLight.shadow.camera.near = 1; - directionalLight.shadow.camera.far = 4; - - directionalLight.shadow.mapSize.width = 1024; - directionalLight.shadow.mapSize.height = 1024; - - directionalLight.shadow.bias = -0.005; - - } - - function onWindowResize() { - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize( window.innerWidth, window.innerHeight ); - - } - - function animate() { - - requestAnimationFrame( animate ); - - //render(); - renderer.render( scene, camera ); - //stats.update(); - - } - - function render() { - var timer = Date.now() * 0.0005; - camera.position.x = Math.cos( timer ) * 3; - camera.position.z = Math.sin( timer ) * 3; - camera.lookAt( cameraTarget ); - renderer.render( scene, camera ); - } - </script><div><canvas style="width: 1589px; height: 711px;" height="711" width="1589"></canvas><div style="position: fixed; top: 0px; left: 0px; cursor: pointer; opacity: 0.9; z-index: 10000;"><canvas style="width: 80px; height: 48px; display: block;" height="48" width="80"></canvas><canvas style="width: 80px; height: 48px; display: none;" height="48" width="80"></canvas></div></div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stlviewer.js Tue Apr 04 00:47:22 2017 +0200 @@ -0,0 +1,199 @@ +$( function() { + $( "#sortable1 li" ).draggable({ + helper: "clone", + containment:"document" + }); + $( "#sortable2" ).droppable({ + accept: "#sortable1 li", + drop: function( event, ui ) { + ui.draggable.clone(false).appendTo($(this)); + for (i =0; i<10; i++) clear_scene(); + window.setTimeout(update_scene,500); + } + }); + $( "#sortable2" ).sortable({ + placeholder: "ui-state-highlight", + start: clear_scene, + stop: function() { + for (i =0; i<10; i++) clear_scene(); + window.setTimeout(update_scene,500) + } + }); + $( "#sortable2" ).disableSelection(); +} ); + +function clear_scene() { + scene.children.forEach(function(v){ + if(v.stlfile === true) { + v.material.dispose(); + v.geometry.dispose(); + scene.remove(v); + } + //scene.remove(object); + }); +} + +function update_scene() { + var cylinders = []; + $("#sortable2 li").each(function(idx, e){ + cylinders.push($(e).attr('key')); + }); + if (cylinders.length < 2) return; + + // fetch new objects list + $.ajax({ + url: "#", + method: 'post', + dataType: 'json', + data: { + action: 'calculate', + cylinders: cylinders + }, + success: function(data) { + //console.log(data); + // remove all meshes + clear_scene(); + // append the objects with positioning + for (i = 0; i<data.objects.length; i++) { + var obj = data.objects[i]; + if (obj[5] == "") { + // spacer + filename = "stl/spacer_" + obj[4][0] + '.stl'; + } else { + // cylinder + filename = "stl/cylinder_" + obj[5] + '.stl'; + } + position = [data.scale3d * 0.01 * obj[0], 0, 0]; + loadSTL(filename, null, position); + } + + } + }); +} + +function loadSTL(filename, material, position, rotation, scale) { + //console.log(filename, position); + var loader = new THREE.STLLoader(); + loader.load( filename, function ( geometry ) { + if (!material) + var material = new THREE.MeshPhongMaterial( { + color: 0xff5533, specular: 0x111111, shininess: 200 } ); + var mesh = new THREE.Mesh( geometry, material ); + mesh["stlfile"] = true; + if (!position) mesh.position.set( 0, -0.25, 0 ); else + mesh.position.set( position[0], position[1], position[2] ); + if (!rotation) mesh.rotation.set( 0, 0, 0 ); else + mesh.rotation.set( rotation[0], rotation[1], rotation[2] ); + if (!scale) mesh.scale.set( 0.01, 0.01, 0.01 ); else + mesh.scale.set( scale[0], scale[1], scale[2] ); + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add( mesh ); + + } ); + +} + +function init() { + container = document.createElement( 'div' ); + document.body.appendChild( container ); + + camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 20 ); + camera.position.set( 0.5, 0.35, 2 ); + + cameraTarget = new THREE.Vector3( 0, -0.25, 0 ); + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog( 0x72645b, 2, 15 ); + + var controls = new THREE.OrbitControls(camera); + + // Ground + + var plane = new THREE.Mesh( + new THREE.PlaneBufferGeometry( 40, 40 ), + new THREE.MeshPhongMaterial( { color: 0x999999, specular: 0x101010 } ) + ); + plane.rotation.x = -Math.PI/2; + plane.position.y = -0.5; + scene.add( plane ); + + plane.receiveShadow = true; + + // Lights + + scene.add( new THREE.HemisphereLight( 0x443333, 0x111122 ) ); + + addShadowedLight( 1, 1, 1, 0xffffff, 1.35 ); + //addShadowedLight( 0.5, 1, -1, 0xffaa00, 1 ); + // renderer + + renderer = new THREE.WebGLRenderer( { antialias: true } ); + renderer.setClearColor( scene.fog.color ); + renderer.setPixelRatio( window.devicePixelRatio ); + renderer.setSize( window.innerWidth, window.innerHeight ); + + renderer.gammaInput = true; + renderer.gammaOutput = true; + + renderer.shadowMap.enabled = true; + renderer.shadowMap.renderReverseSided = true; + + container.appendChild( renderer.domElement ); + + window.addEventListener( 'resize', onWindowResize, false ); + +} + +function addShadowedLight( x, y, z, color, intensity ) { + + var directionalLight = new THREE.DirectionalLight( color, intensity ); + directionalLight.position.set( x, y, z ); + scene.add( directionalLight ); + + directionalLight.castShadow = true; + + var d = 5; + directionalLight.shadow.camera.left = -d; + directionalLight.shadow.camera.right = d; + directionalLight.shadow.camera.top = d; + directionalLight.shadow.camera.bottom = -d; + + directionalLight.shadow.camera.near = 1; + directionalLight.shadow.camera.far = 20; + + directionalLight.shadow.mapSize.width = 1024; + directionalLight.shadow.mapSize.height = 1024; + + directionalLight.shadow.bias = -0.005; + +} + +function onWindowResize() { + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize( window.innerWidth, window.innerHeight ); + +} + +function animate() { + + requestAnimationFrame( animate ); + + //render(); + renderer.render( scene, camera ); + //stats.update(); + +} + +function render() { + var timer = Date.now() * 0.0005; + camera.position.x = Math.cos( timer ) * 3; + camera.position.z = Math.sin( timer ) * 3; + camera.lookAt( cameraTarget ); + renderer.render( scene, camera ); +} \ No newline at end of file