├── .gitignore ├── 3d.js ├── LICENSE ├── README.md ├── cone.js ├── cube.js ├── foil.js ├── hex-socket.js ├── hex.js ├── index.js ├── midpoint.js ├── oar.js ├── output ├── hex-socket.stl ├── hex.stl ├── pulley.stl ├── ratchet-cast.stl ├── ratchet.stl └── winch.stl ├── package.json ├── parabola.js ├── pulley.js ├── ratchet-cast.js ├── ratchet.js ├── screenshot-colors.png ├── screenshot.png ├── screenshot_hires-curve.png ├── smooth.js ├── splines.js ├── stat.js ├── stations.js ├── triangulate.js ├── vane-base.js └── winch.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /3d.js: -------------------------------------------------------------------------------- 1 | var m = require('./midpoint') 2 | 3 | var points = [ 4 | [0,0,3], 5 | [3,10,0], 6 | [0,20,1] 7 | ] 8 | 9 | module.exports = function (_) { 10 | var s = m.smooth(points) 11 | return _.CSG.Polygon.createFromPoints(s, null, 12 | _.CSG.Plane.fromPoints.apply(null, s.slice().reverse()) 13 | ).extrude(new _.CSG.Vector3D(0,0,1)) 14 | } 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 'Dominic Tarr' 2 | 3 | Permission is hereby granted, free of charge, 4 | to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, 8 | merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 20 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cad2 2 | 3 | This is just some scripts I wrote to mess around with CSG cad stuff, 4 | I pulled stuff out of openjscad, to make it a bit more node.js. 5 | (such as removed the built in code editor, and made it so you could 6 | maybe publish cad modules on npm. I mainly want this for building 7 | boats from plywood so I jumbled together some code about generating 8 | curves that look like they are made from bending wood. The next thing 9 | I need is an algorithm for _developing_ a model, i.e. laying the polygons 10 | out flat (so they can be cut from plywood) 11 | 12 | ## usage 13 | 14 | ``` 15 | git clone https://github.com/dominictarr/cad2 16 | cd cad2 17 | npm install 18 | npm install electro -g 19 | electro index.js smooth.js 20 | #there are some other files that also have stuff you can run like this 21 | ``` 22 | 23 | ![half model dinghy](./screenshot.png) 24 | 25 | ## License 26 | 27 | MIT 28 | -------------------------------------------------------------------------------- /cone.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function (_) { 3 | return _.CAG.fromPoints([[0,0,0], [0, 1, 0], [10, 0, 0]]).rotateExtrude() 4 | //.extrude(1) 5 | } 6 | 7 | -------------------------------------------------------------------------------- /cube.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = function (_) { 4 | return M = _.CSG.cylinder({center: [0,0,0], size: 20, resolution: 6}).scale([20,20,20]).union(_.CSG.cylinder({ 5 | center: [0,0,0], size: 1 6 | }).scale([1,100,1])) 7 | } 8 | -------------------------------------------------------------------------------- /foil.js: -------------------------------------------------------------------------------- 1 | 2 | var naca = require('../naca') 3 | 4 | module.exports = function (_) { 5 | 6 | CSG = _.CSG 7 | 8 | function poly (points) { 9 | return CSG.Polygon.createFromPoints(points, null, CSG.Plane.fromPoints.apply(null, points)) 10 | } 11 | 12 | var points = [] 13 | var width = 12, t = 0.20, N=50 14 | for(var i = 0; i <= N; i++) { 15 | var x = i/N 16 | points.push([x*width, naca(t, x)*width, 0]) 17 | } 18 | points.push([0, 0, 0]) 19 | 20 | return poly(points) 21 | .extrude(new CSG.Vector3D(0,0, 65), 1) 22 | .rotate([0,0,0], [1,0,0], 90) 23 | // .subtract( 24 | // CSG.cube({ 25 | // corner1: [6, 1.2, -1], 26 | // corner2: [7, 0, 2], 27 | // }) 28 | // ) 29 | .unionForNonIntersecting( 30 | CSG.cube({ 31 | corner1: 32 | [0, 0, 0], 33 | corner2: 34 | [12, 10, 1.2] 35 | }) 36 | .subtract( 37 | CSG.cube({ 38 | corner1: [0, 0.6, 0], 39 | corner2: [6, 10, 1.2] 40 | }) 41 | ) 42 | .subtract( 43 | CSG.cube({ 44 | corner1: [6, 1.2, -1], 45 | corner2: [6+0.3, 0.6, 2], 46 | }) 47 | ) 48 | ) 49 | } 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /hex-socket.js: -------------------------------------------------------------------------------- 1 | function outer(size) { 2 | return size - 0.2 3 | } 4 | 5 | function inner(size) { 6 | return size + 0.2 7 | } 8 | 9 | module.exports = function (_) { 10 | var Hex = require('./hex').create(_) 11 | 12 | function cyl (width, height) { 13 | return _.CSG.cylinder({ 14 | start: [0,0,0], 15 | end: [0,0,height], 16 | radius: width/2, 17 | resolution: 180 18 | }) 19 | } 20 | 21 | return cyl(outer(25.4), 20) 22 | .subtract(Hex({ 23 | radius: inner(12.7/2), 24 | start: [0,0,-10], 25 | end: [0,0,30] 26 | })) 27 | } 28 | -------------------------------------------------------------------------------- /hex.js: -------------------------------------------------------------------------------- 1 | 2 | function create (_) { 3 | return function (opts) { 4 | 5 | function side (flat_radius) { 6 | return 2 * (flat_radius / Math.sqrt(3)) 7 | } 8 | var _opts = Object.assign({ 9 | }, opts, {radius: side(opts.radius || 10), resolution: 6}) 10 | console.log(_opts) 11 | return _.CSG.cylinder(_opts) 12 | return _.CSG.cylinder() 13 | 14 | } 15 | } 16 | 17 | function out (size) { 18 | return size - 0.2 19 | } 20 | 21 | module.exports = function (_) { 22 | 23 | var Hex = create(_) 24 | return Hex({ 25 | radius: out(12.7/2), 26 | start: [0,0,0], 27 | end: [0,0,20], 28 | }) 29 | } 30 | 31 | module.exports.create = create 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var csg = require('@jscad/csg') 2 | var path = require('path') 3 | var container = document.createElement('div') 4 | function resize () { 5 | container.style.height = (window.innerHeight - 4)+'px' 6 | container.style.width = (window.innerWidth - 4)+'px' 7 | } 8 | document.body.style.margin = '0px' 9 | 10 | CSG = csg.CSG 11 | CAG = csg.CAG 12 | 13 | window.onresize = resize 14 | resize() 15 | 16 | document.body.appendChild(container) 17 | 18 | var opts = require('minimist')(process.argv.slice(2)) 19 | 20 | var v = new (require('jscad-viewer'))(container, opts) 21 | 22 | var file = path.basename(process.argv[2]) 23 | var stl_file = file.substring(0, file.indexOf(path.extname(file))) + '.stl' 24 | var stl_path = path.join(__dirname, 'output', stl_file) 25 | var model = require(path.resolve(process.cwd(), process.argv[2]))(csg) 26 | V = v 27 | v.setCsg(model) 28 | 29 | var data = require('@jscad/stl-serializer').serialize(model) 30 | 31 | require('fs') 32 | .writeFileSync(stl_path, (Buffer.concat(data.map(function (ab) { 33 | return Buffer.from(ab) 34 | })))) 35 | console.log('wrote', stl_path) 36 | 37 | //console.log(Buffer.concat(.map(Buffer.from) )) 38 | 39 | //require('fs').writeFileSync('output.stl', 40 | // require('@jscad/stl-serializer')(model) 41 | //) 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /midpoint.js: -------------------------------------------------------------------------------- 1 | var Cardinal = require('cardinal-spline-js') 2 | 3 | function midpointY (a, b, y) { 4 | if(y < a[1] || y > b[1]) throw new Error('out of bounds in y axis') 5 | var _y = b[1] - a[1] 6 | var m = _y/(y-a[1]) 7 | return [a[0] + (b[0]-a[0])/m, y] 8 | } 9 | 10 | function midpointX (a, b, x) { 11 | if(x < a[0] || x > b[0]) throw new Error('out of bounds in x axis') 12 | var _x = b[0] - a[0] 13 | var m = _x/(y-a[0]) 14 | return [x, a[1] + (b[1]-a[1])/m] 15 | } 16 | 17 | exports.midpointX = midpointX 18 | exports.midpointY = midpointY 19 | 20 | function snap (points, size, dimension) { 21 | dimension = 1 22 | var output = [], max = points[0][dimension] 23 | for(var i = 1; i < points.length; i ++) 24 | while(max <= points[i][dimension]) { 25 | output.push(midpointY(points[i-1], points[i], max)) 26 | max += size 27 | } 28 | 29 | return output 30 | } 31 | 32 | exports.snapY = snap 33 | 34 | function smooth2D (points, tension, sides) { 35 | tension = tension || 0.5 36 | sides = sides || 5 37 | var p = Cardinal.getCurvePoints(points.reduce(function (a, b) { 38 | return a.concat(b) 39 | }), tension, sides) 40 | 41 | var o = [] 42 | for(var i = 0; i < p.length; i+=2) 43 | o.push([p[i],p[i+1]]) 44 | 45 | return o 46 | } 47 | 48 | var m = require('./midpoint') 49 | 50 | function smooth (points, step, tension) { 51 | step = step || 1 52 | var smoothX = m.snapY(smooth2D(points.map(function (e) { 53 | return [e[0],e[1]] 54 | }), tension, 10), step) 55 | var smoothZ = m.snapY(smooth2D(points.map(function (e) { 56 | return [e[2],e[1]] 57 | }), tension, 10), step) 58 | 59 | return smoothX.map(function (e, i) { 60 | return [e[0],e[1], smoothZ[i][0]] 61 | }) 62 | } 63 | 64 | exports.smooth = smooth 65 | -------------------------------------------------------------------------------- /oar.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var CSG = require('@jscad/csg') 4 | var m = require('./midpoint') 5 | 6 | var Cardinal = require('cardinal-spline-js') 7 | var stations = [ 8 | [ 9 | [0,0,1], 10 | [4.5,0,0.7], 11 | [4.5,0,-0.7], 12 | [0,0,-1] 13 | ], 14 | [ 15 | [0, 60 , 1.5], 16 | [4.5,60 , 0.7], 17 | [4.5,60 ,-0.7], 18 | [0, 60 ,-1.5] 19 | ], 20 | [ 21 | [0, 90, 2], 22 | [2, 90, 0.25], 23 | [2, 90, 0.25], 24 | [0, 90, -2], 25 | ], 26 | [ 27 | [1, 120, 0], 28 | [1, 120, -0.1], 29 | [1, 120, -0.1], 30 | [1, 120, 0] 31 | ] 32 | ] 33 | 34 | var smooth = require('./midpoint').smooth 35 | 36 | var chines = stations[0].map(function (_chine, i) { 37 | return smooth(stations.map(function (station) { 38 | return station[i] 39 | }), 10, 0.5) 40 | }) 41 | 42 | console.log('chines', JSON.stringify(chines)) 43 | 44 | var stations2 = chines[0].map(function (e, i) { 45 | return chines.map(function (chine, j) { 46 | return chine[i]//.concat(stations[0][j][2]) 47 | }) 48 | }) 49 | 50 | module.exports = function (_) { 51 | function poly (out) { 52 | return _.CSG.Polygon.createFromPoints(out) //, null, _.CSG.Plane.fromPoints.apply(null, out.slice().reverse())) 53 | } 54 | var blade = require('./stations')(_)(stations.map(poly)) 55 | var handle = _.CSG.cylinder({size: 1, length: 100}).scale({x: 2, y:100, z: 2}) 56 | return blade 57 | 58 | // var blade = _.CSG.cube({}).scale({x:9, y: 60, z: 4}) 59 | // return handle.union(blade) 60 | // return blade.union(handle) 61 | } 62 | 63 | -------------------------------------------------------------------------------- /output/hex-socket.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dominictarr/cad2/dadfd5fd35e3db6645f350f1007c67e2fb629144/output/hex-socket.stl -------------------------------------------------------------------------------- /output/hex.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dominictarr/cad2/dadfd5fd35e3db6645f350f1007c67e2fb629144/output/hex.stl -------------------------------------------------------------------------------- /output/pulley.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dominictarr/cad2/dadfd5fd35e3db6645f350f1007c67e2fb629144/output/pulley.stl -------------------------------------------------------------------------------- /output/ratchet-cast.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dominictarr/cad2/dadfd5fd35e3db6645f350f1007c67e2fb629144/output/ratchet-cast.stl -------------------------------------------------------------------------------- /output/ratchet.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dominictarr/cad2/dadfd5fd35e3db6645f350f1007c67e2fb629144/output/ratchet.stl -------------------------------------------------------------------------------- /output/winch.stl: -------------------------------------------------------------------------------- 1 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cad2", 3 | "description": "", 4 | "version": "1.0.0", 5 | "homepage": "https://github.com/dominictarr/cad2", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/dominictarr/cad2.git" 9 | }, 10 | "dependencies": { 11 | "@jscad/io": "^0.3.12", 12 | "b-spline": "^2.0.1", 13 | "cardinal-spline-js": "^2.3.6", 14 | "jscad-viewer": "^1.0.0", 15 | "minimist": "^1.2.0" 16 | }, 17 | "devDependencies": {}, 18 | "scripts": { 19 | "test": "set -e; for t in test/*.js; do node $t; done" 20 | }, 21 | "author": "'Dominic Tarr' (dominictarr.com)", 22 | "license": "MIT" 23 | } 24 | -------------------------------------------------------------------------------- /parabola.js: -------------------------------------------------------------------------------- 1 | 2 | var A = 0.1, B = -0, C = 0 3 | 4 | function f (x) { 5 | return (3*Math.pow(x, 2)/25 - 3*x/5 + 10) 6 | } 7 | 8 | module.exports = function (_) { 9 | var CSG = _.CSG 10 | 11 | var points = [] 12 | for(var x = -100; x <= 100; x ++) 13 | points.push([x, f(x)]) //A*Math.pow(x, 2) + B*x + C]) 14 | 15 | return CSG.Polygon.createFromPoints(points, null, CSG.Plane.fromPoints.apply(null, points)).extrude(new CSG.Vector3D(0,0,1)) 16 | } 17 | 18 | -------------------------------------------------------------------------------- /pulley.js: -------------------------------------------------------------------------------- 1 | 2 | function o (s) { 3 | return s - 0.2 4 | } 5 | function i (s) { 6 | return s + 0.2 7 | } 8 | 9 | module.exports = function (_) { 10 | function pulley () { 11 | var rope_diameter = o(6); 12 | var pulley_diameter = o(28) 13 | var width = o(9.5) //rope_diameter * 1.2 14 | var axel_diameter = i(8+0.2) 15 | var clearance = 0.5 16 | 17 | var half_width = (width)/2 - clearance 18 | 19 | return ( 20 | _.CAG.rectangle({corner1: [pulley_diameter/2, half_width], corner2: [0, -half_width]}) 21 | .subtract( 22 | _.CAG.circle({radius: rope_diameter/2}).translate([pulley_diameter/2, 0]) 23 | ) 24 | .rotateExtrude({resolution: 90}) 25 | // .extrude([0,0,0.1]) 26 | .union( 27 | _.CSG.cylinder({ 28 | radius: axel_diameter*3/4, 29 | start: [0, 0, -width/2], 30 | end: [0, 0, width/2] 31 | }) 32 | ) 33 | .subtract( 34 | _.CSG.cylinder({ 35 | radius: axel_diameter/2, 36 | start: [0, 0, -pulley_diameter], 37 | end: [0, 0, pulley_diameter] 38 | }) 39 | ) 40 | ) 41 | } 42 | 43 | 44 | return pulley().scale([1,1,1/1.022]) 45 | } 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ratchet-cast.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | //diameter 7.8 4 | //width ratchet 3.9 5 | //length 18.3 6 | //height 12.4 7 | 8 | function out(size) { 9 | return size - 0.2 10 | } 11 | 12 | var radius = out(7.8)/2 13 | var height = 12.4+2 14 | var width = out(3.9) 15 | var length = out(18.3) 16 | var gap = 1.6 17 | 18 | function Ratchet(_) { 19 | return _.CSG.cylinder({ 20 | start: [0, 0, 0], 21 | end: [0, 0, height], 22 | radius: radius 23 | }).union( 24 | _.CSG.cube({ 25 | corner1: [0,0,0], 26 | corner2: [width, length - radius, height] 27 | }) 28 | ) 29 | } 30 | 31 | var space = 10, Z = 1 32 | module.exports = function (_) { 33 | return _.CSG.cube({ 34 | corner1: [0,0,-2], 35 | corner1: [50,40,-5], 36 | }).translate([-10, -10, 0]) 37 | .union(Ratchet(_).translate([space*3, 0,Z])) 38 | .union(Ratchet(_).rotateZ(180).translate([3+space*2, 20 ,Z])) 39 | .union(Ratchet(_).translate([space*1, 0,Z])) 40 | .union(Ratchet(_).rotateZ(180).translate([3, 20,Z])) 41 | 42 | //don't cast gap, so easier to extract mould. can just cut it with a saw, anyway. 43 | //gap for the spring, measured the gap, then position by eye. 44 | // .subtract(_.CSG.cube({ 45 | // corner1: [3.5, -length, height/2 - gap/2], 46 | // corner2: [-20, length, height/2 + gap/2], 47 | // }).rotateZ(20)) 48 | 49 | //decrease height by 1.022 to get correct height 50 | // .scale([1,1,1/1.022]) 51 | } 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /ratchet.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | //diameter 7.8 4 | //width ratchet 3.9 5 | //length 18.3 6 | //height 12.4 7 | 8 | function out(size) { 9 | return size - 0.2 10 | } 11 | 12 | var radius = out(7.8)/2 13 | var height = 12.4 14 | var width = out(3.9) 15 | var length = out(18.3) 16 | var gap = 1.6 17 | 18 | module.exports = function (_) { 19 | return _.CSG.cylinder({ 20 | start: [0, 0, 0], 21 | end: [0, 0, height], 22 | radius: radius 23 | }).union( 24 | _.CSG.cube({ 25 | corner1: [0,0,0], 26 | corner2: [width, length - radius, height] 27 | }) 28 | ) 29 | //gap for the spring, measured the gap, then position by eye. 30 | .subtract(_.CSG.cube({ 31 | corner1: [3.5, -length, height/2 - gap/2], 32 | corner2: [-20, length, height/2 + gap/2], 33 | }).rotateZ(20)) 34 | 35 | //decrease height by 1.022 to get correct height 36 | .scale([1,1,1/1.022]) 37 | } 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /screenshot-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dominictarr/cad2/dadfd5fd35e3db6645f350f1007c67e2fb629144/screenshot-colors.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dominictarr/cad2/dadfd5fd35e3db6645f350f1007c67e2fb629144/screenshot.png -------------------------------------------------------------------------------- /screenshot_hires-curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dominictarr/cad2/dadfd5fd35e3db6645f350f1007c67e2fb629144/screenshot_hires-curve.png -------------------------------------------------------------------------------- /smooth.js: -------------------------------------------------------------------------------- 1 | var CSG = require('@jscad/csg') 2 | var m = require('./midpoint') 3 | 4 | var Cardinal = require('cardinal-spline-js') 5 | var stations = [ 6 | [ 7 | [0,0,1], 8 | [1,0,2], 9 | [2,0,5], 10 | [0,0,5] 11 | ], 12 | [ 13 | [0, 10, 0], 14 | [5, 10, 1], 15 | [6, 10, 4], 16 | [0, 10, 4], 17 | ], 18 | [ 19 | [0, 20, 0.5], 20 | [2, 20, 1.5], 21 | [3, 20, 4.5], 22 | [0, 20, 4.5] 23 | ] 24 | ] 25 | 26 | var smooth = require('./midpoint').smooth 27 | 28 | var chines = stations[0].map(function (_chine, i) { 29 | return smooth(stations.map(function (station) { 30 | return station[i] 31 | }), 1, 0.5) 32 | }) 33 | 34 | console.log('chines', JSON.stringify(chines)) 35 | 36 | var stations2 = chines[0].map(function (e, i) { 37 | return chines.map(function (chine, j) { 38 | return chine[i]//.concat(stations[0][j][2]) 39 | }) 40 | }) 41 | 42 | var CSG = require('@jscad/csg').CSG 43 | 44 | function rainbowByNormal (shape) { 45 | shape.polygons.forEach(function (p) { 46 | var n = p.plane.normal 47 | p.shared = new CSG.Polygon.Shared([(n.x+1)/2,(n.y+1)/2,(n.z+1)/2, 0.5]) 48 | //[Math.random(),Math.random(),Math.random(), 0.5]) 49 | }) 50 | return shape 51 | } 52 | 53 | function eachPair (set, iter) { 54 | for(var i = 0; i < set.length; i += 2) 55 | iter(set[(i-1+set.length)%set.length], set[i], i) 56 | } 57 | 58 | function polygonByVertice (shape) { 59 | var index = {} 60 | shape.polygons.forEach(function (poly) { 61 | poly.vertices.forEach(function (vertex) { 62 | var id = vertex.getTag() //[a.toString(), b.toString()].sort().join(':') 63 | if(!index[id]) index[id] = [poly] 64 | else index[id].push(poly) 65 | }) 66 | }) 67 | return index 68 | } 69 | 70 | function rainbowByPanel (shape, bend) { 71 | console.log(polygonByVertice(shape)) 72 | bend = bend || 0.9 73 | var index = polygonByVertice(shape) 74 | 75 | function addToSide(p, shared) { 76 | if(p.shared.color) return 77 | p.shared = shared 78 | p.vertices.forEach(function (v) { 79 | index[v.getTag()].forEach(function (poly) { 80 | if(poly.plane.normal.dot(p.plane.normal) < bend) return 81 | addToSide(poly, shared) 82 | }) 83 | }) 84 | } 85 | 86 | //only the best colors 87 | var colors = [ 88 | new CSG.Polygon.Shared([0,0,1, 0.5]), 89 | new CSG.Polygon.Shared([0,1,0, 0.5]), 90 | new CSG.Polygon.Shared([1,0,0, 0.5]), 91 | new CSG.Polygon.Shared([0,1,1, 0.5]), 92 | new CSG.Polygon.Shared([1,1,0, 0.5]), 93 | new CSG.Polygon.Shared([1,0,1, 0.5]) 94 | ] 95 | 96 | shape.polygons.forEach(function (poly) { 97 | if(poly.shared.color) return 98 | addToSide(poly, colors.shift()) 99 | }) 100 | 101 | return shape 102 | } 103 | 104 | console.log(JSON.stringify(stations2)) 105 | //console.log(JSON.stringify(o)) 106 | 107 | //take a line of points, and snap to Y dimension in steps of `size` 108 | 109 | //var aligned = m.snapY(o, 2) 110 | 111 | module.exports = function (_) { 112 | function poly (out) { 113 | return _.CSG.Polygon.createFromPoints(out) //, null, _.CSG.Plane.fromPoints.apply(null, out.slice().reverse())) 114 | } 115 | // return M = rainbowByNormal(require('./stations')(_)(stations2.map(poly))) 116 | return M = rainbowByPanel(require('./stations')(_)(stations2.map(poly))) 117 | } 118 | 119 | -------------------------------------------------------------------------------- /splines.js: -------------------------------------------------------------------------------- 1 | var bspline = require('b-spline'); 2 | 3 | var points = [ 4 | [ 0.0, -5], 5 | [-5, -5], 6 | 7 | [-5, 0.0], 8 | [-5, 5], 9 | 10 | [ 0.0, 5], 11 | [ 5, 5], 12 | 13 | [ 5, 0.0], 14 | [ 5, -5], 15 | [ 0.0, -5] // P0 16 | ] 17 | 18 | // Here the curve is called non-uniform as the knots 19 | // are not equally spaced 20 | 21 | var knots = [ 22 | 0, 0, 0, 1/4, 1/4, 1/2, 1/2, 3/4, 3/4, 1, 1, 1 23 | ]; 24 | 25 | var degree = 2; 26 | 27 | module.exports = function (_) { 28 | var CSG = _.CSG 29 | var stations = [] 30 | for(var i = 0; i <= 20; i++) { 31 | var w = Math.pow(2, (i/7) - 1) / 2; 32 | // and rational as its control points have varying weights 33 | var weights = [ 34 | 1, w, 1, w, 1, w, 1, w, 1 35 | ] 36 | 37 | var out = [] 38 | for(var t=0; t<1; t+=0.01) { 39 | out.push(bspline(t, degree, points, knots, weights)) 40 | } 41 | stations.push(CSG.Polygon.createFromPoints(out, null, CSG.Plane.fromPoints.apply(null, out))) 42 | } 43 | var Stations = require('./stations')(_) 44 | return Stations(stations) 45 | } 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /stat.js: -------------------------------------------------------------------------------- 1 | function mirror (station) { 2 | if(!station) return 3 | var side = station.slice().concat( 4 | station.slice().reverse().map(function (e) { 5 | return [e[0], e[1], -e[2]] 6 | }) 7 | ) 8 | return side 9 | return side.concat(side.slice().reverse().map(function (e) { 10 | return [-e[0], e[1], e[2]] 11 | })).slice(0, 7) 12 | } 13 | 14 | var smooth = require('./midpoint').smooth 15 | 16 | function smoothing (stations, step) { 17 | step = step || 10 18 | var chines = stations[0].map(function (_chine, i) { 19 | return smooth(stations.map(function (station) { return station[i] }), step, 0.5) 20 | }) 21 | 22 | return chines[0].map(function (e, i) { 23 | return chines.map(function (chine, j) { return chine[i] }) 24 | }) 25 | } 26 | 27 | var inverse = [ 28 | [[0,-220, 2],[2, -220, 2]], 29 | [[0,-80, 2],[2, -80, 2]], 30 | [[0, -40, 2], [2, -40, 1]], 31 | [[0, -10, 2], [3.5, -10, 0.5]], 32 | [[0, 80, 0.6], [2, 80, 0.5]], 33 | ] 34 | .map(mirror).filter(Boolean) 35 | 36 | var stations = [ 37 | [[0, -60, 1], [1, -60, 1]], 38 | [[0, -40, 2], [2, -40, 1]], 39 | [[0, -10, 2], [3.5, -10, 0.5]], 40 | // [[0, 40, 1], [4.5, 40, 0.5]], 41 | //[[0, 60, 0.75], [4.5, 60, 0.5]], 42 | [[0, 70, 0.6], [4.5, 70, 0.5]], 43 | [[0, 80, 0.6], [1, 80, 0.5]], 44 | ] 45 | .map(mirror).filter(Boolean) 46 | 47 | module.exports = function (_) { 48 | 49 | function tab (y) { 50 | return _.CSG. 51 | cube({center:[0, 0, 0]}) 52 | .scale([4.5,2,0.5]) 53 | // .union(_.CSG.cube({center: [0, 0, 0]}).scale([1,2,2.25]).translate({x: 3.5, y:0, z:0})) 54 | // .union(_.CSG.cube({center: [0, 0, 0]}).scale([1,2,2.25]).translate({x: -3.5, y:0, z:0})) 55 | .translate({x:0, y:y,z:0}) 56 | } 57 | 58 | function tab2 (y) { 59 | return _.CSG. 60 | cube({center:[0, 0, 0]}) 61 | .scale([2.5,3,0.5]) 62 | // .union(_.CSG.cube({center: [0, 0, 0]}).scale([1,2,2.25]).translate({x: 3.5, y:0, z:0})) 63 | // .union(_.CSG.cube({center: [0, 0, 0]}).scale([1,2,2.25]).translate({x: -3.5, y:0, z:0})) 64 | .translate({x:0, y:y-1,z:0}) 65 | } 66 | 67 | 68 | function sides (y) { 69 | return _.CSG. 70 | _.CSG.cube({center: [0, 0, 0]}).scale([1,100,2.25]) 71 | .translate({x: 3.5, y:0, z:0}) 72 | .union( 73 | _.CSG.cube({center: [0, 0, 0]}).scale([1,100,2.25]) 74 | .translate({x: -3.5, y:0, z:0})) 75 | .translate({x:0, y:y-1,z:0}) 76 | } 77 | 78 | 79 | 80 | var Stations = require('./stations')(_) 81 | 82 | function poly (out) { 83 | return _.CSG.Polygon.createFromPoints(out) 84 | } 85 | // var s = stations.map(poly) 86 | 87 | function toBlade (stations) { 88 | var smoothed = smoothing(stations, 1).map(poly) 89 | 90 | var wedge = Stations(smoothed).scale({x:-1, y:1, z:1}) 91 | .union(Stations(smoothed)) 92 | 93 | return wedge.scale({x:1, y:0.625,z:1}).translate([0,8,0]) 94 | } 95 | 96 | var loom = 97 | _.CSG.cylinder({radius: 2, resolution: 24}) 98 | .scale({x:1, y: 55, z: 1}) 99 | .translate({x:0, y:-91, z: 0}) 100 | .union( 101 | _.CSG.cylinder({radius: 1.5, resolution: 24}) 102 | .scale({x:1, y: 20, z: 1}) 103 | .translate({x:0, y:-140, z: 0}) 104 | ) 105 | .translate([0,40,0]) 106 | 107 | loom = loom.intersect(toBlade(inverse)) 108 | 109 | var blade = toBlade(stations) 110 | 111 | var oar = blade.union(loom) 112 | .union(tab2(62)) 113 | .union(tab(-40)) 114 | .union(tab(-80)) 115 | .union(tab(-122)) 116 | .translate([0,30,0]) 117 | //.union(tab(-120)) 118 | 119 | //ground 120 | .union( 121 | _.CSG.cube({side: 0.5}).scale({x:9/2,y:180/2, z: 2.25/2}).translate([0,0,-1.125]) 122 | ) 123 | //side rails 124 | .union( 125 | _.CSG.cube({side: 1}).scale([0.5, 50, 2.25]) .translate([4,-40,0]) 126 | ) 127 | .union( 128 | _.CSG.cube({side: 1}).scale([0.5, 50, 2.25]) .translate([-4,-40,0]) 129 | ) 130 | 131 | // .union(sides(10)) 132 | 133 | require('fs').writeFileSync( 134 | 'oar.stla', 135 | require('@jscad/stl-serializer').serialize(oar, {binary: false})[0] 136 | ) 137 | 138 | var raw = Buffer.concat(require('@jscad/stl-serializer').serialize(oar).map(function (a) { return new Buffer(a) })) 139 | 140 | require('fs').writeFileSync('oar.stl', raw) 141 | 142 | return oar 143 | } 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /stations.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var stations = [ 4 | // [[0, 0], [0,1], [1, 2]], 5 | // [[0, 0], [0,2], [1, 3]], 6 | [[0, 0], [0.5, 1.5], [1.5, 1.5], [2, 0]], 7 | [[0, 0], [0, 2], [2, 2], [2, 0]], 8 | [[0, 0], [0.5, 1.5], [1.5, 1.5], [2, 0]] 9 | ] 10 | 11 | module.exports = function (_) { 12 | CSG = _.CSG 13 | 14 | function poly (points) { 15 | return CSG.Polygon.createFromPoints(points, null, CSG.Plane.fromPoints.apply(null, points)) 16 | } 17 | 18 | return function fromStations(stations) { 19 | // stations = stations.map(poly) 20 | STATIONS = stations 21 | console.log(stations) 22 | var bottom = stations[0] 23 | // return bottom.extrude(new CSG.Vector3D(0,0,1)) 24 | 25 | return bottom.solidFromSlices({ 26 | numslices: stations.length, 27 | callback: function (t, slice) { 28 | return stations[slice]//.translate([0,0,t*10]) 29 | } 30 | }) 31 | } 32 | 33 | return fromStations(stations) 34 | // return _.CSG.sphere({center: [0,0,0], radius: 10, resolution: 100}) 35 | } 36 | 37 | -------------------------------------------------------------------------------- /triangulate.js: -------------------------------------------------------------------------------- 1 | function toDegrees (rad) { 2 | return rad*180/Math.PI 3 | } 4 | 5 | function dist (a, b) { 6 | return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2)) 7 | } 8 | 9 | //given two 2d points and two distances, return a point. 10 | //this has some floating point errors, probably better ways 11 | //to do with without sin/cos/tan? 12 | function triangulate(a, da, b, db) { 13 | var c = dist(a, b) 14 | var cos = (da*da + c*c - db*db)/(2*da*c) 15 | var angle = Math.acos(cos) 16 | var cos2 = (db*db + c*c - da*da)/(2*db*c) 17 | var angle2 = Math.acos(cos2) 18 | var _angle = Math.atan( (a[1]-b[1]) / (a[0]-b[0]) ) 19 | var _angle2 = Math.atan( (b[1]-a[1]) / (b[0]-a[0]) ) 20 | 21 | // console.log('ANGLE', toDegrees(angle), toDegrees(_angle), 22 | // Math.sin(angle+_angle), 23 | // Math.cos(angle+_angle), 24 | // da 25 | // 26 | // ) 27 | console.log('angle', toDegrees(_angle), toDegrees(_angle2)) 28 | var x = [ 29 | a[0] + Math.sin(angle+_angle)*da, 30 | a[1] + Math.cos(angle+_angle)*da 31 | ] 32 | var x2 = [ 33 | b[0] + Math.sin(angle2+_angle2)*db, 34 | b[1] + Math.cos(angle2+_angle2)*db 35 | ] 36 | console.log(x, x2) 37 | 38 | 39 | console.log('dist a', da - dist(a, x)) 40 | console.log('dist b', db - dist(b, x)) 41 | if(angle2 < angle) return x2 42 | return x 43 | } 44 | 45 | //console.log(dist([0,0], [1,-1])) 46 | 47 | //console.log(triangulate([0,0], 1, [1,1], 1)) 48 | //console.log(triangulate([0,0], 5, [3,0], 4)) 49 | 50 | //should be [-1, 1], or [1, 1] 51 | console.log('T', triangulate([0,0], 1.414, [0,1], 1)) 52 | //should be [-1, 0] or [1, 0] 53 | console.log('T', triangulate([0,0], 1, [0,1], 1.414)) 54 | 55 | //return 56 | 57 | exports.triangulate = triangulate 58 | 59 | exports.develop = function (polygon, developed) { 60 | var a, b 61 | //given 62 | //developed is a map of vertices already layed out 63 | //first find a known edge: two vertices already developed. 64 | for(var k in polygon.vertices) { 65 | var v = polygon.vertices[k] 66 | if(!a && developed[vertexId(v)]) 67 | a = v 68 | else if(!b && developed[vertexId(v)]) 69 | b = v 70 | } 71 | if(!a && !b) throw new Error('could not develop polygon, do not have two matching vertices') 72 | 73 | //calculate distance between a and b 74 | // console.log(a, b) 75 | var d = a.pos.distanceTo(b.pos) 76 | var vertices = [] 77 | for(var k in polygon.vertices) { 78 | var v = polygon.vertices[k] 79 | if(developed[vertexId(v)]) //either a or b 80 | vertices.push(developed[vertexId(v)]) 81 | else { 82 | var _v = triangulate(developed[vertexId(a)], a.pos.distanceTo(v.pos), developed[vertexId(b)], b.pos.distanceTo(v.pos)) 83 | console.log("VERTICE", _v, a.pos.distanceTo(v.pos), b.pos.distanceTo(v.pos)) 84 | vertices.push(_v) 85 | if(!developed[vertexId(v)]) 86 | developed[vertexId(v)] = _v 87 | } 88 | } 89 | return vertices 90 | } 91 | 92 | var CSG = require('@jscad/csg').CSG 93 | 94 | var square = {vertices: [ 95 | new CSG.Vector3D(0, 0, 0), 96 | new CSG.Vector3D(0, 3, 0), 97 | new CSG.Vector3D(4, 3, 0), 98 | // new CSG.Vector3D(10, 0, 0) 99 | ].map(function (e, i) { 100 | e = new CSG.Vertex(e) 101 | e.tag = i+1 102 | return e 103 | })} 104 | 105 | //console.log(exports.develop(square, {1: [0,2], 2:[0, 5]})) 106 | 107 | var c = CSG.cube({center: [0,0,0], size: 2}) 108 | console.log(c) 109 | var d = {} //: [0,0], 2:[2, 0]} 110 | d[vertexId(c.polygons[0].vertices[0])] = [0,0] 111 | d[vertexId(c.polygons[0].vertices[1])] = [0,2] 112 | 113 | console.log(d) 114 | 115 | function vertexId(e) { 116 | e = e.pos || e 117 | return [e.x,e.y,e.z].join(',') 118 | } 119 | 120 | function sideId (poly) { 121 | return poly.vertices.map(vertexId).join(':') 122 | } 123 | 124 | function findUndevelopedPoly(shape, sides, developed) { 125 | for(var i = 0; i < shape.polygons.length; i++) { 126 | var poly = shape.polygons[i] 127 | if(!sides[sideId(poly)]) { 128 | var n = 0 129 | for(var j = 0; j < poly.vertices.length; j++) { 130 | if(developed[vertexId(poly.vertices[j])]) n++ 131 | } 132 | 133 | console.log(n) 134 | if(n >= 2) return poly 135 | } 136 | } 137 | // throw new Error('could not find a polygon to develop') 138 | } 139 | 140 | 141 | var sides = {} 142 | var side = findUndevelopedPoly(c, sides, d) 143 | while(side) { 144 | sides[sideId(side)] = exports.develop(side, d) 145 | // console.log('so far', sides, d) 146 | side = findUndevelopedPoly(c, sides, d) 147 | } 148 | 149 | console.log('SIDES', sides, d) 150 | 151 | //return 152 | module.exports = function (_) { 153 | //return c 154 | sd = Object.keys(sides).map(function (k) { 155 | var side = sides[k].map(function (v) { 156 | console.log(v) 157 | return v 158 | // return new _.CSG.Vector2D(v[0], v[1]) 159 | }) 160 | return _.CSG 161 | .Polygon.createFromPoints(side, null, CSG.Plane.fromPoints.apply(null, side.slice().reverse())) 162 | .extrude(new CSG.Vector3D(0,0,1)) 163 | })//.slice(0, 1) 164 | console.log('SIDES', sd) 165 | return sd[0] 166 | // .reduce(function (a, b) { 167 | // console.log('union', a) 168 | // return a.union(b) 169 | // }) 170 | } 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /vane-base.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = function (_) { 4 | 5 | var points = [ 6 | [0, -7.5], [0, 7.5], 7 | [50, 25], [50, -25] 8 | ] 9 | 10 | return ( 11 | CSG.Polygon.createFromPoints(points, null, CSG.Plane.fromPoints.apply(null, points)) 12 | .extrude(new CSG.Vector3D(0,0,10)) 13 | .union( 14 | _.CSG.cylinder({center: [0,0,0], size: 5}) 15 | .scale([10,10,10]) 16 | .rotate(90, [1,0,0]) 17 | ) 18 | ) 19 | 20 | // return _.Polygon.createFromPoints( 21 | return M = _.CSG.cylinder({center: [0,0,0], size: 20}).scale([20,20,20]) 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /winch.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = function (_) { 4 | 5 | function o(size) { 6 | return size - 0.2 7 | } 8 | function i(size) { 9 | return size + 0.2 10 | } 11 | 12 | function union (shapes) { 13 | return shapes.reduce(function (a, b) { 14 | return a.union(b) 15 | }) 16 | } 17 | 18 | function cone (r, h) { 19 | return _.CAG 20 | .fromPoints([[0,0,0], [0, h, 0], [r/2, 0, 0]]) 21 | .rotateExtrude({resolution: 90}) 22 | } 23 | 24 | function cyl (width, height) { 25 | return _.CSG.cylinder({ 26 | start: [0,0,0], 27 | end: [0,0,height], 28 | radius: width/2, 29 | resolution: 180 30 | }) 31 | } 32 | 33 | var W = 12.5 34 | var box = _.CSG.cube({ 35 | corner1: [-W, 50, 0], 36 | corner2: [W, -50, 100], 37 | }) 38 | 39 | var box2 = _.CSG.cube({ 40 | corner1: [-W, 0, 0], 41 | corner2: [0, -50, 100], 42 | }) 43 | var box3 = _.CSG.cube({ 44 | corner1: [0, 50, 0], 45 | corner2: [W, 0, 100], 46 | }) 47 | 48 | function side (flat_radius) { 49 | return 2 * (flat_radius / Math.sqrt(3)) 50 | 51 | } 52 | 53 | var hex = _.CSG.cylinder({ 54 | start: [0,0,50.2-15.3], 55 | end: [0,0, 55], 56 | resolution: 6, 57 | //i think radius is measured across the points 58 | radius: side(i(12.7))/2 59 | // radius: (14.4)/2 //across the points 60 | // radius: 12.7/2 //across the flats 61 | }) 62 | 63 | function cube (w, h) { 64 | return _.CSG.cube({ 65 | corner1: [-w/2,-w/2,0], 66 | corner2: [w/2,w/2,h], 67 | }) 68 | } 69 | 70 | var handle = union([ 71 | cube(i(17.8), 25), 72 | cube(i(17.8), 25).rotateZ(45), 73 | cone(i(17.8)*Math.sqrt(2), 6.4), 74 | cyl(i(17.8)*Math.sqrt(2), 5).translate([0,0,20]), 75 | cyl(i(12), 31), 76 | cyl(i(7), 50), 77 | ]).rotateZ(360/16) 78 | 79 | //just the hex 80 | // return cyl(o(25.3), 10).subtract(hex.translate([0,0,-40])) 81 | 82 | return M = union([ 83 | cyl(o(56.8), 3.1), 84 | cyl(o(53.9), 5.9), 85 | cyl(o(53.9), 16.3).intersect(box), 86 | 87 | union([ 88 | cyl(o(44.5), 30.1) 89 | .intersect(box2.union(box3)), 90 | cyl(o(31.4), 30.1), 91 | cyl(o(44.5), 17.9).intersect(box) 92 | ]) 93 | .subtract( 94 | union([ 95 | //0.2 bigger so fit isn't too tight. 96 | cyl(i(7.8+0.2), 130.7) 97 | .translate([0,(o(25.3)+o(7.8)+2)/2, 0]), 98 | cyl(i(7.8+0.2), 130.7) 99 | .translate([0,-(o(25.3)+o(7.8)+2)/2, 0]) 100 | ]).rotateZ(-5) 101 | ) 102 | , 103 | cyl(o(25.3), 50.2).subtract(hex) 104 | ]) 105 | .subtract(handle) 106 | //finally, to get around 3d quirk, scale whole thing 1.022 in Z axis 107 | .scale([1,1,1/1.022]) 108 | } 109 | 110 | --------------------------------------------------------------------------------