├── README.md ├── demo ├── CoolGear.png ├── demo.html └── wait.gif ├── jqCube.html ├── jqCube.jquery.js └── jqCube.jquery.min.js /README.md: -------------------------------------------------------------------------------- 1 | jqCube 2 | ====== 3 | 4 | **jqCube** is a [jQuery](http://jquery.com/) library for adding cubes to pages, such as the one pictured below. 5 | A full API is included for manipulating these cubes, allowing you to change its colors, dimensions, and animate it. You can put any content you like on each side of the cube. 6 | 7 | You can [play around with the API-accessible features on this playground page](http://peterolson.github.com/jqCube/jqCube.html) 8 | 9 | ![Screenshot of web form on green rotating cube](http://i.stack.imgur.com/zMebo.png) 10 | 11 | Background 12 | === 13 | 14 | You can read some [extended discussion about the user interface implications of rotating cubes](http://ux.stackexchange.com/q/11229/3966). Having seen it's popularity there among user interface professionals, I have decided to release it as an open source project so that anybody can include cubes in their own websites. 15 | 16 | How to Use 17 | === 18 | 19 | You can download the source files by cloning this repository, or just copying it from this GitHub hosted url: 20 | 21 | http://peterolson.github.com/jqCube/jqCube.jquery.min.js 22 | 23 | Include it as a script on your page. It depends on jQuery, so make sure you include it *after* jQuery. 24 | 25 | Create a div on your page, and include six divs inside of it, and each should have one of the classes "front", "back", "left", "right", "bottom", "top", like this: 26 | 27 |
28 |
Front Side
29 |
Back Side
30 |
Left Side
31 |
Right Side
32 |
Bottom Side
33 |
Top Side
34 |
35 | 36 | Then you can make `myCube` into a cube. For default settings, just call `.cube` on a jQuery element with no arguments. 37 | 38 | $("#myCube").cube(); 39 | 40 | If you want to initialize the cube with custom settings, you can pass a settings object as an argument. 41 | 42 | $("#myCube").cube({ /* settings */ }); 43 | 44 | Settings 45 | === 46 | 47 | `length`, `width`, `height` 48 | --- 49 | Sets the dimensions of the cube in pixels with these three settings. 50 | 51 | **Expected Value**: Integer
52 | **Default:** `400` 53 | 54 | `center` 55 | --- 56 | Sets the center point of the cube. Defined by pixel offsets to the left and top of the page. 57 | 58 | **Expected Value**: [Integer, Integer]
59 | **Default**: `[300, 300]` 60 | 61 | 62 | `lightDirection` 63 | --- 64 | Sets the direction light comes from. Defined by a proportion of [lightFromRight, lightFromTop, lightFromFront] facing toward the cube. You can use negative values to reverse the direction. 65 | 66 | **Expected Value**: [Number, Number, Number]
67 | **Default**: `[1, 1, 1]` 68 | 69 | `hue` 70 | --- 71 | Sets the hue of the cube. 72 | 73 | **Expected Value**: Integer in range [0, 255]
74 | **Default**: `128` 75 | 76 | `saturation` 77 | --- 78 | Sets the saturation of the cube. 79 | 80 | **Expected Value**: Integer in range [0, 100]
81 | **Default**: `50` 82 | 83 | `opacity` 84 | --- 85 | Sets the opacity/transparency of the cube. 0 is transparent, and 100 is opaque. 86 | 87 | **Expected Value**: Integer in range [0, 100]
88 | **Default**: `100` 89 | 90 | `minLuminosity` 91 | --- 92 | Sets the luminosity of the darkest face of the cube. 93 | 94 | **Expected Value**: Integer in range [0, 100]
95 | **Default**: `50` 96 | 97 | `maxLuminosity` 98 | --- 99 | Sets the luminosity of the brightest face of the cube. 100 | 101 | **Expected Value**: Integer in range [0, 100]
102 | **Default**: `85` 103 | 104 | `animate` 105 | --- 106 | Boolean indicating whether the cube will animate. 107 | 108 | **Expected Value**: Boolean
109 | **Default**: `true` 110 | 111 | `frameRate` 112 | --- 113 | Sets the number of frames drawn per second when the cube animates. 114 | 115 | **Expected Value**: Integer
116 | **Default**: `20` 117 | 118 | `secondsPerRotation` 119 | --- 120 | Sets the approximate time in seconds it will take the cube to rotate fully about one axis at default spinning speed. 121 | 122 | **Expected Value**: Integer
123 | **Default**: `5` 124 | 125 | `initialPosition` 126 | --- 127 | Sets the initial position of the cube in terms of 90 degree [turnsRight, turnsUp] from the default position where the front side is facing forward with the top, bottom, left, and right sides respectively above, below, left, and right of the front side. You can use non-integers to specify partial turns. 128 | 129 | **Expected Value**: [Number, Number]
130 | **Default**: `[0, 0]` 131 | 132 | `horizontalCoefficient` 133 | --- 134 | The relative speed that the cube will rotate in a horizontal direction. 135 | 136 | **Expected Value**: Number
137 | **Default**: `1` 138 | 139 | `verticalCoeficcient` 140 | --- 141 | The relative speed that the cube will rotate in a vertical direction. 142 | 143 | **Expected Value**: Number
144 | **Default**: `1` 145 | 146 | Methods 147 | === 148 | `setOption(property, value)` 149 | --- 150 | Used to dynamically change the cube's settings. 151 | 152 | `startAnimation()` 153 | --- 154 | Starts animating the cube 155 | 156 | `stopAnimation()` 157 | --- 158 | Stops animating the cube 159 | 160 | `setPosition(position, time, framesPerSecond)` 161 | --- 162 | Moves the cube to the specified position (see `initialPosition` setting) in the given time in seconds at the given frames per second. Accepts face names (`"front", "back", "left", "right", "top", "botton"`) as positions. 163 | 164 | `moveRight(turns, time, framesPerSecond)` 165 | --- 166 | Turns the cube the given number of turns to the right in the given time in seconds at the given number of frames per second. 167 | 168 | `moveLeft(turns, time, framesPerSecond)` 169 | --- 170 | Turns the cube the given number of turns to the left in the given time in seconds at the given number of frames per second. 171 | 172 | `moveUp(turns, time, framesPerSecond)` 173 | --- 174 | Turns the cube the given number of turns upward in the given time in seconds at the given number of frames per second. 175 | 176 | `moveDown(turns, time, framesPerSecond)` 177 | --- 178 | Turns the cube the given number of turns downward in the given time in seconds at the given number of frames per second. 179 | 180 | `move([turnsRight, turnsUp], time, framesPerSecond)` 181 | --- 182 | Turns the cube the given number of turns in the horizontal and vertical directions at the same time in the given time in seconds at the given number of frames per second. Negative numbers may be used to turn left or down. 183 | 184 | -------------------------------------------------------------------------------- /demo/CoolGear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterolson/jqCube/e5be3c64c7817f9ada60d2033c96af1b263699d0/demo/CoolGear.png -------------------------------------------------------------------------------- /demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fill out customer form 6 | 7 | 8 | 35 | 36 | 37 |
38 |
39 | Please wait while we process a request that you never made: 40 | 41 |
42 | While you are waiting, please do what you typically do while you are waiting for something to happen. Thank you. :)

