Last active
December 8, 2020 09:26
-
-
Save hrgdavor/d122fdba873a2ba796273ec7c0d4bc89 to your computer and use it in GitHub Desktop.
towel hanger
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
.\_dev_build\watch_dev.bat -l .\_useful\towel_hnager.jscad | |
*/ | |
var _parameters = [ | |
{name:'pipeDiameter',caption:'Pipe diameter', type:'int', initial:23}, | |
{name:'pipeDistance',caption:'Pipe distance', type:'int', initial:36}, | |
{name:'width',caption:'width', type:'int', initial:10}, | |
{name:'thick',caption:'Thickness', type:'float', initial:3.5}, | |
{name:'preview',caption:'Preview', type:'checkbox', checked:false}, | |
]; | |
var funcs = new Map(); | |
var outerRad = 202; | |
var outerRad = 202; | |
var wall = 1.5; | |
var bottom = 1; | |
funcs.base = function({pipeDiameter, pipeDistance, width=10, thick=3, preview=false}){ | |
var r = pipeDiameter/2 + thick/2 | |
var ret = [] | |
var cx = 0, cy = pipeDistance; | |
if(preview){ | |
ret.push(colors.colorize([1,0,0,0.7],bottomCylinder({radius:pipeDiameter/2, height:width, ty:pipeDistance}))) | |
ret.push(colors.colorize([1,0,0,0.7],bottomCylinder({radius:pipeDiameter/2, height:width}))) | |
} | |
var p = new P2DPoints(...rPointD(r,190,cx,cy)) | |
for(var a = 185; a>-41; a-=5){ | |
p.at(...rPointD(r,a,cx,cy)) | |
} | |
p.at(...rPointD(r-1,135)) | |
ret.push( p.build(width,{w:thick/2, closed:false, preview:0})) | |
var p = new P2DPoints(r, pipeDistance) | |
p.yTo(0) | |
// ret.push( p.build(width,{w:thick/2, closed:false, preview:0})) | |
var startX = r+thick/2 - thick; | |
p = new P2DPoints(startX,0).x(thick) | |
p.y(pipeDistance-25) | |
p.add(PointUtils.arc,{segments:8, y:2, mirror:false}) | |
p.m(8,13) | |
p.add(PointUtils.arc,{segments:8, y:0.5, mirror:true}) | |
p.m(7,7) | |
p.add(PointUtils.arc,{segments:4, y:1, mirror:true}) | |
p.m(1,8) | |
p.add(PointUtils.arc,{segments:4, y:0.5, mirror:true}) | |
p.m(-4,5) | |
p.x(-1) | |
p.add(PointUtils.arc,{segments:4, y:0.5, mirror:false}) | |
p.m(1.5,-8) | |
p.add(PointUtils.arc,{segments:8, y:6, mirror:false}) | |
p.at(startX+thick,pipeDistance-1).yTo(pipeDistance).x(-thick) | |
p.at(...rPointD(r,-30,cx,cy)) | |
p.at(...rPointD(r,-45,cx,cy)) | |
p.at(...rPointD(r,-60,cx,cy)) | |
p.at(...rPointD(r+thick/2+1.5,-60,cx,cy)) | |
p.add(PointUtils.arc,{segments:8, y:1.5, mirror:false}) | |
p.xToMy(startX,-2) | |
ret.push(p.build(width,{preview:0, reverse:false})) | |
// return p.build(width,{preview:0, reverse:false}) | |
// p = new P2DPoints(r+16,29) | |
// p.m(-1,3) | |
// ret.push(p.build(width,{w:thick/2, closed:false, preview:0})) | |
var r2 = 6+thick; | |
ret.push(bottomCylinderElliptic({radius:r2, wall:thick, height: width, sliceD:[180,0], tx:startX+r2})); | |
// ret.push(p.build(width,{w:thick/2, closed:false, preview:0})) | |
p = new P2DPoints() | |
p.add(PointUtils.arc,{segments:2, y:1, mirror:true}) | |
p.m(-thick,5).x(-1) | |
p.add(PointUtils.arc,{segments:2, y:0.5}) | |
p.at(-thick,0) | |
ret.push(p.build(width,{preview:0, reverse:false, tx:startX+r2*2})) | |
return preview ? ret : union(ret); | |
} | |
// bottomCube cornerCube bottomCylinder makeText size length P2DPoints | |
// translate rotate difference cylinderElliptic bottomCylinderElliptic startRadius | |
function getParameterDefinitions() { | |
let keys = Object.keys(funcs); | |
if(keys.length > 1){ | |
_parameters.push({ | |
name:'piece', | |
caption:'Piece', type:'choice', | |
keys | |
}); | |
} | |
return _parameters; | |
} | |
function main(params){ | |
let keys = Object.keys(funcs); | |
var piece = params.piece || keys[0]; | |
if(!funcs[piece]){ piece = keys[0]} | |
return funcs[piece](params || {}); | |
} | |
module.exports = {main, getParameterDefinitions} | |
const jscad = require('@jscad/modeling') | |
const { connectors, geometry, math, primitives, text, utils, booleans, expansions, extrusions, hulls, measurements, transforms, colors } = jscad | |
const { cuboid, sphere, cylinder, circle, star,cylinderElliptic } = jscad.primitives | |
const { translate, scale, center } = transforms | |
const { union, subtract, intersect } = booleans | |
const difference = subtract; | |
const { vectorText } = jscad.text | |
const rectangular_extrude = jscad.extrusions.extrudeRectangular; | |
const linear_extrude = jscad.extrusions.extrudeLinear; | |
const { mat4 } = jscad.maths | |
const { geom2, geom3, path2, poly2, poly3 } = jscad.geometries | |
const { extrudeFromSlices, slice, extrudeLinear } = jscad.extrusions | |
const extrudeChamfer = ({baseSlice, h=10, cut=0, cutTop=0, cutBottom=0, tz=0, tx=0, ty=0}) => { | |
cutTop = cutTop || cut; | |
cutBottom = cutBottom || cut; | |
let levels = []; | |
if(cutBottom){ | |
levels.push([0,-cutBottom]); | |
levels.push([cutBottom,0]); | |
}else{ | |
levels.push([0,0]); | |
} | |
levels.push([h-cutTop-cutBottom,0]); | |
if(cutTop){ | |
levels.push([cutTop, -cutTop]); | |
} | |
return extrudeLevels({levels, baseSlice, tz, tx, ty}) | |
} | |
const extrudeLevels = ({levels, baseSlice, corners='edge', segments=16, tz=0, tx=0, ty=0}) => { | |
var h = 0; | |
return extrudeFromSlices({numberOfSlices:levels.length, callback:(progress, counter)=>{ | |
var level = levels[counter]; | |
h += level[0]; | |
base = jscad.expansions.offset({delta:level[1], corners, segments},baseSlice); | |
// DIRTY FIX for what expand does to original geom | |
if(level[1] > 0){ | |
if(corners == 'round'){ | |
for(let i=0; i<segments-1; i++){ | |
let tmp = base.sides.shift(); | |
base.sides.push(tmp); | |
} | |
}else{ | |
let tmp = base.sides.pop(); | |
base.sides.unshift(tmp); | |
} | |
} | |
base = slice.fromSides(base.sides); | |
return slice.transform(mat4.fromTranslation([0, 0, h+tz]), base); | |
}}); | |
} | |
const extrudeScaleLevels = ({levels, baseSlice, corners='edge', segments=16, tz=0, tx=0, ty=0}) => { | |
var h = 0; | |
baseSlice = slice.fromSides(geom2.toSides(baseSlice)) | |
return extrudeFromSlices({numberOfSlices:levels.length, callback:(progress, counter)=>{ | |
var level = levels[counter]; | |
h += level[0]; | |
var scaleX = scaleY = level[1]; | |
if(level.length > 2) scaleY = level[2]; | |
const scaleMatrix = mat4.fromScaling([scaleX, scaleY, 1]) | |
const transformMatrix = mat4.fromTranslation([0, 0, h]) | |
return slice.transform(mat4.multiply(scaleMatrix, transformMatrix), baseSlice) | |
}}); | |
} | |
const roundTop = (base, {height=4, r1=0.2, r1Percent=0, rx=0.1, ry=0, segments=1}) => { | |
if(r1Percent) r1 = r1Percent * height; | |
ry = ry || rx; | |
// radius is for one side, and scale factor is for both | |
rx = rx*2; | |
ry = ry*2; | |
var numberOfSlices = 2 + segments; | |
var step = 1/(numberOfSlices-1); | |
var angleDelta = Math.PI / (segments) / 2 ; | |
var angleDelta2 = 180 / (segments) / 2 ; | |
var startH = height - r1; | |
var baseSlice = slice.fromSides(geom2.toSides(base)) | |
return extrudeFromSlices({ | |
numberOfSlices, | |
callback: (progress, counter, baseSlice) => { | |
var h = progress * height; | |
let scaleX = 1; | |
let scaleY = 1; | |
if(progress == 1){ // top slice (counter = numSlices-1) | |
scaleX = 1 - rx; | |
scaleY = 1 - ry; | |
}else if(progress == step){ // first top slice (counter = 1) | |
h = startH; | |
}else if(progress > step){ // curve (counter = 2,3,4...) | |
var angle = (counter -1) * angleDelta; | |
h = height - r1 + (Math.sin(angle) * r1); | |
scaleX = 1 - rx + (Math.cos(angle) * rx); | |
scaleY = 1 - rx + (Math.cos(angle) * ry); | |
}else{ | |
// bottom slice (counter = 0) | |
} | |
const scaleMatrix = mat4.fromScaling([scaleX, scaleY, 1]) | |
const transformMatrix = mat4.fromTranslation([0, 0, h]) | |
return slice.transform(mat4.multiply(scaleMatrix, transformMatrix), baseSlice) | |
} | |
}, baseSlice) | |
} | |
function cylinderFromTo(p1,p2, radius){ | |
const sqr = x=>x*x | |
let dx = p2[0] - p1[0] | |
let dy = p2[1] - p1[1] | |
let dz = p2[2] - p1[2] | |
let height = Math.sqrt( sqr(dx) + sqr(dy) + sqr(dz)) | |
let obj = cylinder({radius, height}) | |
if(dx || dy){ | |
let dxy = Math.sqrt( sqr(dx) + sqr(dy)) | |
let ay = Math.atan(dxy/dz) *(dx < 0 ? -1:1) | |
let az = Math.atan(dy/dx) | |
let ax = dz < 0 ? -Math.PI:0 | |
obj = transforms.rotate([ax,ay,az], obj) | |
} | |
let mid = [p1[0]+dx/2,p1[1]+dy/2,p1[2]+dz/2] | |
return translate(mid, obj) | |
} | |
function rPointD(r, angle, tx=0, ty=0){ | |
return rPoint(r, toRad(angle), tx, ty) | |
} | |
function rPoint(r, angle, tx=0, ty=0){ | |
return [tx + r * Math.cos(angle), ty + r * Math.sin(angle)]; | |
} | |
function elipsePoint(r1,r2, angle){ | |
return [r1 * Math.cos(angle), r2 * Math.sin(angle)]; | |
} | |
function elipsePointD(r1,r2, angle){ | |
return elipsePoint(r1,r2, toRad(angle)) | |
} | |
function elipseAngle(r1,r2, angle){ | |
if(r1 == r2) return angle; | |
var point = elipsePoint(r1,r2, angle); | |
var f1 = [0,0]; | |
var f2 = [0,0]; | |
var f; | |
if(r1 > r2){ | |
f = Math.sqrt(r1*r1 - r2*r2) | |
f1[0] -= f; | |
f2[0] += f; | |
}else{ | |
f = Math.sqrt(r2*r2 - r1*r1) | |
f1[1] -= f; | |
f2[1] += f; | |
} | |
return ( pointAngle(f1,point) + pointAngle(f2,point) ) / 2; | |
} | |
function elipseAngleD(r1,r2, angle){ | |
if(r1 == r2) return angle; | |
return toDeg( elipseAngle(r1,r2, toRad(angle)) ) | |
} | |
function elipseAngleMoveD(r1,r2,tz,angle,...pieces){ | |
return elipseAngleMove(r1,r2,tz,toRad(angle),...pieces) | |
} | |
function elipseAngleMove(r1,r2,tz,angle,...pieces){ | |
var p = elipsePoint(r1,r2,angle) | |
var angle = elipseAngle(r1,r2,angle) | |
return translate([...p,tz], transforms.rotate([0,0,angle],...pieces)) | |
} | |
function pointAngle(p1, p2){ | |
return Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) | |
} | |
function pointAngleD(p1, p2){ | |
return toDeg(pointAngle(p1, p2)) | |
} | |
function toRad(d){return d/180*Math.PI;} | |
function toDeg(r){return r*180/Math.PI;} | |
const _rMagnet = 2.1; | |
const _hMagnet = 2.9; | |
function rotateD(angles, ...parts){ | |
return transforms.rotate(angles.map(toRad),...parts); | |
} | |
const _m5_screw = 2.6; | |
const _m5_nut = 4.6; | |
const _m5_nut_h = 3.9; | |
function reduceWidth(r, reduce){ | |
var ret = [...r] | |
ret[0] = r[0] - reduce | |
ret[1] = r[1] - reduce | |
return ret; | |
} | |
function bottomCube(options){ | |
options.center = dirAlign(options.align || 'T', options.size, [0,0,options.bottom || 0]) | |
var piece = cuboid(options) | |
if(options.wall){ | |
var {topWall = 0, bottomWall = 0} = options | |
options.size = reduceWidth(options.size, options.wall*2)// need to reduce double, one for each side | |
options.size[2] -= topWall + bottomWall | |
options.center[2] += bottomWall - (bottomWall + topWall) / 2 | |
piece = subtract(piece, cuboid(options)) | |
} | |
if(options.holes) piece = subtract(piece, translate(options.center,...options.holes)) | |
return dirRotateMove(options, piece)[0]; | |
} | |
function bottomCylinder(options){ | |
var r2 = options.radius*2 | |
options.center = dirAlign(options.align || 'T', [r2,r2,options.height], [0,0,options.bottom || 0]) | |
var piece = cylinder(options) | |
if(options.wall){ | |
var {topWall = 0, bottomWall = 0} = options | |
options.height -= topWall + bottomWall | |
options.radius -= options.wall | |
options.center[2] += bottomWall - (bottomWall + topWall) / 2 | |
piece = subtract(piece, cylinder(options)) | |
} | |
if(options.holes) piece = subtract(piece, ...options.holes) | |
return dirRotateMove(options, piece)[0]; | |
} | |
function bottomCylinderElliptic(options){ | |
var {radius} = options | |
const toArray = val=>val instanceof Array ? val:[val,val] | |
if(radius) options.startRadius = [radius,radius] | |
options.startRadius = toArray(options.startRadius) | |
var {startRadius} = options | |
if(!options.endRadius) options.endRadius = startRadius; | |
options.endRadius = toArray(options.endRadius) | |
options.center = dirAlign(options.align || 'T', [startRadius[0]*2,startRadius[1]*2,options.height], [0,0,options.bottom || 0]) | |
// options.center = [0,0,options.height/2+(options.bottom || 0)]; | |
if(options.slice){ | |
options.startAngle = options.slice[0] | |
options.endAngle = options.slice[1] | |
} | |
if(options.sliceD){ | |
options.startAngle = toRad(options.sliceD[0]) | |
options.endAngle = toRad(options.sliceD[1]) | |
} | |
var piece = cylinderElliptic(options) | |
if(options.wall){ | |
var {topWall = 0, bottomWall = 0} = options | |
options.height -= topWall + bottomWall | |
options.startRadius = reduceWidth(startRadius, options.wall) | |
options.endRadius = reduceWidth(options.endRadius, options.wall) | |
options.center[2] += bottomWall - (bottomWall + topWall) / 2 | |
piece = subtract(piece, cylinderElliptic(options)) | |
} | |
if(options.holes) piece = subtract(piece, ...options.holes) | |
return dirRotateMove(options, piece)[0]; | |
} | |
function dirAlign(align='T',size=[1,1,1],center=[0,0,0]){ | |
for(let i=0; i<align.length; i++){ | |
var dir = align[i]; | |
if(dir == 'T') center[2] += size[2]/2 | |
if(dir == 'B') center[2] -= size[2]/2 | |
if(dir == 'S') center[1] -= size[1]/2 | |
if(dir == 'N') center[1] += size[1]/2 | |
if(dir == 'E') center[0] += size[0]/2 | |
if(dir == 'W') center[0] -= size[0]/2 | |
} | |
return center; | |
} | |
function dirRotateMove(options,...elems){ | |
var dirs = options.dir||''; | |
var rot = [0,0,0] | |
for(let i=0; i<dirs.length; i++){ | |
var dir = dirs[i]; | |
if(dir == 'B') rot = [180,0,0] | |
if(dir == 'S') rot = [90,0,0] | |
if(dir == 'N') rot = [-90,0,0] | |
if(dir == 'E') rot = [0,90,0] | |
if(dir == 'W') rot = [0,-90,0] | |
if(dir == 'Y') rot[1] += 90 | |
if(dir == 'y') rot[1] -= 90 | |
if(dir == 'X') rot[0] += 90 | |
if(dir == 'x') rot[0] -= 90 | |
if(dir == 'Z') rot[2] += 90 | |
if(dir == 'z') rot[2] -= 90 | |
} | |
elems = [rotateD(rot ,...elems)] | |
if(options.tx || options.ty || options.tz) | |
elems = [translate([options.tx || 0, options.ty || 0, options.tz || 0 ], ...elems)]; | |
return elems; | |
} | |
function magnetHole({r=_rMagnet, h=_hMagnet, tz=0, hTol=0}={}){ | |
return bottomCylinder({radius:r, height:h+hTol, tz}) | |
} | |
function csgFromSegments (segments, {w=2, h=1}={}) { | |
return segments.map(segment => rectangular_extrude(segment, { w, h }) ); | |
} | |
function makeText({text='A', height=10, w=2, h=1}={}) { | |
if (text === undefined || text.length === 0) return [] | |
const lineSegments3D = [] | |
const lineSegmentPointArrays = vectorText({ x: 0, y: 0, height, input: text }) // line segments for each character | |
lineSegmentPointArrays.forEach((segmentPoints) => { // process the line segment | |
const segmentShape = extrudeLinear( | |
{ height: h }, | |
jscad.expansions.expand({ delta: w, corners: 'round', segments: 16 }, jscad.primitives.line(segmentPoints)) | |
) | |
lineSegments3D.push(segmentShape) | |
}) | |
const messageObject = center([true, true, false], union(lineSegments3D)) | |
return messageObject; | |
} | |
function m5_nut({height=_m5_nut_h, radius=_m5_nut, segments=6, rTol=0, hTol=0, bottom=0, dir='T', align='T', tx=0, ty=0, tz=0}={}){ | |
var options = {height, radius, segments, rTol, hTol, bottom, align, dir, tx, ty, tz} | |
options.radius = options.radius + options.rTol; | |
options.height = options.height + options.hTol; | |
return bottomCylinder(options); | |
} | |
function m5_screw({height=10, radius=_m5_screw, segments=32, rTol=0, hTol=0, bottom=0, dir='T', align='T', tx=0, ty=0, tz=0}={}){ | |
var options = {height, radius, segments, rTol, hTol, bottom, dir, tx, ty, tz} | |
options.radius = options.radius + options.rTol; | |
options.height = options.height + options.hTol; | |
return bottomCylinder(options); | |
} | |
function m5_both({height=10, bottom=0, rTol1=0, rTol2=0, hTol=0, dir='T', align='T', tx=0, ty=0, tz=0}={}){ | |
var r2 = _m5_nut*2 | |
var size = [r2,r2,height] // size together | |
var nutHalf = _m5_nut_h/2; | |
var center = dirAlign(align,size, [0,0, bottom - height/2 + nutHalf]) | |
var nut = cylinder({segments:6, radius:_m5_nut+rTol1, height:_m5_nut_h, center}) | |
var center = dirAlign(align,size, [0,0, bottom +nutHalf]) | |
var screw = cylinder({height:height- _m5_nut_h, radius:_m5_screw+rTol2, center}) | |
return dirRotateMove({dir,tx,ty,tz},[nut, screw]) | |
} | |
function P2DPoints(x=0, y=0){ | |
this.pos = {x,y}; | |
this.points = [[x,y]]; | |
} | |
var proto = P2DPoints.prototype; | |
proto.add = function(shape,options={}){ | |
this.points.push({shape,options}); | |
return this; | |
} | |
proto.at = function(x=0,y=0){ | |
this.points.push([this.pos.x = x, this.pos.y = y]); | |
return this; | |
} | |
proto.m = function(x=0, y=0){ | |
return this.at(this.pos.x + x, this.pos.y + y); | |
} | |
proto.x = function(x=0){ | |
return this.m(x, 0); | |
} | |
proto.y = function(y){ | |
return this.m(0, y); | |
} | |
proto.xTo = function(x=0){ | |
return this.at(x, this.pos.y); | |
} | |
proto.xToMy = function(x=0, y=0){ | |
return this.at(x, this.pos.y+y); | |
} | |
proto.yTo = function(y=0){ | |
return this.at(this.pos.x, y); | |
} | |
proto.mXyTo = function(x=0,y=0){ | |
return this.at(this.pos.x+x, y); | |
} | |
proto.aRad = function(rad, r){ | |
return this.m(r * Math.cos(rad), r * Math.sin(rad)); | |
} | |
proto.aDeg = function(deg, r){ | |
return this.aRad(deg/180 * Math.PI, r); | |
} | |
proto.xRad = function(rad, x){ | |
return this.m(x, x * Math.sin(rad) / Math.cos(rad)); | |
} | |
proto.xDeg = function(deg, x){ | |
return this.xRad(deg/180 * Math.PI, x); | |
} | |
proto.yRad = function(rad, y){ | |
return this.m(y * Math.cos(rad) / Math.sin(rad), y); | |
} | |
proto.yDeg = function(deg, y){ | |
return this.xRad(deg/180 * Math.PI, y); | |
} | |
proto.calcPoints = function(){ | |
this.points = this.buildPoints(); | |
} | |
proto.mirrorX = function({a=1,b=0, m0=-1, m1=1}={}){ | |
return this.mirrorY({a,b,m0,m1}); | |
} | |
proto.mirrorY = function({a=0, b=1, m0=1, m1=-1}={}){ | |
var points = this.points; | |
// remove start vertical | |
var p0 = points[0]; | |
var p1 = points[1]; | |
if(!p1.shape && p0[a] == p1[a]) points.shift(); | |
// remove end vertical | |
var pEnd0 = points[points.length -1]; | |
var pEnd1 = points[points.length -2]; | |
if(!pEnd1.shape && pEnd0[a] == pEnd1[a]) points.pop(); | |
var count = points.length; | |
for(var i=count-1; i>=0;i--){ | |
var p = points[i]; | |
if(p.shape){ | |
points.push(p); | |
}else{ | |
points.push([p[0]*m0, p[1]*m1]); | |
} | |
} | |
} | |
proto.buildPoints = function(){ | |
var sqr = x=>x*x; | |
var ret = []; | |
var points = this.points; | |
const myFix2 = n=>parseFloat(n.toFixed(2)) | |
const isEqPoint = (p1,p2)=> myFix2(p1[0]) == myFix2(p2[0]) && myFix2(p1[1]) == myFix2(p2[1]); | |
for(var i=0; i<points.length; i++){ | |
var point = points[i]; | |
if(point.shape){ | |
var prev = points[i-1]; | |
var next = points[i+1]; | |
var {options, shape} = point; | |
var tx=0, ty=0, distance=0, rad=0; | |
var deltaX = next[0] - prev[0]; | |
var deltaY = next[1] - prev[1]; | |
distance = Math.sqrt(sqr(next[0] - prev[0]) + sqr(next[1] - prev[1])); | |
if(typeof shape == 'function') shape = shape(distance, options); | |
var toAdd = shape.buildPoints ? shape.buildPoints():shape; | |
var rad = Math.atan2(deltaY, deltaX); | |
if(options.mirror) PointUtils.scalePoints(toAdd,1,-1); | |
if(options.start != undefined) tx = options.start; | |
if(options.center || options.end != undefined){ | |
var len = 0; | |
toAdd.forEach(p=>{if(p[0] > len) len = p[0]}); | |
if(options.center){ | |
tx = distance/2 - len/2 + (options.start || 0); | |
} | |
if(options.end != undefined){ | |
tx = distance - len - options.end; | |
} | |
} | |
if(rad && tx) { | |
// use tx to calc ty before it is changed | |
ty = tx * Math.sin(rad) | |
tx = tx * Math.cos(rad) | |
} | |
if(rad) PointUtils.rotatePoints(toAdd, rad) | |
if(tx) PointUtils.translatePoints(toAdd,tx,ty); | |
var pStart = points[0]; | |
//PointUtils.translatePoints(toAdd, prev[0]-pStart[0], prev[1]-pStart[1]) | |
PointUtils.translatePoints(toAdd, prev[0], prev[1]) | |
while(isEqPoint(prev,toAdd[0])) toAdd.shift(); | |
while(isEqPoint(next,toAdd[toAdd.length-1])) toAdd.pop(); | |
ret.push(...toAdd); | |
}else{ | |
ret.push([...point]) | |
} | |
} | |
while(isEqPoint(ret[0], ret[ret.length-1])) ret.pop(); | |
return ret; | |
} | |
function myFix2(n){ return parseFloat(n.toFixed(2))} | |
proto.build = function(h=0, {preview=0, closed=true, tx=0, ty=0, sx=1,sy=1, w=0, reverse=true}={}){ | |
var points = this.buildPoints() | |
if(reverse) points=points.reverse(); | |
if(sx != 1 || sy != 1) PointUtils.scalePoints(points, sx,sy); | |
if(tx != 0 || ty != 0) PointUtils.translatePoints(points, tx,ty); | |
if(!preview) try{ | |
var shape = path2.fromPoints({closed}, points) | |
if(w) return [extrusions.extrudeRectangular({size:w , height:h, closed}, closed ? shape : path2.fromPoints({closed:false},points))] | |
shape = geom2.fromPoints(points) | |
if(h) return [extrusions.extrudeLinear({height: h}, shape)] | |
return [shape]; | |
}catch(e){ | |
console.log(e.message,e); | |
} | |
preview = preview || 0.1; | |
var tmp = this.points.filter(p=>!p.shape); | |
var ret = [extrusions.extrudeRectangular({size:preview , height:preview, closed}, path2.fromPoints({closed},points))]; | |
if(tmp.length != this.points.length){ | |
ret.push(colors.colorize([1,1,0],translate([0,0,-preview], extrusions.extrudeRectangular({size:preview , height:preview}, path2.fromPoints({closed},tmp))))) | |
} | |
for(var i=0; i<points.length; i++){ | |
var p = points[i]; | |
ret.push( | |
colors.colorize([0,0,1], | |
translate([p[0],p[1],preview], | |
cylinder({radius:preview/2, height:preview}) | |
) | |
) | |
); | |
} | |
return ret; | |
} | |
var PointUtils = {}; | |
PointUtils.arc = function(len, {y=10, segments=4}){ | |
var p = []; | |
var x = len/2; | |
var r = y + ((x*x - y*y)/2/y * (y > x ? 1:1)); | |
var rad = Math.atan2(r-y, x)* (y > x ? 1:1); | |
console.log(rad); | |
var step = (Math.PI/2 - rad ) / segments; | |
p.push([x,y]); | |
for(var i=segments-1; i>=0; i--){ | |
var y2 = r * Math.sin(rad+step*i); | |
var x2 = r * Math.cos(rad+step*i); | |
p.push([x+x2,y2-r+y]); | |
p.unshift([x-x2,y2-r+y]); | |
} | |
return p; | |
} | |
PointUtils.curve = function(len, {i=2, px=0.3, py=0.2, y=0}){ | |
var sqr = x=>x*x | |
if(y) py = y/len; | |
var p = []; | |
var segments = 4; | |
var top = len*py; | |
var dx = len*px; | |
for(var i=0; i<segments; i++){ | |
p.push([dx/segments*i, top-sqr(1/segments*(segments-i))*top]) | |
} | |
p.push([len*px,top]); | |
var dx2 = len-dx; | |
for(var i=1; i<segments; i++){ | |
p.push([dx+dx2/segments*i, top-sqr(1/segments*i)*top]) | |
} | |
return p; | |
} | |
PointUtils.bump = function({d=2,w=2}={}){ | |
return new P2DPoints(0,0).y(d).x(w).y(-d); | |
} | |
PointUtils.flipPoints = function(points){ | |
points.forEach(p=>{ | |
let tmp = p[0]; | |
p[0] = p[1]; | |
p[1] = tmp; | |
}); | |
} | |
PointUtils.scalePoints = function(points, sx, sy){ | |
if(sx == 1 && sy == 1) return; | |
points.forEach(p=>{ | |
p[0] = p[0] * sx; | |
p[1] = p[1] * sy; | |
}); | |
} | |
PointUtils.rotatePoints = function(points, rad){ | |
if(rad == 0) return; | |
points.forEach(p=>{ | |
var x = p[0], y=p[1]; | |
p[0] = x * Math.cos(rad) - y * Math.sin(rad); | |
p[1] = y * Math.cos(rad) + x * Math.sin(rad); | |
}); | |
} | |
PointUtils.translatePoints = function(points, tx, ty){ | |
points.forEach(p=>{ | |
p[0] = p[0] + tx; | |
p[1] = p[1] + ty; | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment