openjscad-aurora-webapp
324 строки · 9.7 Кб
1// title: Celtic Knot Ring
2// author: Joost Nieuwenhuijse
3// license: MIT License
4// tags: Catmull Spline
5
6// -*- mode: javascript; -*-
7
8
9'use strict';10
11function getParameterDefinitions() {12return [13{14name: 'hisorhers',15type: 'choice',16caption: 'For Daniel or Zette:',17values: [0, 1],18captions: ["Dan", "Suzette"],19initial: 020}21];22}
23
24
25var his = true;26
27
28var debugcount = 10;29function debugprint () {30if (debugcount-- > 0) {31try {32console.log(arguments);33} catch (err) {34//35}36}37}
38
39
40// interpolate between v2 and v3, at time u
41function catmullRom(v1, v2, v3, v4, u) {42var c1x,c2x,c3x,c4x, resX;43var c1y,c2y,c3y,c4y, resY;44var c1z,c2z,c3z,c4z, resZ;45
46// Coefficients for Matrix M47// these should all be const, but MSIE doens't handle that48var M11 = 0.0;49var M12 = 1.0;50var M13 = 0.0;51var M14 = 0.0;52var M21 =-0.5;53var M22 = 0.0;54var M23 = 0.5;55var M24 = 0.0;56var M31 = 1.0;57var M32 =-2.5;58var M33 = 2.0;59var M34 =-0.5;60var M41 =-0.5;61var M42 = 1.5;62var M43 =-1.5;63var M44 = 0.5;64
65c1x = M12*v2.x;66c2x = M21*v1.x + M23*v3.x;67c3x = M31*v1.x + M32*v2.x + M33*v3.x + M34*v4.x;68c4x = M41*v1.x + M42*v2.x + M43*v3.x + M44*v4.x;69
70c1y = M12*v2.y;71c2y = M21*v1.y + M23*v3.y;72c3y = M31*v1.y + M32*v2.y + M33*v3.y + M34*v4.y;73c4y = M41*v1.y + M42*v2.y + M43*v3.y + M44*v4.y;74
75c1z = M12*v2.z;76c2z = M21*v1.z + M23*v3.z;77c3z = M31*v1.z + M32*v2.z + M33*v3.z + M34*v4.z;78c4z = M41*v1.z + M42*v2.z + M43*v3.z + M44*v4.z;79
80resX = (((c4x*u + c3x)*u +c2x)*u + c1x);81resY = (((c4y*u + c3y)*u +c2y)*u + c1y);82resZ = (((c4z*u + c3z)*u +c2z)*u + c1z);83
84return new CSG.Vector3D(resX, resY, resZ);85}
86
87var tiny = 0.0000001;88
89function catmullRomWithTangent(v1, v2, v3, v4, u) {90var res1, res2, tangent;91if ((u+tiny) <= 1) {92res1 = catmullRom(v1, v2, v3, v4, u) ;93res2 = catmullRom(v1, v2, v3, v4, u+tiny) ;94tangent = res2.minus(res1).unit();95return [res1, tangent];96} else {97res1 = catmullRom(v1, v2, v3, v4, u-tiny) ;98res2 = catmullRom(v1, v2, v3, v4, u) ;99tangent = res2.minus(res1).unit();100return [res2, tangent];101}102}
103
104
105
106// create a CSG by dragging a CAG along a Catmull-Rom spline
107// where the 'top' of the CAG is 'up' and 'sideways' of
108// the CAG are perpendicular to 'up' and the spline tangent
109
110
111function splineExtrude(vCP, numInterps, up,112cag, transform) {113var polygons = [];114var splinePointsAndTangents = [];115// corners is an array of arrays116
117// corners [j] corresponds to the array of all points on the118// spline with the offset of cag.sides[j].vertex0119
120// corners[j][i] is the i'th interpolated point on the master121// spline, plus the offset of cag.sides[j].vertex0122
123var corners = [];124var nSides = cag.sides.length;125
126for (i=0; i< nSides; i++) {127corners.push([]);128}129
130if (typeof(transform) != 'function') {131transform = function(e) { return e; };132}133
134// fencepost - do the zeroth point of the zeroth segment135splinePointsAndTangents.push(catmullRomWithTangent(vCP[0],vCP[0+1],136vCP[0+2],vCP[0+3],0));137for (var j = 0; j <= vCP.length-4; j++) {138// don't do the zeroth point, because it's the same as the139// last point of the previous segment140for (var i = 1; i <= numInterps; i++) {141var u = i/numInterps;142splinePointsAndTangents.push(catmullRomWithTangent(vCP[j],vCP[j+1],143vCP[j+2],vCP[j+3],u));144}145}146
147
148for (var m=0; m < splinePointsAndTangents.length; m++) {149var sideways = up.cross(splinePointsAndTangents[m][1]);150for (var n = 0; n < nSides; n++) {151corners[n].push(transform(splinePointsAndTangents[m][0]152.plus(sideways.times(cag.sides[n].vertex0.pos.x))153.plus(up.times(cag.sides[n].vertex0.pos.y))));154
155// vertex1 should be the same as vertex0 of the next side,156// so I don't need to handle it here157}158}159var shared = CSG.Polygon.defaultShared;160
161var start = 0;162var end = corners[0].length-1;163var nCorners = corners[0].length;164
165
166
167//start cap168var startCap = [];169for (var p =nSides-1; p>=0; p--) {170startCap.push(corners[p][start]);171}172polygons.push(CSG.Polygon.createFromPoints(startCap, shared));173
174// polygons.push(CSG.Polygon.createFromPoints([corners4[start],corners3[start],
175// corners2[start],corners1[start]],
176// shared));
177for (var q = start; q < end; q++) {178
179// This is done as triangles, (rather than rectangles) because180// at points on the spline with high curvature, the inside181// corners can become twisted, which messes up the182// renderer. What I don't know is what happens when such a183// file is converted to STL and sent to a 3D printer.184
185// In the words of Shapeways, it makes the printer cry.186
187for (var r = 0; r < nSides; r++) {188
189polygons.push(CSG.Polygon.createFromPoints([corners[r][q], corners[(r+1)%nSides][q],190corners[r][q+1]],191shared));192polygons.push(CSG.Polygon.createFromPoints([ corners[(r+1)%nSides][q],193corners[(r+1)%nSides][q+1],corners[r][q+1]],194shared));195}196}197var endCap = [];198for (var s =0; s< nSides; s++) {199endCap.push(corners[s][end]);200}201polygons.push(CSG.Polygon.createFromPoints(endCap, shared));202
203return CSG.fromPolygons(polygons);204}
205
206
207var controlPoints =208[209[0, 0, 1, 1], //over across the middle210[10, 10, -1, 0], //under the first cross211[20, 20, 1, 1], //over the second cross212// [30, 24, 0, 0], //curving into the corner
213[39, 27.25, 0, 1], // the sharp corner214// [32, 12, 0, 0],
215[30, 10, -1, 0],216// [28, 8, 0, 0],
217[20, 3.75, 0, 0], // bottom of loop under the corner218[10, 10, 1, 1],219[4, 20, 0, 0], // grand curve near the sharp corner (under the long arc)220//// [6, 26, 0, 0],
221// [8, 28, 0, 0],
222[10, 30, -1, 0],223[24, 34, 0, 0],224// [30, 35, 0, 0], // top of the long arc
225[40, 34, 0, 0],226[50, 30, 1, 1], // about where the long arc crosses over227// [58, 22, 0, 0],
228[60, 20, -1, 0],229[70, 10, 1, 1],230[75, 5, 0, 0],231[80, 0, -1, 0]232// [79.9, .1, -1, 0] // under the middle (2 cycles right)
233// [79.95, .05, -1, 0] // under the middle (2 cycles right)
234];235
236var numberOfPatterns = 11;237var circumference = 40 * numberOfPatterns;238var radius = circumference / 2 / Math.PI;239var targetCircumference = his?54.3:56.3;240
241
242function main (params) {243his = (params.hisorhers != 1);244targetCircumference = his?54.3:56.3;245
246var up = new CSG.Vector3D(0,0,1);247var flipCP = controlPoints.slice();248flipCP.reverse();249flipCP = flipCP.map(function(elt) { return ([elt[0]*-1, elt[1]*-1,250elt[2], elt[3]]); });251// delete the repeated 0,0 point;252controlPoints.shift();253controlPoints = flipCP.concat(controlPoints);254if (!his) {255controlPoints = controlPoints.map(function(elt) {256return ([elt[0], elt[1]*-1,257elt[2]*-1, elt[3]]); });258}259var splines = [];260var lastPoint ;261var tripleCP = [];262// one extra cycle before and after263for (var i=-1; i<numberOfPatterns+1; i++) {264// for (var i=-1; i<4+1; i++) {
265tripleCP = tripleCP.concat(controlPoints.map(266function(elt) { return ([elt[0]+(i*160), elt[1],267elt[2], elt[3]]); }));268// delete final point so it's not duplicated269lastPoint = tripleCP.pop();270}271// put final point back after the last spline272tripleCP.push(lastPoint);273
274// delete all but last two points of the extra cycle275// ie. the start/end point and the extra control point276for (var t=0; t < controlPoints.length-2; t++) {277tripleCP.pop();278tripleCP.shift();279}280
281debugprint(tripleCP);282var vCP = tripleCP.map(function(e) {283return new CSG.Vector3D(e[0],e[1],e[2]);284});285
286var shape1 = CAG.fromPoints([[-4,0],287[-4,5.0], [-1.5,8.5],288[1.55,8.5], [4, 5.0],289[4,0]]);290
291// var shape1 = CAG.fromPoints([[-2.75,0], [0,2.75], [2.75,0]]);292splines.push(splineExtrude(vCP, 11, up, shape1, transformVec3DtoRingSpace));293// splines.push(splineExtrude(vCP, 11, up, shape1));
294var csg = new CSG();295for (var u=0; u < splines.length; u++) {296csg = csg.union(splines[u]);297}298
299csg = csg.transform(CSG.Matrix4x4.rotationX(90));300csg = csg.scale(targetCircumference/circumference);301return csg;302
303// 7.5 ring size is 17.7 mm diameter 55.7mm circumference
304// my guess as to my own ring size is 54mm
305
306// augh! the pass-under bits are 1 pre-scaled unit narrower
307// (after scaling, about 1/8 mm, so .4 mm in extra circumference.
308}
309
310function transformVec3DtoRingSpace (vec) {311var m = new CSG.Matrix4x4();312
313m = m.multiply(CSG.Matrix4x4.translation([-vec.x, 0, radius]));314m = m.multiply(CSG.Matrix4x4.rotationY(360*(vec.x)/circumference));315var res = vec.transform(m);316return res;317}
318
319// Question: polygons are supposed to be coplanar vertices, but after
320// being transformed into ring space, are 4 coplanar vertices still
321// always coplanar? I'm pretty sure the answer is 'No'. Does this matter?
322// I think yes. So I can generate triangles instead, easily enough.
323// Excpet the end caps... which for my model are not actually rotated,
324// because they are at the origin or exactly 11 loops away.
325
326
327
328