43 |
44 |
45 | For your convenience, please fill out the following information. :) 46 | 47 | 48 | 52 | 55 | 56 | 57 | 61 | 64 | 65 | 66 | 67 | 71 | 74 | 75 | 76 | 80 | 83 | 84 | 85 | 89 | 92 | 93 | 94 | 95 | 99 | 102 | 103 | 104 | 105 | 109 | 112 | 113 | 114 | 118 | 121 | 122 | 123 | 127 | 130 | 131 | 132 | 133 | 137 | 147 | 148 | 149 |
49 | Username: 50 | 51 | 53 | 54 |
58 | Password: 59 | 60 | 62 | 63 |
68 | Confirm password: 69 | 70 | 72 | 73 |
77 | Social Security Number: 78 | 79 | 81 | 82 |
86 | Security Question: 87 | 88 | 90 | 91 |
96 | Security Answer: 97 | 98 | 100 | 101 |
106 | Telephone Number: 107 | 108 | 110 | 111 |
115 | Street Address: 116 | 117 | 119 | 120 |
124 | City: 125 | 126 | 128 | 129 |
134 | State: 135 | 136 | 138 | 146 |
150 | We feel sorry for our customers who live "Anywhere else". 151 |
152 |
153 | When you feel you are ready to submit you information, please click the submit button that is below (or above or sideways...it really depends on the orientation of the cube): 154 | 155 | 157 |
158 |
159 |

Important Legal Information!!!

160 | The software is for evaluation purposes only. You may not distribute any 161 | program you develop with the software. • Included CoolGear Programs. These 162 | license terms apply to all CoolGear programs included with the software. • 163 | Third Party Programs. The software contains third party programs. The license 164 | terms with those programs apply to your use of them.2. INTERNET-BASED SERVICES. 165 | CoolGear provides Internet-based services with the software. It may change or 166 | cancel them at any time. 3. TIME-SENSITIVE SOFTWARE. 167 | The software will stop 168 | running on 15 March 2008. You will not receive any other notice. 169 | You may not be 170 | able to access data used with the software when it stops running. 4. PRE-RELEASE 171 | SOFTWARE. This software is a pre-release version. It may not work the way a 172 | final version of the software will. We may change it for the final, commercial 173 | version. We also may not release a commercial version. 174 |
175 |
176 | 177 |

Sign Up

178 |

We at CoolGear think that our customers deserve the best. 179 | We are not completely agreed on what exactly best means, but we won't let that hinder our meaningless idealism.

180 |

But since we think our customers deserve the best, and presumably you would like to be a recipient of whatever the best is, 181 | we think it is reasonable to conclude that you would like to be one of our customers.

182 |

So, start filling out your information, already, and become one of our customers, so you can be one among those whom we thing deserve the best!

183 |

Really, go fill out your information. We won't bite.

184 |
185 |
186 | 187 | We're cool because we care about gear and care about our customers.
188 |
189 | Our Mission Statement: 190 | deliver bleeding-edge e-markets, streamline B2C Related Interoperability, 191 | transition one-to-one action-items, delayered transparent cloud applications, productize 24/7 relationships, 192 | mesh dynamic mindshare, grow plug-and-play web-readiness and innovate real-time experiences. 193 |
194 |
195 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /demo/wait.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterolson/jqCube/e5be3c64c7817f9ada60d2033c96af1b263699d0/demo/wait.gif -------------------------------------------------------------------------------- /jqCube.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Rotating Cube 5 | 6 | 7 | 8 | 9 | 10 | 34 | 35 |

Background
text

36 |
37 |
38 |

Top

39 |
40 |
41 |

Bottom

42 |
43 |
44 |

Front

45 |
46 |
47 |

Back

48 |
49 |
50 |

Right

51 |
52 |
53 |

Left

54 |
55 |
56 |
57 | Length: px
58 | Width: px
59 | Height: px
60 | Center:
61 |
62 | Light Direction:
63 | Min Luminosity: 50%
64 | Max Luminosity: 85%
65 | Hue: 128
66 | Saturation: 50%
67 | Opacity: 100%
68 |
69 | Turn
70 | Face 71 |
72 | Animate:
73 | Frames per second:
74 | Seconds per rotation:
75 | Horizontal rate:
76 | Vertical rate:
77 | Change HTML on
85 | 86 |
87 | 130 | 131 | -------------------------------------------------------------------------------- /jqCube.jquery.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | $.fn.cube = function (settings) { 3 | settings = settings || {}; 4 | var $this = this; 5 | var length, width, height, brickCenter, lightVector, cubeColor, alpha, saturation, minLum, maxLum, lumFactor, frameRate, secondsPerRotation, xInit, yInit, xRate, yRate, cubeDiv, front, back, left, right, bottom, top, animating, ivl, num; 6 | 7 | function or(l, r) { 8 | return typeof l !== "undefined" ? l : r; 9 | } 10 | 11 | var positions = { 12 | front: [0, 0], 13 | left: [-1, 0], 14 | right: [1, 0], 15 | back: [2, 0], 16 | top: [0, 1], 17 | bottom: [0, -1] 18 | }; 19 | 20 | function applySettings(settings) { 21 | length = settings.length || length || 400; 22 | width = settings.width || width || 400; 23 | height = settings.height || height || 400; 24 | brickCenter = settings.center || brickCenter || [300, 300]; 25 | 26 | lightVector = settings.lightDirection ? [[settings.lightDirection[0]], [settings.lightDirection[1]], [settings.lightDirection[2]]] : lightVector || [[1], [1], [1]]; 27 | cubeColor = or(settings.hue, or(cubeColor, 128)); 28 | alpha = or(settings.opacity, (or(alpha, 1) * 100)) / 100; 29 | saturation = or(settings.saturation, or(saturation, 50)); 30 | minLum = or(settings.minLuminosity, or(minLum, 50)); 31 | maxLum = settings.maxLuminosity || maxLum || 85; 32 | if (maxLum < minLum) maxLum = minLum; 33 | lumFactor = maxLum - minLum; 34 | 35 | frameRate = 1000 / (settings.frameRate || (1000 / frameRate) || 20); 36 | secondsPerRotation = settings.secondsPerRotation || secondsPerRotation || 5; 37 | 38 | xInit = (settings.initialPosition ? settings.initialPosition[0] : (xInit / (Math.PI * 2)) || 0) * Math.PI / 2; 39 | yInit = (settings.initialPosition ? settings.initialPosition[1] : (yInit / (Math.PI * 2)) || 0) * Math.PI / 2; 40 | xRate = or(settings.horizontalCoefficient, or(xRate, 1)); 41 | yRate = or(settings.verticalCoeficcient, or(yRate, 1)); 42 | 43 | cubeDiv = settings.element ? $(settings.element) : $this; 44 | front = cubeDiv.find(".front"); 45 | back = cubeDiv.find(".back"); 46 | left = cubeDiv.find(".left"); 47 | right = cubeDiv.find(".right"); 48 | bottom = cubeDiv.find(".bottom"); 49 | top = cubeDiv.find(".top"); 50 | animating = settings.hasOwnProperty("animate") ? settings.animate : or(animating, true); 51 | num = num || 0; 52 | var n = num; 53 | draw(); 54 | num = n; 55 | if (animating) { 56 | clearInterval(ivl); 57 | ivl = setInterval(draw, frameRate); 58 | } 59 | } 60 | applySettings(settings); 61 | 62 | function increment() { 63 | return 4 * Math.PI / (frameRate * secondsPerRotation); 64 | }; 65 | 66 | function stopAnimation() { 67 | clearInterval(ivl); 68 | animating = false; 69 | return $this; 70 | } 71 | 72 | function startAnimation() { 73 | clearInterval(ivl); 74 | ivl = setInterval(draw, frameRate); 75 | animating = true; 76 | return $this; 77 | }; 78 | 79 | function setPosition(coords) { 80 | if (positions[coords]) coords = positions[coords]; 81 | xInit = coords[0] * Math.PI / 2; 82 | yInit = coords[1] * Math.PI / 2; 83 | num = 0; 84 | draw(); 85 | } 86 | 87 | function movePosition(coords, time, fps) { 88 | fps = fps || frameRate; 89 | time = time !== false ? 1 : false; 90 | var restart = animating, n = num; 91 | stopAnimation(); 92 | var iterations = time * fps, timeout = 1000 / fps; 93 | if (!time) iterations = 1; 94 | var init = [xInit, yInit]; 95 | var xStep = coords[0] * Math.PI / 2 / iterations, yStep = coords[1] * Math.PI / 2 / iterations; 96 | (function move(i) { 97 | if (i < iterations) { 98 | xInit += xStep; 99 | yInit += yStep; 100 | draw(); 101 | num = n; 102 | setTimeout(function () { move(i + 1); }, timeout); 103 | return; 104 | } 105 | if (restart) startAnimation(); 106 | })(0); 107 | return $this; 108 | }; 109 | 110 | function moveHorizontally(n, time, fps) { 111 | movePosition([-n, 0], time, fps); 112 | } 113 | 114 | function moveVertically(n, time, fps) { 115 | movePosition([0, -n], time, fps); 116 | } 117 | 118 | function draw() { 119 | var qy = xInit + xRate * (num % (2 * Math.PI)); 120 | var qx = yInit + yRate * (num % (2 * Math.PI)); 121 | if (animating) num += increment(); 122 | var rotationMatrix = rotationMatrixCalc(qx, qy); 123 | var baseReferanceFrameX = [[1], [0], [0]], 124 | baseReferanceFrameY = [[0], [1], [0]], 125 | baseReferanceFrameZ = [[0], [0], [1]]; 126 | 127 | //---Cube--- 128 | drawRectangularSurface(front, brickCenter, [[0], [0], [length / 2]], height, width, [[1], [0], [0]], [[0], [1], [0]], qx, qy, cubeColor, lightVector); 129 | drawRectangularSurface(right, brickCenter, [[width / 2], [0], [0]], height, length, [[0], [0], [-1]], [[0], [1], [0]], qx, qy, cubeColor, lightVector); 130 | drawRectangularSurface(top, brickCenter, [[0], [height / 2], [0]], length, width, [[1], [0], [0]], [[0], [0], [-1]], qx, qy, cubeColor, lightVector); 131 | drawRectangularSurface(back, brickCenter, [[0], [0], [-length / 2]], height, width, [[-1], [0], [0]], [[0], [1], [0]], qx, qy, cubeColor, lightVector); 132 | drawRectangularSurface(left, brickCenter, [[-width / 2], [0], [0]], height, length, [[0], [0], [1]], [[0], [1], [0]], qx, qy, cubeColor, lightVector); 133 | drawRectangularSurface(bottom, brickCenter, [[0], [-height / 2], [0]], length, width, [[1], [0], [0]], [[0], [0], [1]], qx, qy, cubeColor, lightVector); 134 | 135 | function drawRectangularSurface(surface, rotationPoint2D, centerPoint3D, height, width, directionX, directionY, viewAngleX, viewAngleY, surfColor, lightVector) { 136 | var rotationMatrix = rotationMatrixCalc(viewAngleX, viewAngleY); 137 | directionX = matrixMultiply(rotationMatrix, vectorNorm(directionX)); 138 | directionY = matrixMultiply(rotationMatrix, vectorNorm(directionY)); 139 | directionZ = vectorCross3D(directionX, directionY); 140 | var baseReferanceFrameX = [[1], [0], [0]], 141 | baseReferanceFrameY = [[0], [1], [0]], 142 | baseReferanceFrameZ = [[0], [0], [1]]; 143 | surfaceMatrix = [[vectorDot(baseReferanceFrameX, directionX), vectorDot(baseReferanceFrameX, directionY), vectorDot(baseReferanceFrameX, directionZ)], 144 | [vectorDot(baseReferanceFrameY, directionX), vectorDot(baseReferanceFrameY, directionY), vectorDot(baseReferanceFrameY, directionZ)], 145 | [vectorDot(baseReferanceFrameZ, directionX), vectorDot(baseReferanceFrameZ, directionY), vectorDot(baseReferanceFrameZ, directionZ)]]; 146 | centerPoint3D = matrixMultiply(rotationMatrix, centerPoint3D); 147 | var lightAngle = minLum + lumFactor * vectorDot(vectorNorm(lightVector), vectorNorm(directionZ)); 148 | var placementX, placementY; 149 | if (directionZ[2][0] > 0) { 150 | placementX = rotationPoint2D[0] + centerPoint3D[0][0] - width / 2; 151 | placementY = rotationPoint2D[1] - centerPoint3D[1][0] - height / 2; 152 | surface.attr("style", "height: " + height + "px; width: " + width + "px;" + "background-color: hsla(" + surfColor + "," + saturation + "%," + lightAngle + "%," + alpha + ");" + "-webkit-transform: matrix(" + roundTo(surfaceMatrix[0][0], 5) + "," + (-roundTo(surfaceMatrix[1][0], 5)) + "," + (-roundTo(surfaceMatrix[0][1], 5)) + "," + roundTo(surfaceMatrix[1][1], 5) + ",0,0);" + "-moz-transform: matrix(" + roundTo(surfaceMatrix[0][0], 5) + "," + (-roundTo(surfaceMatrix[1][0], 5)) + "," + (-roundTo(surfaceMatrix[0][1], 5)) + "," + roundTo(surfaceMatrix[1][1], 5) + ",0,0);" + "-ms-transform: matrix(" + roundTo(surfaceMatrix[0][0], 5) + "," + (-roundTo(surfaceMatrix[1][0], 5)) + "," + (-roundTo(surfaceMatrix[0][1], 5)) + "," + roundTo(surfaceMatrix[1][1], 5) + ",0,0);" + "-o-transform: matrix(" + roundTo(surfaceMatrix[0][0], 5) + "," + (-roundTo(surfaceMatrix[1][0], 5)) + "," + (-roundTo(surfaceMatrix[0][1], 5)) + "," + roundTo(surfaceMatrix[1][1], 5) + ",0,0);" + "position: absolute; left: " + placementX + "px; top: " + placementY + "px;"); 153 | } 154 | else { 155 | surface.css({ "position": "absolute", "left": "-9999px" }); 156 | } 157 | } 158 | 159 | function rotationMatrixCalc(qx, qy) { 160 | var A = [[1, 0, 0], [0, Math.cos(qx), -Math.sin(qx)], [0, Math.sin(qx), Math.cos(qx)]]; 161 | var B = [[Math.cos(qy), 0, -Math.sin(qy)], [0, 1, 0], [Math.sin(qy), 0, Math.cos(qy)]]; 162 | var rotationMatrix = matrixMultiply(A, B); 163 | return rotationMatrix; 164 | } 165 | 166 | function matrixMultiply(A, B) { 167 | var C = new Array(A.length); 168 | for (var i = 0; i < A.length; i++) { 169 | C[i] = new Array(B[0].length); 170 | for (var j = 0; j < B[0].length; j++) { 171 | C[i][j] = 0; 172 | for (var k = 0; k < B.length; k++) { 173 | C[i][j] += A[i][k] * B[k][j]; 174 | } 175 | } 176 | } 177 | return C; 178 | } 179 | 180 | function vectorDot(A, B) { 181 | var C = 0; 182 | for (var i = 0; i < A.length; i++) { 183 | C += A[i][0] * B[i][0]; 184 | } 185 | return C; 186 | } 187 | 188 | function vectorNorm(A) { 189 | magnitude = 0; 190 | var C = new Array(A.length); 191 | for (var i = 0; i < A.length; i++) { 192 | magnitude += A[i][0] * A[i][0]; 193 | } 194 | magnitude = Math.sqrt(magnitude); 195 | for (i = 0; i < A.length; i++) { 196 | C[i] = new Array(1); 197 | C[i][0] = A[i][0] / magnitude; 198 | } 199 | return C; 200 | } 201 | 202 | function vectorCross3D(A, B) { 203 | var C = new Array(3); 204 | C[0] = new Array(1); 205 | C[0][0] = A[1][0] * B[2][0] - A[2][0] * B[1][0]; 206 | C[1] = new Array(1); 207 | C[1][0] = A[2][0] * B[0][0] - A[0][0] * B[2][0]; 208 | C[2] = new Array(1); 209 | C[2][0] = A[0][0] * B[1][0] - A[1][0] * B[0][0]; 210 | return C; 211 | } 212 | 213 | function roundTo(number, numberOfDecimalPlaces) { 214 | var roundedNumber = Math.round(number * Math.pow(10, numberOfDecimalPlaces)) / Math.pow(10, numberOfDecimalPlaces); 215 | return roundedNumber; 216 | } 217 | } 218 | 219 | this.setOption = function (prop, value) { 220 | settings[prop] = value; 221 | applySettings(settings); 222 | return $this; 223 | }; 224 | 225 | this.startAnimation = startAnimation; 226 | this.stopAnimation = stopAnimation; 227 | 228 | this.move = movePosition; 229 | this.moveRight = moveHorizontally; 230 | this.moveLeft = function (a, b, c) { return moveHorizontally(-a, b, c); }; 231 | this.moveUp = moveVertically; 232 | this.moveDown = function (a, b, c) { return moveVertically(-a, b, c); }; 233 | 234 | this.setPosition = setPosition; 235 | 236 | return this; 237 | }; 238 | })(jQuery); -------------------------------------------------------------------------------- /jqCube.jquery.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a.fn.cube=function(t){function b(a,b){return"undefined"!==typeof a?a:b}function A(t){d=t.length||d||400;e=t.width||e||400;f=t.height||f||400;g=t.center||g||[300,300];h=t.lightDirection?[[t.lightDirection[0]],[t.lightDirection[1]],[t.lightDirection[2]]]:h||[[1],[1],[1]];i=b(t.hue,b(i,128));u=b(t.opacity,100*b(u,1))/100;v=b(t.saturation,b(v,50));l=b(t.minLuminosity,b(l,50));n=t.maxLuminosity||n||85;n