├── Brandbook Automation ├── FillAndScale.js ├── FillPage.js ├── Footer.js ├── Grid14pt.js ├── Header.js ├── PageSizes.js ├── README_RU.txt └── TheSamePosition.js ├── Delaunay.js ├── Generator.js ├── ParseShape.js ├── README ├── Splitter ├── Butterflator.js ├── Cubeficator │ ├── cube gradientor CF.js │ ├── cube gradientor Cx.js │ ├── cube gradientor H.js │ ├── cube gradientor R.js │ ├── cube gradientor Rx.js │ ├── cube random shift.js │ ├── cube split gradient.js │ ├── cube split hilight.js │ ├── cube split.js │ └── draw_cube.js ├── Exploder.js ├── GetImagesWithSameColor.js ├── PackingColor.js ├── Random Splitter.js ├── ReplaceWithImage.js ├── SplitBrush.js ├── Splitter Raster Color.js ├── Splitter Raster Gradient.js ├── Splitter.js ├── SplitterHex.js └── packing.js ├── Triangular.js ├── Triangular2.js └── colors.js /Brandbook Automation/FillAndScale.js: -------------------------------------------------------------------------------- 1 | if(document.selectedItems.length>0) { 2 | var item = document.selectedItems[0]; 3 | for(var i=0; i0) { 2 | var item = document.selectedItems[0]; 3 | for(var i=0; i hs ? ws : hs; 9 | clone.bounds.center = A.bounds.center; 10 | clone.scale(ss); 11 | clone.translate(-document.activeArtboard.bounds.topLeft); 12 | var mask = new Path.Rectangle(A.bounds); 13 | var g = new Group(); 14 | g.appendChild(clone); 15 | g.appendChild(mask); 16 | g.clipped = true; 17 | mask.clipMask = true; 18 | mask.translate(-document.activeArtboard.bounds.topLeft); 19 | 20 | }; 21 | item.remove(); 22 | 23 | } -------------------------------------------------------------------------------- /Brandbook Automation/Footer.js: -------------------------------------------------------------------------------- 1 | if(document.selectedItems.length>0) { 2 | var item = document.selectedItems[0]; 3 | for(var i=0; iA.bounds.height) { 12 | var mask = new Path.Rectangle(A.bounds); 13 | var g = new Group(); 14 | g.appendChild(clone); 15 | g.appendChild(mask); 16 | g.clipped = true; 17 | mask.clipMask = true; 18 | mask.translate(-document.activeArtboard.bounds.topLeft); 19 | } 20 | 21 | }; 22 | item.remove(); 23 | 24 | } -------------------------------------------------------------------------------- /Brandbook Automation/Grid14pt.js: -------------------------------------------------------------------------------- 1 | var p2mm = 2.83464567; 2 | 3 | for(var i=0; i0) { 2 | var item = document.selectedItems[0]; 3 | for(var i=0; iA.bounds.height) { 13 | var mask = new Path.Rectangle(A.bounds); 14 | var g = new Group(); 15 | g.appendChild(clone); 16 | g.appendChild(mask); 17 | g.clipped = true; 18 | mask.clipMask = true; 19 | mask.translate(-document.activeArtboard.bounds.topLeft); 20 | } 21 | }; 22 | item.remove(); 23 | 24 | } -------------------------------------------------------------------------------- /Brandbook Automation/PageSizes.js: -------------------------------------------------------------------------------- 1 | var p2mm = 2.83464567; 2 | 3 | for(var i=0; i0) { 2 | var item = document.selectedItems[0]; 3 | var active = document.activeArtboard; 4 | 5 | var kw = item.bounds.width/active.bounds.width; 6 | var dx = item.bounds.left - active.bounds.left; 7 | var dy = item.bounds.top - active.bounds.top; 8 | 9 | var pos = item.bounds.center; 10 | 11 | for(var i=0; i5) return false; 107 | return true; 108 | } 109 | 110 | function max3( a, b, c ) { return ( a >= b && a >= c ) ? a : ( b >= a && b >= c ) ? b : c; } 111 | function min3( a, b, c ) { return ( a <= b && a <= c ) ? a : ( b <= a && b <= c ) ? b : c; } 112 | function len ( p1, p2) { return Math.sqrt((p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y)); } 113 | 114 | // DELAUNAY // 115 | 116 | var EPSILON = 1.0e-6; 117 | 118 | //------------------------------------------------------------ 119 | // Vertex class 120 | //------------------------------------------------------------ 121 | 122 | function Vertex( x, y ) 123 | { 124 | this.x = x; 125 | this.y = y; 126 | 127 | } // Vertex 128 | 129 | //------------------------------------------------------------ 130 | // Triangle class 131 | //------------------------------------------------------------ 132 | 133 | function Triangle( v0, v1, v2 ) 134 | { 135 | this.v0 = v0; 136 | this.v1 = v1; 137 | this.v2 = v2; 138 | 139 | this.CalcCircumcircle(); 140 | 141 | } // Triangle 142 | 143 | Triangle.prototype.CalcCircumcircle = function() 144 | { 145 | // From: http://www.exaflop.org/docs/cgafaq/cga1.html 146 | 147 | var A = this.v1.x - this.v0.x; 148 | var B = this.v1.y - this.v0.y; 149 | var C = this.v2.x - this.v0.x; 150 | var D = this.v2.y - this.v0.y; 151 | 152 | var E = A*(this.v0.x + this.v1.x) + B*(this.v0.y + this.v1.y); 153 | var F = C*(this.v0.x + this.v2.x) + D*(this.v0.y + this.v2.y); 154 | 155 | var G = 2.0*(A*(this.v2.y - this.v1.y)-B*(this.v2.x - this.v1.x)); 156 | 157 | var dx, dy; 158 | 159 | if( Math.abs(G) < EPSILON ) 160 | { 161 | // Collinear - find extremes and use the midpoint 162 | 163 | 164 | var minx = min3( this.v0.x, this.v1.x, this.v2.x ); 165 | var miny = min3( this.v0.y, this.v1.y, this.v2.y ); 166 | var maxx = max3( this.v0.x, this.v1.x, this.v2.x ); 167 | var maxy = max3( this.v0.y, this.v1.y, this.v2.y ); 168 | 169 | this.center = new Vertex( ( minx + maxx ) / 2, ( miny + maxy ) / 2 ); 170 | 171 | dx = this.center.x - minx; 172 | dy = this.center.y - miny; 173 | } 174 | else 175 | { 176 | var cx = (D*E - B*F) / G; 177 | var cy = (A*F - C*E) / G; 178 | 179 | this.center = new Vertex( cx, cy ); 180 | 181 | dx = this.center.x - this.v0.x; 182 | dy = this.center.y - this.v0.y; 183 | } 184 | 185 | this.radius_squared = dx * dx + dy * dy; 186 | this.radius = Math.sqrt( this.radius_squared ); 187 | }; // CalcCircumcircle 188 | 189 | Triangle.prototype.InCircumcircle = function( v ) 190 | { 191 | var dx = this.center.x - v.x; 192 | var dy = this.center.y - v.y; 193 | var dist_squared = dx * dx + dy * dy; 194 | 195 | return ( dist_squared <= this.radius_squared ); 196 | 197 | }; // InCircumcircle 198 | 199 | 200 | //------------------------------------------------------------ 201 | // Edge class 202 | //------------------------------------------------------------ 203 | 204 | function Edge( v0, v1 ) 205 | { 206 | this.v0 = v0; 207 | this.v1 = v1; 208 | 209 | } // Edge 210 | 211 | 212 | //------------------------------------------------------------ 213 | // Triangulate 214 | // 215 | // Perform the Delaunay Triangulation of a set of vertices. 216 | // 217 | // vertices: Array of Vertex objects 218 | // 219 | // returns: Array of Triangles 220 | //------------------------------------------------------------ 221 | function Triangulate( vertices ) 222 | { 223 | var triangles = []; 224 | 225 | // 226 | // First, create a "supertriangle" that bounds all vertices 227 | // 228 | var st = CreateBoundingTriangle( vertices ); 229 | 230 | triangles.push( st ); 231 | 232 | // 233 | // Next, begin the triangulation one vertex at a time 234 | // 235 | var i; 236 | for( i in vertices ) 237 | { 238 | // NOTE: This is O(n^2) - can be optimized by sorting vertices 239 | // along the x-axis and only considering triangles that have 240 | // potentially overlapping circumcircles 241 | 242 | var vertex = vertices[i]; 243 | AddVertex( vertex, triangles ); 244 | } 245 | 246 | // 247 | // Remove triangles that shared edges with "supertriangle" 248 | // 249 | for( i in triangles ) 250 | { 251 | var triangle = triangles[i]; 252 | 253 | if( triangle.v0 == st.v0 || triangle.v0 == st.v1 || triangle.v0 == st.v2 || 254 | triangle.v1 == st.v0 || triangle.v1 == st.v1 || triangle.v1 == st.v2 || 255 | triangle.v2 == st.v0 || triangle.v2 == st.v1 || triangle.v2 == st.v2 ) 256 | { 257 | delete triangles[i]; 258 | } 259 | } 260 | 261 | return triangles; 262 | 263 | } // Triangulate 264 | 265 | 266 | // Internal: create a triangle that bounds the given vertices, with room to spare 267 | function CreateBoundingTriangle( vertices ) 268 | { 269 | // NOTE: There's a bit of a heuristic here. If the bounding triangle 270 | // is too large and you see overflow/underflow errors. If it is too small 271 | // you end up with a non-convex hull. 272 | 273 | var minx, miny, maxx, maxy; 274 | for( var i in vertices ) 275 | { 276 | var vertex = vertices[i]; 277 | if( minx === undefined || vertex.x < minx ) { minx = vertex.x; } 278 | if( miny === undefined || vertex.y < miny ) { miny = vertex.y; } 279 | if( maxx === undefined || vertex.x > maxx ) { maxx = vertex.x; } 280 | if( maxy === undefined || vertex.y > maxy ) { maxy = vertex.y; } 281 | } 282 | 283 | var dx = ( maxx - minx ) * 10; 284 | var dy = ( maxy - miny ) * 10; 285 | 286 | var stv0 = new Vertex( minx - dx, miny - dy*3 ); 287 | var stv1 = new Vertex( minx - dx, maxy + dy ); 288 | var stv2 = new Vertex( maxx + dx*3, maxy + dy ); 289 | 290 | return new Triangle( stv0, stv1, stv2 ); 291 | 292 | } // CreateBoundingTriangle 293 | 294 | 295 | // Internal: update triangulation with a vertex 296 | function AddVertex( vertex, triangles ) 297 | { 298 | var edges = []; 299 | 300 | // Remove triangles with circumcircles containing the vertex 301 | var i; 302 | for( i in triangles ) 303 | { 304 | var triangle = triangles[i]; 305 | 306 | if( triangle.InCircumcircle( vertex ) ) 307 | { 308 | edges.push( new Edge( triangle.v0, triangle.v1 ) ); 309 | edges.push( new Edge( triangle.v1, triangle.v2 ) ); 310 | edges.push( new Edge( triangle.v2, triangle.v0 ) ); 311 | 312 | delete triangles[i]; 313 | } 314 | } 315 | 316 | edges = UniqueEdges( edges ); 317 | 318 | // Create new triangles from the unique edges and new vertex 319 | for( i in edges ) 320 | { 321 | var edge = edges[i]; 322 | 323 | triangles.push( new Triangle( edge.v0, edge.v1, vertex ) ); 324 | } 325 | } // AddVertex 326 | 327 | 328 | // Internal: remove duplicate edges from an array 329 | function UniqueEdges( edges ) 330 | { 331 | // TODO: This is O(n^2), make it O(n) with a hash or some such 332 | var uniqueEdges = []; 333 | for( var i in edges ) 334 | { 335 | var edge1 = edges[i]; 336 | var unique = true; 337 | 338 | for( var j in edges ) 339 | { 340 | if( i != j ) 341 | { 342 | var edge2 = edges[j]; 343 | 344 | if( ( edge1.v0 == edge2.v0 && edge1.v1 == edge2.v1 ) || 345 | ( edge1.v0 == edge2.v1 && edge1.v1 == edge2.v0 ) ) 346 | { 347 | unique = false; 348 | break; 349 | } 350 | } 351 | } 352 | 353 | if( unique ) 354 | { 355 | uniqueEdges.push( edge1 ); 356 | } 357 | } 358 | 359 | return uniqueEdges; 360 | 361 | } // UniqueEdges 362 | 363 | 364 | -------------------------------------------------------------------------------- /Generator.js: -------------------------------------------------------------------------------- 1 | // Define the values object, declaring default values and also 2 | // receiving the user changes, for simpler further reference: 3 | var values = { 4 | string: "a.selivonchik@grape.ru\ra.budilin@grape.ru\ra.kazanov@ailove.ru\rannakryazheva@lifecom.ru", 5 | button: 'Generrate' 6 | }; 7 | 8 | var components = { 9 | 10 | string: { 11 | type: 'string', label: 'emails', 12 | multiline: true, rows: 6, columns: 32 13 | }, 14 | 15 | button: { 16 | type: 'button', 17 | onClick: function() { 18 | process(); 19 | } 20 | } 21 | 22 | }; 23 | 24 | var palette = new Palette('emails', components, values); 25 | 26 | cx = 400; 27 | cy = 400; 28 | step = 300; 29 | 30 | function process () { 31 | 32 | emails = String(values.string).split("\r"); 33 | side = Math.ceil(Math.sqrt(emails.length)); 34 | 35 | for (e=0; e 32) bkey = binb_sha512(bkey, key.length * 8); 154 | 155 | var ipad = Array(32), opad = Array(32); 156 | for(var i = 0; i < 32; i++) 157 | { 158 | ipad[i] = bkey[i] ^ 0x36363636; 159 | opad[i] = bkey[i] ^ 0x5C5C5C5C; 160 | } 161 | 162 | var hash = binb_sha512(ipad.concat(rstr2binb(data)), 1024 + data.length * 8); 163 | return binb2rstr(binb_sha512(opad.concat(hash), 1024 + 512)); 164 | } 165 | 166 | /* 167 | * Convert a raw string to a hex string 168 | */ 169 | function rstr2hex(input) 170 | { 171 | try { hexcase } catch(e) { hexcase=0; } 172 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; 173 | var output = ""; 174 | var x; 175 | for(var i = 0; i < input.length; i++) 176 | { 177 | x = input.charCodeAt(i); 178 | output += hex_tab.charAt((x >>> 4) & 0x0F) 179 | + hex_tab.charAt( x & 0x0F); 180 | } 181 | return output; 182 | } 183 | 184 | /* 185 | * Convert a raw string to a base-64 string 186 | */ 187 | function rstr2b64(input) 188 | { 189 | try { b64pad } catch(e) { b64pad=''; } 190 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 191 | var output = ""; 192 | var len = input.length; 193 | for(var i = 0; i < len; i += 3) 194 | { 195 | var triplet = (input.charCodeAt(i) << 16) 196 | | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) 197 | | (i + 2 < len ? input.charCodeAt(i+2) : 0); 198 | for(var j = 0; j < 4; j++) 199 | { 200 | if(i * 8 + j * 6 > input.length * 8) output += b64pad; 201 | else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); 202 | } 203 | } 204 | return output; 205 | } 206 | 207 | /* 208 | * Convert a raw string to an arbitrary string encoding 209 | */ 210 | function rstr2any(input, encoding) 211 | { 212 | var divisor = encoding.length; 213 | var i, j, q, x, quotient; 214 | 215 | /* Convert to an array of 16-bit big-endian values, forming the dividend */ 216 | var dividend = Array(Math.ceil(input.length / 2)); 217 | for(i = 0; i < dividend.length; i++) 218 | { 219 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); 220 | } 221 | 222 | /* 223 | * Repeatedly perform a long division. The binary array forms the dividend, 224 | * the length of the encoding is the divisor. Once computed, the quotient 225 | * forms the dividend for the next step. All remainders are stored for later 226 | * use. 227 | */ 228 | var full_length = Math.ceil(input.length * 8 / 229 | (Math.log(encoding.length) / Math.log(2))); 230 | var remainders = Array(full_length); 231 | for(j = 0; j < full_length; j++) 232 | { 233 | quotient = Array(); 234 | x = 0; 235 | for(i = 0; i < dividend.length; i++) 236 | { 237 | x = (x << 16) + dividend[i]; 238 | q = Math.floor(x / divisor); 239 | x -= q * divisor; 240 | if(quotient.length > 0 || q > 0) 241 | quotient[quotient.length] = q; 242 | } 243 | remainders[j] = x; 244 | dividend = quotient; 245 | } 246 | 247 | /* Convert the remainders to the output string */ 248 | var output = ""; 249 | for(i = remainders.length - 1; i >= 0; i--) 250 | output += encoding.charAt(remainders[i]); 251 | 252 | return output; 253 | } 254 | 255 | /* 256 | * Encode a string as utf-8. 257 | * For efficiency, this assumes the input is valid utf-16. 258 | */ 259 | function str2rstr_utf8(input) 260 | { 261 | var output = ""; 262 | var i = -1; 263 | var x, y; 264 | 265 | while(++i < input.length) 266 | { 267 | /* Decode utf-16 surrogate pairs */ 268 | x = input.charCodeAt(i); 269 | y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; 270 | if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) 271 | { 272 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); 273 | i++; 274 | } 275 | 276 | /* Encode output as utf-8 */ 277 | if(x <= 0x7F) 278 | output += String.fromCharCode(x); 279 | else if(x <= 0x7FF) 280 | output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), 281 | 0x80 | ( x & 0x3F)); 282 | else if(x <= 0xFFFF) 283 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 284 | 0x80 | ((x >>> 6 ) & 0x3F), 285 | 0x80 | ( x & 0x3F)); 286 | else if(x <= 0x1FFFFF) 287 | output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 288 | 0x80 | ((x >>> 12) & 0x3F), 289 | 0x80 | ((x >>> 6 ) & 0x3F), 290 | 0x80 | ( x & 0x3F)); 291 | } 292 | return output; 293 | } 294 | 295 | /* 296 | * Encode a string as utf-16 297 | */ 298 | function str2rstr_utf16le(input) 299 | { 300 | var output = ""; 301 | for(var i = 0; i < input.length; i++) 302 | output += String.fromCharCode( input.charCodeAt(i) & 0xFF, 303 | (input.charCodeAt(i) >>> 8) & 0xFF); 304 | return output; 305 | } 306 | 307 | function str2rstr_utf16be(input) 308 | { 309 | var output = ""; 310 | for(var i = 0; i < input.length; i++) 311 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, 312 | input.charCodeAt(i) & 0xFF); 313 | return output; 314 | } 315 | 316 | /* 317 | * Convert a raw string to an array of big-endian words 318 | * Characters >255 have their high-byte silently ignored. 319 | */ 320 | function rstr2binb(input) 321 | { 322 | var output = Array(input.length >> 2); 323 | for(var i = 0; i < output.length; i++) 324 | output[i] = 0; 325 | for(var i = 0; i < input.length * 8; i += 8) 326 | output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); 327 | return output; 328 | } 329 | 330 | /* 331 | * Convert an array of big-endian words to a string 332 | */ 333 | function binb2rstr(input) 334 | { 335 | var output = ""; 336 | for(var i = 0; i < input.length * 32; i += 8) 337 | output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF); 338 | return output; 339 | } 340 | 341 | /* 342 | * Calculate the SHA-512 of an array of big-endian dwords, and a bit length 343 | */ 344 | var sha512_k; 345 | function binb_sha512(x, len) 346 | { 347 | if(sha512_k == undefined) 348 | { 349 | //SHA512 constants 350 | sha512_k = new Array( 351 | new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd), 352 | new int64(-1245643825, -330482897), new int64(-373957723, -2121671748), 353 | new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031), 354 | new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736), 355 | new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe), 356 | new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302), 357 | new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1), 358 | new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428), 359 | new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3), 360 | new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65), 361 | new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483), 362 | new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459), 363 | new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210), 364 | new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340), 365 | new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395), 366 | new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70), 367 | new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926), 368 | new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473), 369 | new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8), 370 | new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b), 371 | new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023), 372 | new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30), 373 | new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910), 374 | new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8), 375 | new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53), 376 | new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016), 377 | new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893), 378 | new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397), 379 | new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60), 380 | new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec), 381 | new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047), 382 | new int64(-1090935817, -1295615723), new int64(-965641998, -479046869), 383 | new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207), 384 | new int64(-354779690, -840897762), new int64(-176337025, -294727304), 385 | new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026), 386 | new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b), 387 | new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493), 388 | new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620), 389 | new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430), 390 | new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)); 391 | } 392 | 393 | //Initial hash values 394 | var H = new Array( 395 | new int64(0x6a09e667, -205731576), 396 | new int64(-1150833019, -2067093701), 397 | new int64(0x3c6ef372, -23791573), 398 | new int64(-1521486534, 0x5f1d36f1), 399 | new int64(0x510e527f, -1377402159), 400 | new int64(-1694144372, 0x2b3e6c1f), 401 | new int64(0x1f83d9ab, -79577749), 402 | new int64(0x5be0cd19, 0x137e2179)); 403 | 404 | var T1 = new int64(0, 0), 405 | T2 = new int64(0, 0), 406 | a = new int64(0,0), 407 | b = new int64(0,0), 408 | c = new int64(0,0), 409 | d = new int64(0,0), 410 | e = new int64(0,0), 411 | f = new int64(0,0), 412 | g = new int64(0,0), 413 | h = new int64(0,0), 414 | //Temporary variables not specified by the document 415 | s0 = new int64(0, 0), 416 | s1 = new int64(0, 0), 417 | Ch = new int64(0, 0), 418 | Maj = new int64(0, 0), 419 | r1 = new int64(0, 0), 420 | r2 = new int64(0, 0), 421 | r3 = new int64(0, 0); 422 | var j, i; 423 | var W = new Array(80); 424 | for(i=0; i<80; i++) 425 | W[i] = new int64(0, 0); 426 | 427 | // append padding to the source string. The format is described in the FIPS. 428 | x[len >> 5] |= 0x80 << (24 - (len & 0x1f)); 429 | x[((len + 128 >> 10)<< 5) + 31] = len; 430 | 431 | for(i = 0; i=32 539 | //The function revrrot() is for that 540 | function int64rrot(dst, x, shift) 541 | { 542 | dst.l = (x.l >>> shift) | (x.h << (32-shift)); 543 | dst.h = (x.h >>> shift) | (x.l << (32-shift)); 544 | } 545 | 546 | //Reverses the dwords of the source and then rotates right by shift. 547 | //This is equivalent to rotation by 32+shift 548 | function int64revrrot(dst, x, shift) 549 | { 550 | dst.l = (x.h >>> shift) | (x.l << (32-shift)); 551 | dst.h = (x.l >>> shift) | (x.h << (32-shift)); 552 | } 553 | 554 | //Bitwise-shifts right a 64-bit number by shift 555 | //Won't handle shift>=32, but it's never needed in SHA512 556 | function int64shr(dst, x, shift) 557 | { 558 | dst.l = (x.l >>> shift) | (x.h << (32-shift)); 559 | dst.h = (x.h >>> shift); 560 | } 561 | 562 | //Adds two 64-bit numbers 563 | //Like the original implementation, does not rely on 32-bit operations 564 | function int64add(dst, x, y) 565 | { 566 | var w0 = (x.l & 0xffff) + (y.l & 0xffff); 567 | var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16); 568 | var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16); 569 | var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16); 570 | dst.l = (w0 & 0xffff) | (w1 << 16); 571 | dst.h = (w2 & 0xffff) | (w3 << 16); 572 | } 573 | 574 | //Same, except with 4 addends. Works faster than adding them one by one. 575 | function int64add4(dst, a, b, c, d) 576 | { 577 | var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff); 578 | var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16); 579 | var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16); 580 | var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16); 581 | dst.l = (w0 & 0xffff) | (w1 << 16); 582 | dst.h = (w2 & 0xffff) | (w3 << 16); 583 | } 584 | 585 | //Same, except with 5 addends 586 | function int64add5(dst, a, b, c, d, e) 587 | { 588 | var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff); 589 | var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16); 590 | var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16); 591 | var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16); 592 | dst.l = (w0 & 0xffff) | (w1 << 16); 593 | dst.h = (w2 & 0xffff) | (w3 << 16); 594 | } 595 | 596 | -------------------------------------------------------------------------------- /ParseShape.js: -------------------------------------------------------------------------------- 1 | // Export script for illustrator, map image generates map/area layout for html 2 | // Works in Illustrator cs5 with latest scriptographer plugin (http://scriptographer.org) 3 | 4 | // First we define the dialog components 5 | var components = { 6 | zoom: { type: 'string', label: 'zoom', value: "1.0" }, 7 | step: { type: 'string', label: 'step', value: "2.0" } 8 | }; 9 | 10 | // Now we bring up the dialog 11 | var values = Dialog.prompt('Enter zoom value', components); 12 | 13 | var zoom = values && values['zoom'] ? parseFloat(values['zoom']) : 1.0; 14 | print("ZOOM "+zoom); 15 | 16 | var step = values && values['step'] ? parseFloat(values['step']) : 2.0; 17 | print("STEP " + step); 18 | 19 | var doc = document; 20 | var arb = doc.artboards[0]; 21 | 22 | function parsePath(item, center) { 23 | 24 | console.log("Item", item, item.hasChildren()); 25 | var curves = ""; 26 | 27 | if (item.hasChildren()) { 28 | for each (path in item.children){ 29 | curves += parsePath(path, center); 30 | } 31 | return curves; 32 | } 33 | 34 | //var pp = new Path(); 35 | //pp.closed = item.closed; 36 | curves += "["; 37 | for each (curve in item.curves) { 38 | var l = curve.length; 39 | var s = Math.ceil(l/step); 40 | s =1.0/s; 41 | if (s > 0 && s < 1.0) { 42 | for (var t=0; t<1.0; t += s) { 43 | var pt = curve.getPoint(t); 44 | //pp.add(pt); 45 | curves += "{x:"+((pt.x-center.x)*zoom).toFixed(2)+",y:"+((pt.y-center.y)*zoom).toFixed(2)+"},"; 46 | } 47 | } 48 | //console.log(segment.handleIn); 49 | } 50 | return curves.slice(0,-1)+"],"; 51 | } 52 | 53 | 54 | var data = "var SHAPES = ["; 55 | for each (item in doc.selectedItems) { 56 | data += "{name:\""+item.name+"\",curves:"; 57 | data += "["+parsePath(item, item.bounds.center).slice(0,-1)+"]"; 58 | data += "},"; 59 | } 60 | data = data.slice(0,-1)+"];"; 61 | 62 | 63 | var file = new File(script.file.parent, "shapes.js"); 64 | if (file.exists()) file.remove(); 65 | file.open(); 66 | 67 | file.write(data); file.close(); -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ENG 2 | 3 | All this scripts under Creative Common license (CC BY-SA) 4 | http://creativecommons.org/licenses/by-sa/2.0/ 5 | It's mean YOU ARE FREE: 6 | to share — to copy, distribute and transmit the work, 7 | to remix — to adapt the work 8 | to make commercial use of the work 9 | 10 | BY: peko [peko@gasubasu.com] 11 | http://gasubasu.com 12 | 13 | RUS 14 | 15 | Все представленные здесь скрипты опубликованы под лицензией Creative Common (CC BY-SA) 16 | Это значит, что вы абсолютно свободны: 17 | как угодно распространять код, 18 | переделывать его под свои нужды, 19 | зарабатывать с его помощью деньги. 20 | С двумя лишь ограничениями: 21 | Код и в дальнейшем останется под той же лицензией (SA), 22 | Первоначальный автор будет упомянут (BY). 23 | 24 | Идеи и пожелания приветсвуются ;) 25 | Если что, пишите на peko@gasubasu.com 26 | -------------------------------------------------------------------------------- /Splitter/Butterflator.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLITING PATH AND COLOR IT WITH IMAGE BEHIND 5 | 6 | var raster; 7 | var k = 0.1; 8 | 9 | function segment(item, scale) { 10 | 11 | i = item.clone(); 12 | i.scale(scale); 13 | i.rotate(Math.random()*360); 14 | i.translate(new Point( 15 | (0.5-Math.random())*item.bounds.width, 16 | (0.5-Math.random())*item.bounds.height)); 17 | 18 | c1 = raster.getAverageColor(i); 19 | if (c1==null && c1==undefined) c1 = new RGBColor(1,1,1); 20 | var c2 = c1.clone(); 21 | 22 | c1.red *= 1-k; 23 | c1.green *= 1-k; 24 | c1.blue *= 1-k; 25 | 26 | c2.red *= 1+k; 27 | c2.green *= 1+k; 28 | c2.blue *= 1+k; 29 | 30 | var gr = new Gradient() {type: "linear", stops:[ 31 | new GradientStop(c2, 0), 32 | new GradientStop(c1, 1)]}; 33 | 34 | 35 | i.fillColor = new GradientColor(gr, i.bounds.topLeft, i.bounds.bottomRight); 36 | } 37 | 38 | function onMouseDown(event) { 39 | 40 | if (raster == null) { 41 | var rasters = document.getItems({ 42 | type: Raster, 43 | selected: true 44 | }); 45 | if(rasters.length > 0) { 46 | raster = rasters[0]; 47 | } else { 48 | raster = null; 49 | Dialog.alert('Please select an image first!') 50 | return; 51 | } 52 | } 53 | 54 | if(event.item) { 55 | if (event.item instanceof Path || event.item instanceof CompoundPath) { 56 | var s = 0.5; 57 | for (var c=0; c<27; c++) { 58 | segment(event.item, s-Math.random()*0.25); 59 | } 60 | event.item.remove(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube gradientor CF.js: -------------------------------------------------------------------------------- 1 | var items = document.selectedItems.clone(); 2 | var SQ3 = Math.sqrt(3); 3 | var gt = "linear"; 4 | for (si in items) { 5 | var i = items[si]; 6 | var c = i.bounds.center; 7 | var r = i.bounds.height / 2; 8 | var w = r * SQ3 / 2; 9 | var ml = 1.1; 10 | var sm = 0.5; 11 | if (i.children.length == 3) { 12 | 13 | var s = i.children[0]; 14 | if (s.fillColor) { 15 | if (s.fillColor.type != "rgb") s.fillColor = s.fillColor.convert("rgb"); 16 | var c1 = s.fillColor.clone(); 17 | var c2 = c1.clone(); 18 | c2.red *= ml; c2.red += sm; 19 | c2.green *= ml; c2.green += sm; 20 | c2.blue *= ml; c2.blue += sm; 21 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c2, 0), new GradientStop(c1, 1)]}; 22 | s.fillColor = new GradientColor(gr, new Point(c.x , c.y-r), c); 23 | } 24 | 25 | 26 | s = i.children[1]; 27 | if (s.fillColor) { 28 | if (s.fillColor.type != "rgb") s.fillColor = s.fillColor.convert("rgb"); 29 | c1 = s.fillColor.clone(); 30 | var c2 = c1.clone(); 31 | c2.red *= ml; c2.red += sm; 32 | c2.green *= ml; c2.green += sm; 33 | c2.blue *= ml; c2.blue += sm; 34 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c2, 0), new GradientStop(c1, 1)]}; 35 | s.fillColor = new GradientColor(gr, new Point(c.x+w, c.y+r/2), c); 36 | } 37 | 38 | s = i.children[2]; 39 | if (s.fillColor) { 40 | if (s.fillColor.type != "rgb") s.fillColor = s.fillColor.convert("rgb"); 41 | var c1 = s.fillColor.clone(); 42 | var c2 = c1.clone(); 43 | c2.red *= ml; c2.red += sm; 44 | c2.green *= ml; c2.green += sm; 45 | c2.blue *= ml; c2.blue += sm; 46 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c2, 0), new GradientStop(c1, 1)]}; 47 | s.fillColor = new GradientColor(gr, new Point(c.x-w, c.y+r/2), c); 48 | } 49 | } else print("NOT CUBE"); 50 | 51 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube gradientor Cx.js: -------------------------------------------------------------------------------- 1 | var items = document.selectedItems.clone(); 2 | var SQ3 = Math.sqrt(3); 3 | var gt = "linear"; 4 | for (si in items) { 5 | var i = items[si]; 6 | var c = i.bounds.center; 7 | var r = i.bounds.height / 2; 8 | var w = r * SQ3 / 2; 9 | if (i.children.length == 3) { 10 | 11 | 12 | var c1 = i.children[0].fillColor; 13 | var c2 = i.children[1].fillColor; 14 | var c3 = i.children[2].fillColor; 15 | 16 | c1 = c1 ? c1.clone() : new RGBColor(1,1,1); 17 | c2 = c2 ? c2.clone() : new RGBColor(1,1,1); 18 | c3 = c3 ? c3.clone() : new RGBColor(1,1,1); 19 | 20 | var s = i.children[0]; 21 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c1, 0), new GradientStop(c2, 1)]}; 22 | s.fillColor = new GradientColor(gr, new Point(c.x , c.y-r), c); 23 | 24 | s = i.children[1]; 25 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c2, 0), new GradientStop(c3, 1)]}; 26 | s.fillColor = new GradientColor(gr, new Point(c.x+w, c.y+r/2), c); 27 | 28 | s = i.children[2]; 29 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c3, 0), new GradientStop(c1, 1)]}; 30 | s.fillColor = new GradientColor(gr, new Point(c.x-w, c.y+r/2), c); 31 | } else print("NOT CUBE"); 32 | 33 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube gradientor H.js: -------------------------------------------------------------------------------- 1 | var items = document.selectedItems.clone(); 2 | var SQ3 = Math.sqrt(3); 3 | var gt = "linear"; 4 | for (si in items) { 5 | var i = items[si]; 6 | var c = i.bounds.center; 7 | var r = i.bounds.height / 2; 8 | var w = r * SQ3 / 2; 9 | var k = 0.05; 10 | if (i.children.length == 3) { 11 | 12 | var s = i.children[0]; 13 | if (s.fillColor) { 14 | if (s.fillColor.type != "rgb") s.fillColor = s.fillColor.convert("rgb"); 15 | var c1 = s.fillColor.clone(); 16 | var c2 = c1.clone(); 17 | c2.red *= 1+k; 18 | c2.green *= 1+k; 19 | c2.blue *= 1+k; 20 | 21 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c2, 0), new GradientStop(c1, 1)]}; 22 | s.fillColor = new GradientColor(gr, c, new Point(c.x , c.y-r)); 23 | } 24 | 25 | 26 | s = i.children[1]; 27 | if (s.fillColor) { 28 | if (s.fillColor.type != "rgb") s.fillColor = s.fillColor.convert("rgb"); 29 | c1 = s.fillColor.clone(); 30 | var c2 = c1.clone(); 31 | c2.red *= 1-k; 32 | c2.green *= 1-k; 33 | c2.blue *= 1-k; 34 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c1, 0), new GradientStop(c2, 1)]}; 35 | s.fillColor = new GradientColor(gr, new Point(c.x+w, c.y+r/2), c); 36 | } 37 | 38 | s = i.children[2]; 39 | if (s.fillColor) { 40 | if (s.fillColor.type != "rgb") s.fillColor = s.fillColor.convert("rgb"); 41 | var c1 = s.fillColor.clone(); 42 | var c2 = c1.clone(); 43 | c2.red *= 1-2*k; 44 | c2.green *= 1-2*k; 45 | c2.blue *= 1-2*k; 46 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c1, 0), new GradientStop(c2, 1)]}; 47 | s.fillColor = new GradientColor(gr, new Point(c.x-w, c.y+r/2), c); 48 | } 49 | } else print("NOT CUBE"); 50 | 51 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube gradientor R.js: -------------------------------------------------------------------------------- 1 | var items = document.selectedItems.clone(); 2 | var SQ3 = Math.sqrt(3); 3 | var gt = "linear"; 4 | for (si in items) { 5 | var i = items[si]; 6 | var c = i.bounds.center; 7 | var r = i.bounds.height / 2; 8 | var w = r * SQ3 / 2; 9 | if (i.children.length == 3) { 10 | 11 | 12 | var c1 = i.children[0].fillColor; 13 | var c2 = i.children[1].fillColor; 14 | var c3 = i.children[2].fillColor; 15 | 16 | c1 = c1!=null && c1!=undefined ? c1.clone() : new RGBColor(1,1,1); 17 | c2 = c2!=null && c2!=undefined ? c2.clone() : new RGBColor(1,1,1); 18 | c3 = c3!=null && c3!=undefined ? c3.clone() : new RGBColor(1,1,1); 19 | 20 | var s = i.children[0]; 21 | var gr = new Gradient() {type: gt, stops:[ 22 | new GradientStop(c1.clone(), 0), 23 | new GradientStop(c2.clone(), 1)]}; 24 | s.fillColor = new GradientColor(gr, 25 | new Point(c.x-w, c.y-r/2), 26 | new Point(c.x+w, c.y-r/2)); 27 | 28 | s = i.children[1]; 29 | gr = new Gradient() {type: gt, stops:[ 30 | new GradientStop(c2.clone(), 0), 31 | new GradientStop(c3.clone(), 1)]}; 32 | s.fillColor = new GradientColor(gr, 33 | new Point(c.x+w, c.y-r/2), 34 | new Point(c.x , c.y+r )); 35 | 36 | s = i.children[2]; 37 | gr = new Gradient() {type: gt, stops:[ 38 | new GradientStop(c3.clone(), 0), 39 | new GradientStop(c1.clone(), 1)]}; 40 | s.fillColor = new GradientColor(gr, 41 | new Point(c.x , c.y+r ), 42 | new Point(c.x-w, c.y-r/2)); 43 | 44 | } else print("NOT CUBE"); 45 | 46 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube gradientor Rx.js: -------------------------------------------------------------------------------- 1 | var items = document.selectedItems.clone(); 2 | var SQ3 = Math.sqrt(3); 3 | var gt = "linear"; 4 | for (si in items) { 5 | var i = items[si]; 6 | var c = i.bounds.center; 7 | var r = i.bounds.height / 2; 8 | var w = r * SQ3 / 2; 9 | if (i.children.length == 3) { 10 | 11 | 12 | var c1 = i.children[0].fillColor; 13 | var c2 = i.children[1].fillColor; 14 | var c3 = i.children[2].fillColor; 15 | 16 | c1 = c1 ? c1.clone() : new RGBColor(1,1,1); 17 | c2 = c2 ? c2.clone() : new RGBColor(1,1,1); 18 | c3 = c3 ? c3.clone() : new RGBColor(1,1,1); 19 | 20 | var s = i.children[0]; 21 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c1, 0), new GradientStop(c2, 1)]}; 22 | s.fillColor = new GradientColor(gr, new Point(c.x-w , c.y-r/2), new Point(c.x+w , c.y-r/2)); 23 | 24 | s = i.children[1]; 25 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c2, 0), new GradientStop(c3, 1)]}; 26 | s.fillColor = new GradientColor(gr, new Point(c.x , c.y+r), new Point(c.x+w , c.y-r/2)); 27 | 28 | s = i.children[2]; 29 | var gr = new Gradient() {type: gt, stops:[new GradientStop(c3, 0), new GradientStop(c1, 1)]}; 30 | s.fillColor = new GradientColor(gr, new Point(c.x-w , c.y-r/2), new Point(c.x , c.y+r)); 31 | } else print("NOT CUBE"); 32 | 33 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube random shift.js: -------------------------------------------------------------------------------- 1 | var items = document.selectedItems.clone(); 2 | var SQ3 = Math.sqrt(3); 3 | 4 | for (si in items) { 5 | var i = items[si]; 6 | var r = i.bounds.height / 4; 7 | var w = r * SQ3/2 8 | var rnd = Math.floor(Math.random()*3); 9 | 10 | switch (rnd) { 11 | case 0: 12 | i.position+= new Point( 0, -r); 13 | break; 14 | case 1: 15 | i.position+= new Point( w, r/2); 16 | break; 17 | case 2: 18 | i.position+= new Point(-w, r/2); 19 | break; 20 | 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube split gradient.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLITING PATH AND COLOR IT WITH IMAGE BEHIND 5 | 6 | var raster; 7 | 8 | var SQ3 = Math.sqrt(3); 9 | 10 | function createCube(c, r){ 11 | 12 | var w = r*SQ3/2; 13 | var g = []; 14 | 15 | var cl = raster.getAverageColor(c); 16 | 17 | var cl0 = cl.clone(); // reflex + yellow a bit 18 | cl0.red *= 1.3; 19 | cl0.green *= 1.2; 20 | cl0.blue *= 1.1; 21 | var cl1 = cl.clone(); // normal 22 | var cl2 = cl.clone(); // shadow 1 + blue a bit 23 | cl2.red *= 0.9; 24 | cl2.green *= 0.8; 25 | cl2.blue *= 0.7; 26 | var cl3 = cl2.clone();// shadow 2 + blue a bit 27 | cl3.red *= 0.9; 28 | cl3.green *= 0.8; 29 | cl3.blue *= 0.7; 30 | 31 | 32 | var gt = "linear"; 33 | var gr1 = new Gradient() {type: gt, stops:[new GradientStop(cl0, 0), new GradientStop(cl1, 1)]}; 34 | var gr2 = new Gradient() {type: gt, stops:[new GradientStop(cl1, 0), new GradientStop(cl2, 1)]}; 35 | var gr3 = new Gradient() {type: gt, stops:[new GradientStop(cl2, 0), new GradientStop(cl3, 1)]}; 36 | 37 | //top 38 | var s = new Path(); 39 | s.add(c); 40 | s.add(new Point(c.x-w, c.y-r/2)); 41 | s.add(new Point(c.x , c.y-r )); 42 | s.add(new Point(c.x+w, c.y-r/2)); 43 | s.closed = true; 44 | s.fillColor = new GradientColor(gr1, c, new Point(c.x , c.y-r )); 45 | g[0] = s; 46 | 47 | 48 | //right 49 | s = new Path(); 50 | s.add(c); 51 | s.add(new Point(c.x+w, c.y-r/2)); 52 | s.add(new Point(c.x+w, c.y+r/2)); 53 | s.add(new Point(c.x, c.y+r )); 54 | s.closed = true; 55 | 56 | s.fillColor = new GradientColor(gr2, new Point(c.x+w, c.y+r/2), c); 57 | g[1] = s; 58 | 59 | //left 60 | s = new Path(); 61 | s.add(c); 62 | s.add(new Point(c.x, c.y+r )); 63 | s.add(new Point(c.x-w, c.y+r/2)); 64 | s.add(new Point(c.x-w, c.y-r/2)); 65 | s.closed = true; 66 | cl.red *= 0.618; 67 | cl.green *= 0.618; 68 | cl.blue *= 0.618; 69 | s.fillColor = new GradientColor(gr3, new Point(c.x-w, c.y+r/2), c);; 70 | g[2] = s; 71 | 72 | new Group(g); 73 | 74 | } 75 | 76 | function onMouseDown(event) { 77 | 78 | if (raster == null) { 79 | var rasters = document.getItems({ 80 | type: Raster, 81 | selected: true 82 | }); 83 | if(rasters.length > 0) { 84 | raster = rasters[0]; 85 | } else { 86 | raster = null; 87 | Dialog.alert('Please select an image first!') 88 | return; 89 | } 90 | } 91 | 92 | if(event.item) { 93 | if(event.item instanceof Raster) 94 | createCube(event.item.bounds.center, event.item.bounds.width/SQ3); 95 | else if (event.item instanceof Group) { 96 | var b = event.item.bounds; 97 | var c = b.center; 98 | var r = b.height/2; 99 | var w = r*SQ3/2; 100 | 101 | createCube(new Point(c.x+w/2, c.y+r/4), r/2); 102 | createCube(new Point(c.x-w/2, c.y+r/4), r/2); 103 | createCube(new Point(c.x , c.y-r/2), r/2); 104 | 105 | createCube(new Point(c.x+w/2, c.y-r/4), r/2); 106 | createCube(new Point(c.x-w/2, c.y-r/4), r/2); 107 | createCube(new Point(c.x , c.y+r/2), r/2); 108 | 109 | createCube(c, r/2); 110 | 111 | event.item.remove(); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube split hilight.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLITING PATH AND COLOR IT WITH IMAGE BEHIND 5 | 6 | var raster; 7 | 8 | var SQ3 = Math.sqrt(3); 9 | 10 | function createCube(c, r){ 11 | 12 | var w = r*SQ3/2; 13 | var g = []; 14 | 15 | var k = 0.05; 16 | var cl = raster.getAverageColor(c); 17 | if (!cl) cl = new RGBColor(1,1,1); 18 | var cl1 = cl.clone(); // normal 19 | var cc = cl1.red * (1+3.0*k); 20 | cl1.red = cc > 1 ? 1 : cc; 21 | cc = cl1.green * (1+2.0*k); 22 | cl1.green = cc > 1 ? 1 : cc; 23 | cc = cl1.blue * (1+1.0*k); 24 | cl1.blue = cc > 1 ? 1 : cc; 25 | var cl2 = cl.clone(); // shadow 1 + blue a bit 26 | cl2.red *= 1-3.0*k; 27 | cl2.green *= 1-2.0*k; 28 | cl2.blue *= 1-1.0*k; 29 | var cl3 = cl2.clone();// shadow 2 + blue a bit 30 | cl3.red *= 1-3*k; 31 | cl3.green *= 1-2*k; 32 | cl3.blue *= 1-1*k; 33 | 34 | 35 | //top 36 | var s = new Path(); 37 | s.add(c); 38 | s.add(new Point(c.x-w, c.y-r/2)); 39 | s.add(new Point(c.x , c.y-r )); 40 | s.add(new Point(c.x+w, c.y-r/2)); 41 | s.closed = true; 42 | s.fillColor = cl1; 43 | g[0] = s; 44 | 45 | 46 | //right 47 | s = new Path(); 48 | s.add(c); 49 | s.add(new Point(c.x+w, c.y-r/2)); 50 | s.add(new Point(c.x+w, c.y+r/2)); 51 | s.add(new Point(c.x, c.y+r )); 52 | s.closed = true; 53 | 54 | s.fillColor = cl2; 55 | g[1] = s; 56 | 57 | //left 58 | s = new Path(); 59 | s.add(c); 60 | s.add(new Point(c.x, c.y+r )); 61 | s.add(new Point(c.x-w, c.y+r/2)); 62 | s.add(new Point(c.x-w, c.y-r/2)); 63 | s.closed = true; 64 | s.fillColor = cl3; 65 | g[2] = s; 66 | 67 | new Group(g); 68 | 69 | } 70 | 71 | function onMouseDown(event) { 72 | 73 | if (raster == null) { 74 | var rasters = document.getItems({ 75 | type: Raster, 76 | selected: true 77 | }); 78 | if(rasters.length > 0) { 79 | raster = rasters[0]; 80 | } else { 81 | raster = null; 82 | Dialog.alert('Please select an image first!') 83 | return; 84 | } 85 | } 86 | 87 | if(event.item) { 88 | if(event.item instanceof Raster) 89 | createCube(event.item.bounds.center, event.item.bounds.width/SQ3); 90 | else if (event.item instanceof Group) { 91 | var b = event.item.bounds; 92 | var c = b.center; 93 | var r = b.height/2; 94 | var w = r*SQ3/2; 95 | 96 | createCube(new Point(c.x+w/2, c.y+r/4), r/2); 97 | createCube(new Point(c.x-w/2, c.y+r/4), r/2); 98 | createCube(new Point(c.x , c.y-r/2), r/2); 99 | 100 | createCube(new Point(c.x+w/2, c.y-r/4), r/2); 101 | createCube(new Point(c.x-w/2, c.y-r/4), r/2); 102 | createCube(new Point(c.x , c.y+r/2), r/2); 103 | 104 | createCube(c, r/2); 105 | 106 | event.item.remove(); 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/cube split.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLITING PATH AND COLOR IT WITH IMAGE BEHIND 5 | 6 | var raster; 7 | 8 | var SQ3 = Math.sqrt(3); 9 | 10 | function createCube(c, r){ 11 | 12 | var w = r*SQ3/2; 13 | var g = []; 14 | 15 | //top 16 | var s = new Path(); 17 | s.add(c); 18 | s.add(new Point(c.x-w, c.y-r/2)); 19 | s.add(new Point(c.x , c.y-r )); 20 | s.add(new Point(c.x+w, c.y-r/2)); 21 | s.closed = true; 22 | s.fillColor = raster.getAverageColor(s); 23 | if (s.fillColor == null) s.fillColor = new RGBColor(1,1,1); 24 | g[0] = s; 25 | 26 | //right 27 | s = new Path(); 28 | s.add(c); 29 | s.add(new Point(c.x+w, c.y-r/2)); 30 | s.add(new Point(c.x+w, c.y+r/2)); 31 | s.add(new Point(c.x, c.y+r )); 32 | s.closed = true; 33 | s.fillColor = raster.getAverageColor(s); 34 | if (s.fillColor == null) s.fillColor = new RGBColor(1,1,1); 35 | g[1] = s; 36 | 37 | //left 38 | s = new Path(); 39 | s.add(c); 40 | s.add(new Point(c.x, c.y+r )); 41 | s.add(new Point(c.x-w, c.y+r/2)); 42 | s.add(new Point(c.x-w, c.y-r/2)); 43 | s.closed = true; 44 | s.fillColor = raster.getAverageColor(s); 45 | if (s.fillColor == null) s.fillColor = new RGBColor(1,1,1); 46 | g[2] = s; 47 | 48 | new Group(g); 49 | 50 | } 51 | 52 | function onMouseDown(event) { 53 | 54 | if (raster == null) { 55 | var rasters = document.getItems({ 56 | type: Raster, 57 | selected: true 58 | }); 59 | if(rasters.length > 0) { 60 | raster = rasters[0]; 61 | } else { 62 | raster = null; 63 | Dialog.alert('Please select an image first!') 64 | return; 65 | } 66 | } 67 | 68 | if(event.item) { 69 | if(event.item instanceof Raster) 70 | createCube(event.item.bounds.center, event.item.bounds.width/SQ3); 71 | else if (event.item instanceof Group) { 72 | var b = event.item.bounds; 73 | var c = b.center; 74 | var r = b.height/2; 75 | var w = r*SQ3/2; 76 | 77 | createCube(new Point(c.x+w/2, c.y+r/4), r/2); 78 | createCube(new Point(c.x-w/2, c.y+r/4), r/2); 79 | createCube(new Point(c.x , c.y-r/2), r/2); 80 | 81 | createCube(new Point(c.x+w/2, c.y-r/4), r/2); 82 | createCube(new Point(c.x-w/2, c.y-r/4), r/2); 83 | createCube(new Point(c.x , c.y+r/2), r/2); 84 | 85 | createCube(c, r/2); 86 | 87 | event.item.remove(); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /Splitter/Cubeficator/draw_cube.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peko/scriptographer_scripts/191765ff1e4ae79cc72bf620a039dda85e7dcb9d/Splitter/Cubeficator/draw_cube.js -------------------------------------------------------------------------------- /Splitter/Exploder.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLITING PATH AND COLOR IT WITH IMAGE BEHIND 5 | 6 | var raster; 7 | var k = 0.1; 8 | 9 | function segment(item, scale) { 10 | 11 | i = item.clone(); 12 | i.scale(scale); 13 | i.rotate(Math.random()*360); 14 | i.translate(new Point( 15 | (0.5-Math.random())*item.bounds.width, 16 | (0.5-Math.random())*item.bounds.height)); 17 | } 18 | 19 | function onMouseDown(event) { 20 | 21 | if(event.item) { 22 | if (event.item instanceof Path || event.item instanceof CompoundPath || event.item instanceof Group) { 23 | var s = 0.5; 24 | for (var c=0; c<5; c++) { 25 | segment(event.item, s-Math.random()*0.25); 26 | } 27 | event.item.remove(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Splitter/GetImagesWithSameColor.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | 5 | importPackage(java.net); 6 | importPackage(java.io); 7 | 8 | var bg='FFFFFF'; 9 | 10 | var item = document.selectedItems[0]; 11 | var color = item.fillColor; 12 | var R = (Math.round(color.red *255)).toString(16); if (R.length<2) R='0'+R; 13 | var G = (Math.round(color.green*255)).toString(16); if (G.length<2) G='0'+G; 14 | var B = (Math.round(color.blue *255)).toString(16); if (B.length<2) B='0'+B; 15 | var hex = R+G+B; 16 | 17 | if (item && hex) { 18 | 19 | var url = new URL('http://labs.ideeinc.com/coloursearch/services/rest/?method=color.search&quantity=50&page=0&colors='+bg+'%2C'+hex+'&imageset=flickr'); 20 | var stream = new DataInputStream(url.openStream()); 21 | var json = ''; 22 | var line; 23 | while ((line = stream.readLine()) != null) { 24 | json += line; 25 | }; 26 | var data = Json.decode(json); 27 | var i = 0; 28 | for (n in data.result) { 29 | url = new java.net.URL(data.result[n][1]); 30 | var image = new PlacedFile(url); 31 | 32 | image.bounds.width = 80; 33 | image.bounds.height = 80; 34 | image.bounds.center = new Point(i%10*81, Math.floor(i/10)*81); 35 | 36 | document.redraw(); 37 | i++; 38 | } 39 | } -------------------------------------------------------------------------------- /Splitter/PackingColor.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // CIRCLE PACKING (look wiki) 5 | 6 | var last; 7 | 8 | function onMouseDown(event) { 9 | 10 | var raster; 11 | var rasters = document.getItems({ 12 | type: Raster, 13 | selected: true 14 | }); 15 | 16 | if(rasters.length > 0) { 17 | raster = rasters[0]; 18 | } else { 19 | raster = null; 20 | Dialog.alert('Please select an image first!') 21 | return; 22 | } 23 | 24 | var center; 25 | var first; 26 | var circle = new Path.Circle(event.point, 10); 27 | if (last) circle.moveBelow(last); 28 | last = circle; 29 | 30 | while (circle) { 31 | if (circle) { 32 | var s = 1+2/circle.bounds.width; 33 | if (!center) circle.scale(s); 34 | else circle.scale(s, center); 35 | 36 | var r = circle.bounds.width/2; 37 | 38 | for (var i=0; i<36; i++) { 39 | var p =new Point( 40 | r*Math.sin(i*10/180*3.1415926), 41 | r*Math.cos(i*10/180*3.1415926) 42 | ) + circle.bounds.center; 43 | 44 | var hit = document.hitTest(p,1); 45 | if (hit && circle !== hit.item) { 46 | if(!center){ 47 | //new Path.Circle(p, 2); 48 | center = p; 49 | first = hit.item; 50 | } else if (hit.item !== first) { 51 | //new Path.Circle(p, 2); 52 | circle = null; 53 | break; 54 | } 55 | 56 | } 57 | 58 | } 59 | 60 | } 61 | circle.fillColor = raster.getAverageColor(circle); 62 | if(circle && circle.bounds.width > 200) break; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Splitter/Random Splitter.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // SPLITS PATHES OVER AN SELECTED IMAGE 5 | // Rasterize image to RGB before use 6 | 7 | var raster; 8 | 9 | function segment(item, position) { 10 | 11 | i = item.clone(); 12 | i.scale(0.5, position); 13 | c = raster.getAverageColor(i); 14 | i.fillColor = c; 15 | //print(c, i.fillColor); 16 | } 17 | 18 | function splitItem(item) { 19 | if (item instanceof Path) { 20 | segment(item, item.bounds.topLeft); 21 | segment(item, item.bounds.topRight); 22 | segment(item, item.bounds.bottomLeft); 23 | segment(item, item.bounds.bottomRight); 24 | item.remove(); 25 | } 26 | } 27 | 28 | if (raster == null) { 29 | var rasters = document.getItems({ 30 | type: Raster, 31 | selected: true 32 | }); 33 | if(rasters.length > 0) { 34 | raster = rasters[0]; 35 | } else { 36 | raster = null; 37 | Dialog.alert('Please select an image first!') 38 | } 39 | } 40 | 41 | var splitsNum = 0; 42 | if (raster) 43 | while (splitsNum < 100){ 44 | 45 | var p = Point( 46 | raster.bounds.left+Math.random()*raster.bounds.width, 47 | raster.bounds.top + Math.random()*raster.bounds.height 48 | ); 49 | var hitResult = document.hitTest(p); 50 | if (hitResult && hitResult.item instanceof Path){ 51 | splitItem(hitResult.item); 52 | splitsNum ++; 53 | //new Path.Circle(p, 5); 54 | //document.redraw(); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /Splitter/ReplaceWithImage.js: -------------------------------------------------------------------------------- 1 | // Replace With An Image v.1.2 2 | 3 | // Online search engine has some changes 4 | // So I fix request and parsing 5 | 6 | // DESCRIPTION 7 | 8 | // This script search for selected items images with similar color. 9 | // Then replace selected path with radnom image and clip it with original path. 10 | // Sorry guys, I'm to lazy to write GUI ;) 11 | 12 | // This code under Creative Common license (CC BY-SA) 13 | // http://creativecommons.org/licenses/by-sa/2.0/ 14 | // It's mean YOU ARE FREE: 15 | // to share — to copy, distribute and transmit the work, 16 | // to remix — to adapt the work 17 | // to make commercial use of the work 18 | 19 | // Script author peko [peko@gasubasu.com] 20 | // http://gasubasu.com 21 | 22 | // Script uses online services developed by Idée Inc. 23 | // http://ideeinc.com/ 24 | 25 | 26 | 27 | importPackage(java.net); 28 | importPackage(java.io); 29 | 30 | var bg = 'FFFFFF'; 31 | var bg_w = 0.25; // weight if bg color 32 | var hex_w = 0.75; // weight of main color 33 | var size = 100; 34 | 35 | 36 | items = document.selectedItems.clone(); 37 | for (si in items) { 38 | 39 | var item = items[si]; 40 | var color = item.fillColor; 41 | var R = (Math.round(color.red *255)).toString(16); if (R.length<2) R='0'+R; 42 | var G = (Math.round(color.green*255)).toString(16); if (G.length<2) G='0'+G; 43 | var B = (Math.round(color.blue *255)).toString(16); if (B.length<2) B='0'+B; 44 | var hex = R+G+B; 45 | 46 | if (item && hex) { 47 | 48 | 49 | 50 | // NEW URL 51 | // http://labs.tineye.com/multicolr/rest/color_search/ 52 | // ?limit=73 53 | // &offset=0 54 | // &return_metadata= 55 | // %3CuserID%2F%3E 56 | // %3CphotoID%2F%3E 57 | // %3CimageWidth%2F%3E 58 | // %3CimageHeight%2F%3E 59 | // &colors%5B0%5D=e7402d 60 | // &weights%5B0%5D=100 61 | 62 | 63 | 64 | var url = new URL( 65 | 'http://labs.tineye.com/multicolr/rest/color_search/?'+ 66 | 'limit=50&'+ 67 | 'offset=0&'+ 68 | 'return_metadata='+ 69 | '%3CuserID%2F%3E'+ 70 | '%3CphotoID%2F%3E'+ 71 | '%3CimageWidth%2F%3E'+ 72 | '%3CimageHeight%2F%3E&'+ 73 | 'colors%5B' +1+'%5D='+bg +'&'+ 74 | 'colors%5B' +0+'%5D='+hex +'&'+ 75 | 'weights%5B'+1+'%5D='+bg_w +'&'+ 76 | 'weights%5B'+0+'%5D='+hex_w 77 | ); 78 | 79 | 80 | var stream = new DataInputStream(url.openStream()); 81 | var json = ''; 82 | var line; 83 | 84 | while ((line = stream.readLine()) != null) { 85 | json += line; 86 | }; 87 | var data = Json.decode(json); 88 | 89 | if (!data) { 90 | print("Data parse Error\n"+json); 91 | } else { 92 | 93 | // RETURNIG DATA FORMAT LOOKS LIKE NEXT OBJECT: 94 | 95 | // NEW URL 96 | // http://img.tineye.com/flickr-images/?filepath=labs-flickr-public/images/ 97 | // 49/7584251048_4900a64b9b_m.jpg 98 | // &size=76 99 | 100 | var file = data.result[Math.floor(Math.random()*50)]['filepath']; 101 | var name = file.split('/')[1]; 102 | var path = "http://img.tineye.com/flickr-images/?size=" + size + "&filepath=labs-flickr-public/images/" + file; 103 | print(path); 104 | 105 | url = new java.net.URL(path); 106 | 107 | var input = new java.io.BufferedInputStream(url.openStream()); 108 | var output = new java.io.FileOutputStream(name); 109 | var data = []; 110 | var b = 0 111 | while ((b = input.read()) != -1) { 112 | output.write(b); 113 | } 114 | input.close() 115 | output.close() 116 | var img = new java.io.File("./"+name); 117 | var image = new PlacedFile(img); 118 | image.bounds = item.bounds; 119 | var group = new Group(); 120 | 121 | group.appendChild(image); 122 | group.appendChild(item); 123 | 124 | group.clipped = true; 125 | item.clipMask = true; 126 | } 127 | } 128 | document.redraw(); 129 | } 130 | -------------------------------------------------------------------------------- /Splitter/SplitBrush.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLIT PATH ON 4 5 | 6 | 7 | var raster; 8 | 9 | tool.distanceThreshold = 5; 10 | 11 | function segment(item, position) { 12 | 13 | i = item.clone(); 14 | i.scale(0.5, position); 15 | c = raster.getAverageColor(i); 16 | i.fillColor = c; 17 | //print(c); 18 | } 19 | 20 | function onMouseDown(event) { 21 | 22 | if (raster == null) { 23 | var rasters = document.getItems({ 24 | type: Raster, 25 | selected: true 26 | }); 27 | if(rasters.length > 0) { 28 | raster = rasters[0]; 29 | } else { 30 | raster = null; 31 | Dialog.alert('Please select an image first!') 32 | return; 33 | } 34 | } 35 | 36 | if(event.item) { 37 | 38 | if (event.item instanceof Path) { 39 | segment(event.item, event.item.bounds.topLeft); 40 | segment(event.item, event.item.bounds.topRight); 41 | segment(event.item, event.item.bounds.bottomLeft); 42 | segment(event.item, event.item.bounds.bottomRight); 43 | 44 | event.item.remove(); 45 | } 46 | } 47 | } 48 | 49 | function onMouseDrag(event) { 50 | 51 | if(event.item) { 52 | 53 | if (event.item instanceof Path) { 54 | segment(event.item, event.item.bounds.topLeft); 55 | segment(event.item, event.item.bounds.topRight); 56 | segment(event.item, event.item.bounds.bottomLeft); 57 | segment(event.item, event.item.bounds.bottomRight); 58 | 59 | event.item.remove(); 60 | document.redraw(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Splitter/Splitter Raster Color.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLITING PATH AND COLOR IT WITH IMAGE BEHIND 5 | 6 | var raster; 7 | 8 | function segment(item, position) { 9 | 10 | i = item.clone(); 11 | i.scale(0.5, position); 12 | c = raster.getAverageColor(i); 13 | i.fillColor = c; 14 | print(c); 15 | } 16 | 17 | function onMouseDown(event) { 18 | 19 | if (raster == null) { 20 | var rasters = document.getItems({ 21 | type: Raster, 22 | selected: true 23 | }); 24 | if(rasters.length > 0) { 25 | raster = rasters[0]; 26 | } else { 27 | raster = null; 28 | Dialog.alert('Please select an image first!') 29 | return; 30 | } 31 | } 32 | 33 | if(event.item) { 34 | 35 | if (event.item instanceof Path) { 36 | segment(event.item, event.item.bounds.topLeft); 37 | segment(event.item, event.item.bounds.topRight); 38 | segment(event.item, event.item.bounds.bottomLeft); 39 | segment(event.item, event.item.bounds.bottomRight); 40 | 41 | event.item.remove(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Splitter/Splitter Raster Gradient.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLITING PATH AND COLORING IT WITH IMAGE BEHIND 5 | 6 | var raster; 7 | 8 | function segment(item, position) { 9 | 10 | i = item.clone(); 11 | i.scale(0.5, position); 12 | 13 | var gradient = new Gradient() { 14 | type:'linear', 15 | stops: [ 16 | new GradientStop(raster.getAverageColor(i.bounds.topLeft), 0), 17 | new GradientStop(raster.getAverageColor(i.bounds.bottomRight), 1), 18 | ] 19 | }; 20 | var gradientColor = new GradientColor(gradient, i.bounds.topLeft, i.bounds.bottomRight); 21 | 22 | c = raster.getAverageColor(i); 23 | i.fillColor = gradientColor; 24 | print(c); 25 | } 26 | 27 | function onMouseDown(event) { 28 | 29 | if (raster == null) { 30 | var rasters = document.getItems({ 31 | type: Raster, 32 | selected: true 33 | }); 34 | if(rasters.length > 0) { 35 | raster = rasters[0]; 36 | } else { 37 | raster = null; 38 | Dialog.alert('Please select an image first!') 39 | return; 40 | } 41 | } 42 | 43 | if(event.item) { 44 | 45 | if (event.item instanceof Path) { 46 | segment(event.item, event.item.bounds.topLeft); 47 | segment(event.item, event.item.bounds.topRight); 48 | segment(event.item, event.item.bounds.bottomLeft); 49 | segment(event.item, event.item.bounds.bottomRight); 50 | 51 | event.item.remove(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Splitter/Splitter.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLIT PATH ON 4 5 | 6 | function onMouseDown(event) { 7 | if(event.item) { 8 | 9 | event.item.clone().scale(0.5, event.item.bounds.topLeft); 10 | event.item.clone().scale(0.5, event.item.bounds.topRight); 11 | event.item.clone().scale(0.5, event.item.bounds.bottomLeft); 12 | event.item.clone().scale(0.5, event.item.bounds.bottomRight); 13 | event.item.remove(); 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /Splitter/SplitterHex.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // TOOL SPLIT PATH ON 7 PARTS 5 | 6 | function onMouseDown(event) { 7 | if(event.item) { 8 | 9 | var dx = event.item.bounds.width/4; 10 | var dh = event.item.bounds.width/2; 11 | var dy = event.item.bounds.width/4*Math.sqrt(3); 12 | 13 | event.item.clone().scale(0.3333, event.item.bounds.center); 14 | event.item.clone().scale(0.3333, event.item.bounds.center+new Point( dx, dy)); 15 | event.item.clone().scale(0.3333, event.item.bounds.center+new Point(-dx, dy)); 16 | event.item.clone().scale(0.3333, event.item.bounds.center+new Point( dx,-dy)); 17 | event.item.clone().scale(0.3333, event.item.bounds.center+new Point(-dx,-dy)); 18 | event.item.clone().scale(0.3333, event.item.bounds.center+new Point( dh, 0)); 19 | event.item.clone().scale(0.3333, event.item.bounds.center+new Point(-dh, 0)); 20 | 21 | event.item.remove(); 22 | } 23 | } -------------------------------------------------------------------------------- /Splitter/packing.js: -------------------------------------------------------------------------------- 1 | // Script author peko [peko@gasubasu.com] 2 | // http://gasubasu.com 3 | 4 | // CIRCLE PACKING (look wiki) 5 | 6 | var last; 7 | 8 | function onMouseDown(event) { 9 | var center; 10 | var first; 11 | var circle = new Path.Circle(event.point, 10); 12 | if (last) circle.moveBelow(last); 13 | last = circle; 14 | 15 | while (circle) { 16 | if (circle) { 17 | var s = 1+2/circle.bounds.width; 18 | if (!center) circle.scale(s); 19 | else circle.scale(s, center); 20 | 21 | var r = circle.bounds.width/2; 22 | 23 | for (var i=0; i<36; i++) { 24 | var p =new Point( 25 | r*Math.sin(i*10/180*3.1415926), 26 | r*Math.cos(i*10/180*3.1415926) 27 | ) + circle.bounds.center; 28 | 29 | var hit = document.hitTest(p,1); 30 | if (hit && circle !== hit.item) { 31 | if(!center){ 32 | //new Path.Circle(p, 2); 33 | center = p; 34 | first = hit.item; 35 | } else if (hit.item !== first) { 36 | //new Path.Circle(p, 2); 37 | circle = null; 38 | break; 39 | } 40 | 41 | } 42 | 43 | } 44 | 45 | } 46 | if(circle && circle.bounds.width > 200) break; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Triangular.js: -------------------------------------------------------------------------------- 1 | // Known bugs: 2 | // Removing lines when there are only one or two triangles removes the polygon group. 3 | 4 | var colorSets = { 5 | 'Icy': ['E1F2F7', '89BECE', '4F8B98', '0A3F47'], 6 | 'Citrus': ['FFDA17', 'EC6227', '6D1F08', '6D1F08'], 7 | 'White to Black': ['FBFBFB', 'ADADAD', '39393A', '000000'], 8 | 'Red Blue Yellow Purple': ['E30613', '602483', 'B5DEE4', 'FFE600'], 9 | 'Eye Candy': ['098e7b', 'fdb606', 'd40144', 'fb4810'], 10 | 'Fourties Lipstick': ['0d061c', 'be1900', '1e1c2c', '720a00'], 11 | 'Colorful Mess': ['0fe1fa', 'f0ff00', '009bf7', 'ff4001'], 12 | 'Plum': ['786262', 'b8988b', 'ebc8ad', '434a4c'], 13 | 'Green': ['044c29', '45bf55', '167f39', '96ed89'], 14 | 'Purple Cream': ['fee293', '380c2a', 'a6243d', 'e85400'], 15 | 'Orange Brown': ['520026', '822a21', 'fc9851', 'c2410a'], 16 | 'Green Gold': ['a99000', 'fffb8d', '298737', '005737'], 17 | 'Revolution': ['5f020d', '9c0413', 'd9cda9', '0d0000'] 18 | }; 19 | 20 | var colorSetNames = []; 21 | colorSets.each(function(colorSet, i) { 22 | colorSetNames.push(i); 23 | }); 24 | 25 | var activeColorSet = colorSets[colorSetNames.first]; 26 | 27 | function onMouseDown(event) { 28 | structure.onMouseDown(event); 29 | } 30 | 31 | function onMouseDrag(event) { 32 | structure.onMouseDrag(event); 33 | } 34 | 35 | function onMouseUp(event) { 36 | structure.onMouseUp(event); 37 | } 38 | 39 | var values = { 40 | maxDistance: 100 41 | } 42 | 43 | var components = { 44 | colorset: { 45 | type: 'list', label: 'Color Set', 46 | options: colorSetNames, 47 | onChange: function(value) { 48 | activeColorSet = colorSets[value]; 49 | structure.polygons.each(function(polygon) { 50 | polygon.colorize(); 51 | }); 52 | } 53 | }, 54 | maxDistance: { 55 | label: 'Maximum Distance', 56 | steppers: true, 57 | increment: 50, 58 | units: 'point' 59 | }, 60 | menuEntry: { 61 | type: 'menu-entry', 62 | value: 'Initialize With Selected', 63 | onSelect: function() { 64 | var selectedGroups = document.getItems({ 65 | type: Group, 66 | selected: true 67 | }); 68 | var structureGroup; 69 | for(var i = 0; i < selectedGroups.length; i++) { 70 | var selectedGroup = selectedGroups[i]; 71 | if(selectedGroup.name == 'Triangular') 72 | structureGroup = selectedGroup; 73 | } 74 | if(structureGroup) { 75 | structure = new Structure(structureGroup); 76 | structureGroup.selected = false; 77 | } else { 78 | Dialog.alert('Please select a Triangular group first'); 79 | } 80 | } 81 | } 82 | }; 83 | 84 | var palette = new Palette('Triangular', components, values); 85 | 86 | Structure = Base.extend({ 87 | initialize: function(group) { 88 | this.lines = []; 89 | this.lastLines = []; 90 | this.maxDistance = 20; 91 | this.polygons = {}; 92 | 93 | if(!group) { 94 | this.group = new Group(); 95 | this.group.name = 'Triangular'; 96 | this._id = Math.floor(Math.random() * 10000); 97 | this.group.data._id = this._id; 98 | this.lineGroup = new Group(); 99 | this.lineGroup.name = 'Lines'; 100 | this.lineGroup.visible = false; 101 | this.group.appendTop(this.lineGroup); 102 | 103 | this.polygonGroup = new Group(); 104 | this.polygonGroup.name = 'Polygons'; 105 | this.group.appendTop(this.polygonGroup); 106 | 107 | this.path = new Path(); 108 | this.path.fillColor = null; 109 | this.path.strokeColor = new GrayColor(1); 110 | this.path.strokeWidth = 1; 111 | this.path.name = 'data'; 112 | this.path.visible = false; 113 | this.group.appendTop(this.path); 114 | } else { 115 | this.group = group; 116 | this._id = this.group.data._id; 117 | this.lineGroup = group.children['Lines']; 118 | this.polygonGroup = group.children['Polygons']; 119 | this.path = group.children['data']; 120 | this.rebuild(); 121 | } 122 | }, 123 | 124 | onMouseDrag: function(event) { 125 | this.lastLines.each(function(line) { 126 | if(line.path) 127 | line.path.remove(); 128 | }); 129 | this.lastLines = []; 130 | if(!Key.isDown('shift')) { 131 | this.path.segments.pop(); 132 | // this.points.pop(); 133 | this.addPoint(event.point); 134 | } 135 | }, 136 | 137 | onMouseDown: function(event) { 138 | this.checkValidity(); 139 | 140 | this.lines = []; 141 | this.lineGroup.children.each(function(linePath) { 142 | this.lines.push(new Line(linePath)); 143 | }, this); 144 | this.points = []; 145 | this.path.segments.each(function(segment) { 146 | this.points.push(segment.point); 147 | }, this); 148 | 149 | this.lineGroup.selected = true; 150 | this.showPoints(); 151 | var newPoint = event.point; 152 | this.lineGroup.visible = true; 153 | var hitResult = this.lineGroup.hitTest(event.point); 154 | if(hitResult) { 155 | // if we hit an anchor, remove that point 156 | if(hitResult.type == 'anchor') { 157 | var found = false; 158 | newPoint = hitResult.point; 159 | this.removePoint(newPoint); 160 | } else { 161 | // if we hit a line, remove it first 162 | var item = hitResult.item; 163 | if(item instanceof Path && item.isParent(this.lineGroup)) { 164 | newPoint = hitResult.point; 165 | this.removeLine(new Line(item)); 166 | } 167 | } 168 | } 169 | // if shift is down don't add a point 170 | if(!Key.isDown('shift')) { 171 | this.addPoint(newPoint); 172 | } 173 | }, 174 | 175 | onMouseUp: function(event) { 176 | this.lineGroup.visible = false; 177 | this.hidePoints(); 178 | this.lineGroup.selected = false; 179 | this.addLastLines(); 180 | this.createPolygons(); 181 | }, 182 | 183 | checkValidity: function() { 184 | var valid = true; 185 | if(!this.group.isValid()) { 186 | var groups = document.getItems({ 187 | type: Group 188 | }); 189 | var triGroup; 190 | groups.each(function(group) { 191 | if(group.name && group.name == 'Triangular' && group.data._id == this._id) { 192 | triGroup = group; 193 | } 194 | }, this); 195 | 196 | this.initialize(triGroup); 197 | // print('recreating everything'); 198 | return 199 | } 200 | if(this.lineGroup.isValid()) { 201 | for(var i = 0, l = this.lines.length; i < l; i++) { 202 | var line = this.lines[i]; 203 | if(!line.getPath().isValid()) { 204 | i = l; 205 | this.rebuild(); 206 | } 207 | } 208 | } else { 209 | print('group is invalid'); 210 | } 211 | }, 212 | 213 | showPoints: function() { 214 | this.pointsPreview = new Layer(); 215 | this.pointsPreview.color = 'black'; 216 | var size = 2 / document.activeView.zoom; 217 | for(var i = 0, l = this.path.segments.length; i < l; i++) { 218 | this.pointsPreview.appendChild(new Path.Circle(this.path.segments[i].point, size)); 219 | } 220 | }, 221 | 222 | hidePoints: function() { 223 | if(this.pointsPreview && this.pointsPreview.isValid()) { 224 | this.pointsPreview.remove(); 225 | } 226 | }, 227 | 228 | rebuild: function() { 229 | this.rebuildLines(); 230 | this.rebuildPolygons(); 231 | }, 232 | 233 | rebuildLines: function() { 234 | this.lines = []; 235 | var children = this.lineGroup.children; 236 | for(var j = 0, l = children.length; j < l; j++) { 237 | var linePath = children[j]; 238 | if(linePath.name == 'line') { 239 | if(linePath.segments.length == 2) { 240 | var line = new Line(linePath); 241 | this.lines.push(line); 242 | } else { 243 | linePath.remove(); 244 | } 245 | } 246 | } 247 | }, 248 | 249 | rebuildPolygons: function() { 250 | this.polygons = {}; 251 | this.polygonGroup.children.each(function(polyGroup) { 252 | var polygon = new Polygon(polyGroup); 253 | this.polygons[polygon.getHash()] = polygon; 254 | }, this); 255 | }, 256 | 257 | createPolygons: function() { 258 | var polygonizer = new Polygonizer(); 259 | polygonizer.add(this.lines); 260 | var polys = polygonizer.getPolygons(); 261 | var path; 262 | if(polys) { 263 | for(var i = 0; i < polys.length; i++ ) { 264 | var poly = polys[i]; 265 | var polygon = new Polygon(poly); 266 | if(!polygon.exists()) { 267 | path = polygon.getPath(); 268 | this.polygonGroup.appendTop(polygon.group); 269 | path.data.exists = true; 270 | this.polygons[polygon.getHash()] = polygon; 271 | } else { 272 | path = this.polygons[polygon.getHash()].path; 273 | path.data.exists = true; 274 | } 275 | } 276 | this.removeUnusedPolygons(); 277 | this.polygons.each(function(polygon) { 278 | if(!polygon.fillColor) 279 | polygon.colorize(); 280 | }); 281 | } 282 | }, 283 | 284 | removeUnusedPolygons: function() { 285 | for(var i in this.polygons) { 286 | var polygon = this.polygons[i]; 287 | var path = polygon.path; 288 | if(path.data.exists) { 289 | path.data.exists = false; 290 | } else { 291 | polygon.group.remove(); 292 | delete this.polygons[i]; 293 | } 294 | } 295 | }, 296 | 297 | removeLine: function(toRemove) { 298 | for(var i = 0, l = this.lines.length; i < l; i++) { 299 | var line = this.lines[i]; 300 | if(line.equals(toRemove)) { 301 | this.lines.splice(i, 1); 302 | line.path && line.path.remove(); 303 | i = l; 304 | } 305 | } 306 | }, 307 | 308 | removePoint: function(toRemove) { 309 | for(var i = this.lines.length; i != 0; i--) { 310 | var line = this.lines[i - 1]; 311 | if(line.hasPoint(toRemove)) { 312 | line.path && line.path.remove(); 313 | this.lines.splice(i - 1, 1); 314 | found = true; 315 | } 316 | } 317 | if(found) { 318 | for(var i = 0, l = this.path.segments.length; i < l; i++) { 319 | var segment = this.path.segments[i]; 320 | if(segment.point == toRemove) { 321 | this.path.segments.splice(i, 1); 322 | i = l; 323 | } 324 | } 325 | } 326 | }, 327 | 328 | addLastLines: function() { 329 | this.lastLines.each(function(line) { 330 | this.lines.push(line); 331 | }, this); 332 | this.lastLines = []; 333 | }, 334 | 335 | addPoint: function(newPoint) { 336 | this.path.add(newPoint); 337 | var path = this.path; 338 | var line = new Line(newPoint, newPoint); 339 | for(var j = 0; j < path.segments.length; j++) { 340 | var segment = path.segments[j]; 341 | var point = segment.point; 342 | var delta = point - newPoint; 343 | if(delta.length > 0 && delta.length < values.maxDistance) { 344 | line.initialize(newPoint, point); 345 | var crosses = false; 346 | for(var k = 0, l = this.lines.length; k < l; k++) { 347 | var oldLine = this.lines[k]; 348 | if(oldLine.intersects(line)) { 349 | crosses = true; 350 | k = l; 351 | } 352 | } 353 | if(!crosses) { 354 | this.lastLines.push(new Line(newPoint, point)); 355 | } 356 | } 357 | } 358 | this.lastLines.each(function(line) { 359 | var path = line.getPath(); 360 | this.lineGroup.appendTop(path); 361 | }, this); 362 | } 363 | }); 364 | 365 | Polygon = Base.extend({ 366 | initialize: function(points) { 367 | this.lines = []; 368 | this.points = []; 369 | if(points instanceof Group) { 370 | this.group = points; 371 | this.path = this.group.children['polyPath']; 372 | this.fillColor = this.path.data.fillColor; 373 | for(var i = 0, l = this.path.segments.length; i < l; i++) { 374 | var segment = this.path.segments[i]; 375 | this.points.push(segment.point); 376 | } 377 | } else { 378 | this.points = []; 379 | for(var i = 0, l = points.length; i < l; i++) { 380 | if(i % 2) 381 | this.points.push(points[i]); 382 | } 383 | } 384 | for(var i = 0, l = this.points.length; i < l; i++) { 385 | var point = this.points[i]; 386 | var nextPoint = this.points[i + 1 < this.points.length ? i + 1 : 0]; 387 | this.lines.push([point, nextPoint]); 388 | } 389 | }, 390 | 391 | exists: function() { 392 | return !!structure.polygons[this.getHash()]; 393 | }, 394 | 395 | getPath: function() { 396 | this.path = new Path(); 397 | this.path.strokeColor = null; 398 | this.path.fillColor = null; 399 | this.path.closed = true; 400 | this.path.name = 'polyPath'; 401 | this.path.data.hash = this.getHash(); 402 | for(var j = 0, l = this.points.length; j < l; j++) { 403 | var point = this.points[j]; 404 | this.path.add(point); 405 | } 406 | 407 | this.group = new Group(); 408 | this.group.name = 'polygon'; 409 | this.group.appendTop(this.path); 410 | var center = this.getInCenter(); 411 | if(center) { 412 | var clone = this.path.clone(); 413 | clone.scale(0.5, center); 414 | clone.name = 'inside'; 415 | this.group.appendTop(clone); 416 | 417 | var clone = clone.clone(); 418 | clone.name = 'shadow'; 419 | var delta = clone.segments[1].point - clone.segments[0].point; 420 | delta.length = Math.random() * delta.length / 2 + (delta.length * 0.1); 421 | clone.segments[1].point = clone.segments[0].point + delta; 422 | } 423 | 424 | return this.path; 425 | }, 426 | 427 | getInCenter: function() { 428 | if(!this.center) { 429 | if(this.points.length == 3) { 430 | var lines = []; 431 | for(var i = 0, l = this.points.length; i < l; i++) { 432 | var point = this.points[i]; 433 | var prevPoint = i == 0 ? this.points[l - 1] : this.points[i - 1]; 434 | var nextPoint = i == l - 1 ? this.points[0] : this.points[i + 1]; 435 | var delta1 = nextPoint - point; 436 | var delta2 = prevPoint - point; 437 | delta2.angle = (delta1.angle + delta2.angle) / 2; 438 | lines.push(new Line(point, point + delta2, true)); 439 | } 440 | this.center = lines[0].getIntersectionPoint(lines[1]); 441 | } 442 | } 443 | return this.center; 444 | }, 445 | 446 | getHash: function() { 447 | if(!this.hash) { 448 | if(this.path) { 449 | this.hash = this.path.data.hash; 450 | } else { 451 | var sum = new Point(); 452 | for(var i = 0, l = this.points.length; i < l; i++) { 453 | sum += this.points[i]; 454 | } 455 | sum = (sum * 10000).floor(); 456 | this.hash = sum.x + '' + sum.y; 457 | } 458 | } 459 | return this.hash; 460 | }, 461 | 462 | findShared: function(mLine) { 463 | for(var i in structure.polygons) { 464 | var polygon = structure.polygons[i]; 465 | if(polygon.getHash() != this.getHash()) { 466 | var found = false; 467 | for(var j = 0; j < polygon.lines.length; j++) { 468 | var line = polygon.lines[j]; 469 | if((line[0] == mLine[0] && line[1] == mLine[1]) || (line[1] == mLine[0] && line[0] == mLine[1])) { 470 | return polygon; 471 | } 472 | } 473 | } 474 | } 475 | }, 476 | 477 | colorize: function() { 478 | var colors = {}; 479 | var intersects = false; 480 | var colorSet = activeColorSet; 481 | for(var i = 0; i < 4; i++) { 482 | colors[colorSet[i]] = { 483 | found: false 484 | } 485 | } 486 | var takenColors = []; 487 | for(var i = 0; i < this.lines.length; i++) { 488 | var sPolygon = this.findShared(this.lines[i]); 489 | if(sPolygon && sPolygon.fillColor) { 490 | if(colors[sPolygon.fillColor]) 491 | colors[sPolygon.fillColor].found = true; 492 | } 493 | } 494 | var selected = false; 495 | var colorsArray = []; 496 | for(var i in colors) { 497 | colors[i].color = i; 498 | colorsArray.push(colors[i]); 499 | } 500 | colorsArray = colorsArray.shuffle(); 501 | for(var i = 0; i < colorsArray.length; i++) { 502 | if(!colorsArray[i].found) { 503 | var color = colorsArray[i].color; 504 | this.path.fillColor = '#' + color; 505 | var index = i + 2 >= colorsArray.length ? (i + 2) % colorsArray.length : i + 2; 506 | this.group.children[0].fillColor = colorsArray[index].color; 507 | if(this.group.children.length >= 2) { 508 | var index = i + 1 == colorsArray.length ? 0 : i + 1; 509 | this.group.children[1].fillColor = colorsArray[index].color; 510 | } 511 | this.fillColor = color; 512 | this.path.data.fillColor = color; 513 | break; 514 | } 515 | } 516 | } 517 | }); 518 | 519 | Line = Base.extend({ 520 | initialize: function(point1, point2, extend) { 521 | if(point1 instanceof Path) { 522 | var path = point1; 523 | this.point1 = path.segments.first.point; 524 | this.point2 = path.segments.last.point; 525 | this.path = path; 526 | } else { 527 | this.point1 = point1; 528 | this.point2 = point2; 529 | } 530 | this.vector = this.point2 - this.point1; 531 | var shortV = this.vector.normalize(0.01); 532 | this.shortVector = this.vector - shortV; 533 | this.shortPoint1 = this.point1 - shortV; 534 | // extend controls wether the line extends beyond the defining points, 535 | // meaning point results outside the line segment are allowed. 536 | this.extend = extend; 537 | }, 538 | 539 | getPath: function() { 540 | if(!this.path) { 541 | this.path = new Path.Line(this.point1, this.point2) { 542 | strokeColor: null, 543 | fillColor: null, 544 | name: 'line' 545 | }; 546 | } 547 | return this.path; 548 | }, 549 | 550 | intersects: function(line) { 551 | var v1 = this.shortVector; 552 | var v2 = line.shortVector; 553 | var cross = v1.cross(v2); 554 | // Epsilon tolerance 555 | if (Math.abs(cross) <= 10e-6) 556 | return null; 557 | var v = line.shortPoint1 - this.shortPoint1; 558 | var t1 = v.cross(v2) / cross; 559 | var t2 = v.cross(v1) / cross; 560 | // Check the ranges of t parameters if the line is not allowed to 561 | // extend beyond the definition points. 562 | if ((this.extend || 0 <= t1 && t1 <= 1) 563 | && (line.extend || 0 <= t2 && t2 <= 1)) 564 | return true; 565 | return null; 566 | }, 567 | 568 | getIntersectionPoint: function(line) { 569 | var v1 = this.shortVector; 570 | var v2 = line.shortVector; 571 | var cross = v1.cross(v2); 572 | // Epsilon tolerance 573 | if (Math.abs(cross) <= 10e-6) 574 | return null; 575 | var v = line.shortPoint1 - this.shortPoint1; 576 | var t1 = v.cross(v2) / cross; 577 | var t2 = v.cross(v1) / cross; 578 | // Check the ranges of t parameters if the line is not allowed to 579 | // extend beyond the definition points. 580 | if ((this.extend || 0 <= t1 && t1 <= 1) 581 | && (line.extend || 0 <= t2 && t2 <= 1)) 582 | return this.point1 + v1 * t1; 583 | return null; 584 | }, 585 | 586 | getSide: function(p) { 587 | var v1 = this.point2 - this.point1; 588 | var v2 = p - this.point1; 589 | var ccw = v2.cross(v1); 590 | if (ccw == 0.0) { 591 | ccw = v2.dot(v1); 592 | if (ccw > 0.0) { 593 | ccw = (v2 - v1).dot(v1); 594 | if (ccw < 0.0) 595 | ccw = 0.0; 596 | } 597 | } 598 | return ccw < 0.0 ? -1 : ccw > 0.0 ? 1 : 0; 599 | }, 600 | 601 | getVector: function() { 602 | return this.point2 - this.point1; 603 | }, 604 | 605 | equals: function(line) { 606 | return this.point1 == line.point1 && this.point2 == line.point2; 607 | }, 608 | 609 | sharesPoint: function(line) { 610 | return line.hasPoint(this.point1) || line.hasPoint(this.point2); 611 | }, 612 | 613 | hasPoint: function(point) { 614 | return this.point1 == point || this.point2 == point; 615 | }, 616 | 617 | toString: function() { 618 | return 'Line: point1: ' + this.point1 + ', point2' + this.point2; 619 | } 620 | }); 621 | 622 | /* 623 | * Adapted to Javascript from the JTS Topology Suite by Jonathan Puckey 2010 624 | * 625 | * This library is free software; you can redistribute it and/or 626 | * modify it under the terms of the GNU Lesser General Public 627 | * License as published by the Free Software Foundation; either 628 | * version 2.1 of the License, or (at your option) any later version. 629 | * 630 | * This library is distributed in the hope that it will be useful, 631 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 632 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 633 | * Lesser General Public License for more details. 634 | * 635 | * You should have received a copy of the GNU Lesser General Public 636 | * License along with this library; if not, write to the Free Software 637 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 638 | * 639 | * For more information, contact: 640 | * 641 | * Vivid Solutions 642 | * Suite #1A 643 | * 2328 Government Street 644 | * Victoria BC V8T 5G5 645 | * Canada 646 | * 647 | * (250)385-6040 648 | * www.vividsolutions.com 649 | */ 650 | 651 | Polygonizer = Base.extend({ 652 | initialize: function() { 653 | this.dangles = []; 654 | this.cutEdges = []; 655 | this.invalidRingLines = []; 656 | this.holeList = null; 657 | this.shellList = null; 658 | this.polyList = null; 659 | }, 660 | 661 | add: function(lines) { 662 | lines.each(function(line) { 663 | if(!this.graph) 664 | this.graph = new PolygonizeGraph(); 665 | this.graph.addEdge(line); 666 | }, this); 667 | }, 668 | 669 | getPolygons: function() { 670 | this.polygonize(); 671 | return this.polyList; 672 | }, 673 | 674 | polygonize: function() { 675 | if(this.polyList != null) return; 676 | this.polyList = []; 677 | if(!this.graph) return; 678 | 679 | this.dangles = this.graph.deleteDangles(); 680 | this.cutEdges = this.graph.deleteCutEdges(); 681 | this.edgeRingList = this.graph.getEdgeRings(); 682 | this.validEdgeRingList = []; 683 | this.invalidRingList = []; 684 | // this.edgeRingList = []; 685 | this.findValidRings(); 686 | this.findShellsAndHoles(); 687 | //this.assignHolesToShells(this.holeList, this.shellList); 688 | this.polyList = []; 689 | for(var i = 0, l = this.shellList.length; i < l; i++) { 690 | var er = this.shellList[i]; 691 | var points = er.getCoordinates(); 692 | this.polyList.push(points); 693 | } 694 | }, 695 | 696 | assignHolesToShells: function(holeList, shellList) { 697 | for(var i = 0, l = holeList.length; i < l; i++) { 698 | var holeER = holeList[i]; 699 | this.assignHoleToShell(holeER, shellList); 700 | } 701 | }, 702 | 703 | assignHoleToShell: function(holeER, shellList) { 704 | var shell = EdgeRing.findEdgeRingContaining(holeER, shellList); 705 | if(shell != null) 706 | shell.addHole(holeER.getRing()); 707 | }, 708 | 709 | findValidRings: function() { 710 | for(var i = 0, l = this.edgeRingList.length; i < l; i++) { 711 | var er = this.edgeRingList[i]; 712 | if(er.isValid()) { 713 | this.validEdgeRingList.push(er); 714 | } else { 715 | this.invalidRingList.push(er); 716 | } 717 | } 718 | }, 719 | 720 | findShellsAndHoles: function() { 721 | this.holeList = []; 722 | this.shellList = []; 723 | for(var i = 0, l = this.edgeRingList.length; i < l; i++) { 724 | var er = this.edgeRingList[i]; 725 | if(er.isHole()) { 726 | this.holeList.push(er); 727 | } else { 728 | this.shellList.push(er); 729 | } 730 | } 731 | }, 732 | }); 733 | 734 | /** 735 | * Represents a planar graph of edges that can be used to compute a 736 | * polygonization, and implements the algorithms to compute the 737 | * {@link EdgeRings} formed by the graph. 738 | *

739 | * The marked flag on {@link DirectedEdge}s is used to indicate that a directed edge 740 | * has be logically deleted from the graph. 741 | * 742 | * @version 1.7 743 | */ 744 | PolygonizeGraph = Base.extend({ 745 | initialize: function() { 746 | this.edges = []; 747 | this.dirEdges = []; 748 | this.nodes = {}; 749 | }, 750 | 751 | addEdge: function(line) { 752 | if(line.point1 != line.point2) { 753 | var startPt = line.point1; 754 | var endPt = line.point2; 755 | 756 | var nStart = this.getNode(startPt); 757 | var nEnd = this.getNode(endPt); 758 | 759 | var de0 = new PolygonizeDirectedEdge(nStart, nEnd, line.point2, true); 760 | var de1 = new PolygonizeDirectedEdge(nEnd, nStart, line.point1, false); 761 | var edge = new PolygonizeEdge(line); 762 | edge.setDirectedEdges(de0, de1); 763 | this.add(edge); 764 | } 765 | //var linePts = CoordinateArrays.removeRepeatedPoints(line.getCoordinates()); 766 | }, 767 | 768 | deleteDangles: function() { 769 | // nodes to remove: 770 | var nodeStack = this.findNodesOfDegree(1); 771 | var dangleLines = []; 772 | while(nodeStack.length) { 773 | // for(var i = 0, l = nodesToRemove.length; i < l; i++) { 774 | var node = nodeStack.pop(); 775 | this.deleteAllEdges(node); 776 | var nodeOutEdges = node.getOutEdges().getEdges(); 777 | for(var j = 0, l = nodeOutEdges.length; j < l; j++) { 778 | var de = nodeOutEdges[j]; 779 | de.marked = true; 780 | var sym = de.getSym(); 781 | if(sym) 782 | sym.marked = true; 783 | 784 | // save the line as a dangle 785 | var e = de.getEdge(); 786 | dangleLines.push(e); 787 | var toNode = de.to; 788 | // add the toNode to the list to be processed, if it is now a dangle 789 | if (this.getDegreeNonDeleted(toNode) == 1) 790 | nodeStack.push(toNode); 791 | } 792 | } 793 | return dangleLines; 794 | }, 795 | 796 | deleteCutEdges: function() { 797 | this.computeNextCWEdges(); 798 | // label the current set of edgerings 799 | this.findLabeledEdgeRings(this.dirEdges); 800 | /** 801 | * Cut Edges are edges where both dirEdges have the same label. 802 | * Delete them, and record them 803 | */ 804 | var cutLines = []; 805 | for(var i = 0, l = this.dirEdges.length; i < l; i++) { 806 | var de = this.dirEdges[i]; 807 | if(de.marked) continue; 808 | var sym = de.getSym(); 809 | if(de.label == sym.label) { 810 | de.marked = true; 811 | sym.marked = true; 812 | 813 | // save the line as a cut edge 814 | var e = de.getEdge(); 815 | cutLines.push(e.line); 816 | } 817 | } 818 | return cutLines; 819 | }, 820 | 821 | computeNextCWEdges: function() { 822 | this.nodes.each(function(node) { 823 | var deStar = node.getOutEdges(); 824 | var startDE = null; 825 | var prevDE = null; 826 | var edges = deStar.getEdges(); 827 | for(var i = 0, l = edges.length; i < l; i++) { 828 | var outDE = edges[i]; 829 | if(outDE.marked) continue; 830 | if(startDE == null) 831 | startDE = outDE; 832 | if(prevDE != null) { 833 | var sym = prevDE.getSym(); 834 | sym.next = outDE; 835 | } 836 | prevDE = outDE 837 | } 838 | if(prevDE != null) { 839 | var sym = prevDE.getSym(); 840 | sym.next = startDE; 841 | } 842 | }, this); 843 | }, 844 | 845 | /** 846 | * Computes the EdgeRings formed by the edges in this graph. 847 | * @return a list of the {@link EdgeRing}s found by the polygonization process. 848 | */ 849 | getEdgeRings: function() { 850 | // maybe could optimize this, since most of these pointers should be set correctly already 851 | // by deleteCutEdges() 852 | this.computeNextCWEdges(); 853 | // clear labels of all edges in graph 854 | this.label(this.dirEdges, -1); 855 | var maximalRings = this.findLabeledEdgeRings(this.dirEdges); 856 | this.convertMaximalToMinimalEdgeRings(maximalRings); 857 | var edgeRingList = []; 858 | for(var i = 0, l = this.dirEdges.length; i < l; i++) { 859 | var de = this.dirEdges[i]; 860 | if(de.marked) continue; 861 | if(de.isInRing()) continue 862 | var er = this.findEdgeRing(de); 863 | edgeRingList.push(er); 864 | } 865 | return edgeRingList; 866 | }, 867 | 868 | label: function(dirEdges, label) { 869 | for(var i = 0, l = dirEdges.length; i < l; i++) 870 | dirEdges[i].label = label; 871 | }, 872 | 873 | findEdgeRing: function(startDE) { 874 | var de = startDE; 875 | er = new EdgeRing(); 876 | do { 877 | er.add(de); 878 | de.setRing(er); 879 | de = de.next; 880 | } while (de != startDE); 881 | return er; 882 | }, 883 | 884 | findLabeledEdgeRings: function(dirEdges) { 885 | var edgeRingStarts = []; 886 | var currLabel = 1; 887 | for(var i = 0, l = dirEdges.length; i < l; i++) { 888 | var de = dirEdges[i]; 889 | if(de.marked) continue; 890 | if(de.label >= 0) continue; 891 | 892 | edgeRingStarts.push(de); 893 | var edges = this.findDirEdgesInRing(de); 894 | this.label(edges, currLabel); 895 | currLabel++; 896 | } 897 | return edgeRingStarts; 898 | }, 899 | 900 | findDirEdgesInRing: function(startDE) { 901 | var de = startDE; 902 | var edges = []; 903 | do { 904 | edges.push(de); 905 | de = de.next; 906 | } while (de != startDE); 907 | return edges; 908 | }, 909 | 910 | convertMaximalToMinimalEdgeRings: function(ringEdges) { 911 | for(var i = 0, l = ringEdges.length; i < l; i++) { 912 | var de = ringEdges[i]; 913 | var label = de.label; 914 | var intNodes = this.findIntersectionNodes(de, label); 915 | if(intNodes == null) continue; 916 | // flip the next pointers on the intersection nodes to create minimal edge rings 917 | for(var j = 0, l = intNodes.length; j < l; j++) { 918 | var node = intNodes[j]; 919 | this.computeNextCCWEdges(node, label); 920 | } 921 | } 922 | }, 923 | 924 | getDegree: function(node, label) { 925 | var edges = node.getOutEdges().getEdges(); 926 | var degree = 0; 927 | for(var i = 0, l = edges.length; i < l; i++) { 928 | var de = edges[i]; 929 | if(de.label == label) degree++; 930 | } 931 | return degree; 932 | }, 933 | 934 | findIntersectionNodes: function(startDE, label) { 935 | var intNodes = null; 936 | var de = startDE; 937 | do { 938 | var node = de.from; 939 | if(this.getDegree(node, label) > 1) { 940 | if(intNodes == null) 941 | intNodes = []; 942 | intNodes.push(node); 943 | } 944 | 945 | de = de.next; 946 | // if(!de) { 947 | // print('found null DE in ring'); 948 | // } 949 | // if(de == startDE || !de.isInRing()) { 950 | // print('found DE already in ring'); 951 | // } 952 | } while (de != startDE); 953 | 954 | return intNodes; 955 | }, 956 | 957 | /** 958 | * Computes the next edge pointers going CCW around the given node, for the 959 | * given edgering label. 960 | * This algorithm has the effect of converting maximal edgerings into minimal edgerings 961 | */ 962 | computeNextCCWEdges: function(node, label) { 963 | var deStar = node.getOutEdges(); 964 | var firstOutDE = null; 965 | var prevInDE = null 966 | 967 | // the edges are stored in CCW order around the star 968 | var edges = deStar.getEdges(); 969 | for(var i = edges.length - 1; i >= 0; i--) { 970 | var de = edges[i]; 971 | var sym = de.getSym(); 972 | 973 | var outDE = null; 974 | if(de.label == label) outDE = de; 975 | var inDE = null; 976 | if(sym.label == label) inDE = sym; 977 | if(outDE == null && inDE == null) continue; // this edge is not in edgering 978 | if(inDE != null) 979 | prevInDE = inDE; 980 | if(outDE != null) { 981 | if(prevInDE != null) { 982 | prevInDE.next = outDE; 983 | prevInDE = null 984 | } 985 | if(firstOutDE == null) 986 | firstOutDE = outDE; 987 | } 988 | } 989 | if(prevInDE != null) { 990 | prevInDE.next = firstOutDE; 991 | } 992 | }, 993 | 994 | getDegreeNonDeleted: function(node) { 995 | var edges = node.getOutEdges().getEdges(); 996 | var degree = 0; 997 | edges.each(function(de) { 998 | if(!de.marked) degree++; 999 | }); 1000 | return degree; 1001 | }, 1002 | 1003 | findNodesOfDegree: function(degree) { 1004 | var nodesFound = []; 1005 | this.nodes.each(function(node) { 1006 | if(node.getDegree() == degree) 1007 | nodesFound.push(node); 1008 | }); 1009 | return nodesFound; 1010 | }, 1011 | 1012 | deleteAllEdges: function(node) { 1013 | var edges = node.getOutEdges().getEdges(); 1014 | for(var i = 0, l = edges.length; i < l; i++) { 1015 | var de = edges[i]; 1016 | de.marked = true; 1017 | var sym = de.getSym(); 1018 | if(sym) 1019 | sym.marked = true; 1020 | } 1021 | }, 1022 | 1023 | /** 1024 | * Adds the Edge and its DirectedEdges with this PlanarGraph. 1025 | * Assumes that the Edge has already been created with its associated DirectEdges. 1026 | * Only subclasses can add Edges, to ensure the edges added are of the right class. 1027 | */ 1028 | add: function(edge) { 1029 | this.edges.push(edge); 1030 | this.dirEdges.push(edge.getDirEdge(0)); 1031 | this.dirEdges.push(edge.getDirEdge(1)); 1032 | }, 1033 | 1034 | getNode: function(point) { 1035 | var node = this.nodes[point.toString()]; 1036 | if(!node) 1037 | node = this.nodes[point.toString()] = new Node(point); 1038 | return node; 1039 | } 1040 | }); 1041 | 1042 | EdgeRing = Base.extend({ 1043 | initialize: function() { 1044 | this.deList = []; 1045 | this.ringPts = null; 1046 | }, 1047 | add: function(de) { 1048 | this.deList.push(de); 1049 | }, 1050 | isValid: function() { 1051 | this.getCoordinates(); 1052 | if(this.ringPts.length <= 3) return false; 1053 | return true; 1054 | this.getRing(); 1055 | return this.ring.isValid(); 1056 | }, 1057 | getCoordinates: function() { 1058 | if(!this.ringPts) { 1059 | var coordList = []; 1060 | for(var i = 0, l = this.deList.length; i < l; i++) { 1061 | var de = this.deList[i]; 1062 | var edge = de.getEdge(); 1063 | var line = edge.line; 1064 | coordList = this.addEdge([line.point1, line.point2], de.edgeDirection, coordList); 1065 | } 1066 | this.ringPts = coordList; 1067 | } 1068 | return this.ringPts; 1069 | }, 1070 | addEdge: function(coords, isForward, coordList) { 1071 | if(isForward) { 1072 | for(var i = 0; i < coords.length; i++) { 1073 | coordList.push(coords[i]); 1074 | } 1075 | } else { 1076 | for(var i = coords.length - 1; i >= 0; i--) { 1077 | coordList.push(coords[i]); 1078 | } 1079 | } 1080 | return coordList; 1081 | }, 1082 | addHole: function(hole) { 1083 | if(!this.holes) 1084 | this.holes = []; 1085 | this.holes.push(hole); 1086 | }, 1087 | getRing: function() { 1088 | if(this.ring) return this.ring; 1089 | this.getCoordinates(); 1090 | return this.ringPts; 1091 | }, 1092 | isHole: function() { 1093 | var ring = this.getRing(); 1094 | return CGAlgorithms.isCCW(ring); 1095 | }, 1096 | statics: { 1097 | findEdgeRingContaining: function(testEr, shellList) { 1098 | var testRing = testEr.getRing(); 1099 | var testEnv = testRing.getEnvelopeInternal(); 1100 | var testPt = testRing.getCoordinateN(0); 1101 | 1102 | var minShell = null; 1103 | var minEnv = null; 1104 | for(var i = 0, l = shellList.length; i < l; i++) { 1105 | var tryShell = shellList[i]; 1106 | var tryRing = tryShell.getRing(); 1107 | var tryEnv = tryRing.getEnvelopeInternal(); 1108 | if(minShell != null) minEnv = minShell.getRing().getEnvelopeInternal(); 1109 | var isContained = false; 1110 | if(tryEnv == testEnv) 1111 | continue; 1112 | var testPt = CoordinateArrays.ptNotInList(testRing.getCoordinates(), tryRing.getCoordinates()); 1113 | var isContained = tryEnv.contains(testEnv) && cga.isPointInRing(testPt, tryRing.getCoordinates()); 1114 | if(isContained) { 1115 | if(minShell = null || minEnv.contains(tryEnv)) { 1116 | minShell = tryShell; 1117 | } 1118 | } 1119 | } 1120 | return minShell; 1121 | } 1122 | } 1123 | }); 1124 | 1125 | Edge = Base.extend({ 1126 | /** 1127 | * Initializes this Edge's two DirectedEdges, and for each DirectedEdge: sets the 1128 | * Edge, sets the symmetric DirectedEdge, and adds this Edge to its from-Node. 1129 | */ 1130 | setDirectedEdges: function(de0, de1) { 1131 | this.dirEdge = [de0, de1]; 1132 | de0.setEdge(this); 1133 | de1.setEdge(this); 1134 | de0.setSym(de1); 1135 | de1.setSym(de0); 1136 | de0.from.addOutEdge(de0); 1137 | de1.from.addOutEdge(de1); 1138 | }, 1139 | getDirEdge: function(i) { 1140 | return this.dirEdge[i]; 1141 | } 1142 | }); 1143 | 1144 | PolygonizeEdge = Edge.extend({ 1145 | initialize: function(line) { 1146 | this.line = line; 1147 | } 1148 | }); 1149 | 1150 | PolygonizeDirectedEdge = Edge.extend({ 1151 | initialize: function(from, to, directionPoint, edgeDirection) { 1152 | this.from = from; 1153 | this.to = to; 1154 | this.edgeDirection = edgeDirection; 1155 | this.p0 = from.point; 1156 | this.p1 = directionPoint; 1157 | // var dx = this.p1.x - this.p0.x; 1158 | // var dy = this.p1.y - this.p0.y; 1159 | var delta = this.p1 - this.p0; 1160 | this.quadrant = Quadrant.quadrant(delta); 1161 | this.angle = Math.atan2(delta.y, delta.x); 1162 | }, 1163 | setEdge: function(parentEdge) { 1164 | this.parentEdge = parentEdge; 1165 | }, 1166 | getEdge: function() { 1167 | return this.parentEdge; 1168 | }, 1169 | /** 1170 | * Sets this DirectedEdge's symmetric DirectedEdge, which runs in the opposite 1171 | * direction. 1172 | */ 1173 | setSym: function(sym) { 1174 | this.sym = sym; 1175 | }, 1176 | getSym: function() { 1177 | return this.sym; 1178 | }, 1179 | 1180 | compareDirection: function(e) { 1181 | if(this.quadrant > e.quadrant) return 1; 1182 | if(this.quadrant < e.quadrant) return -1; 1183 | // vectors are in the same quadrant - check relative orientation of direction vectors 1184 | // this is > e if it is CCW of e 1185 | return CGAlgorithms.computeOrientation(e.p0, e.p1, this.p1); 1186 | }, 1187 | 1188 | setRing: function(edgeRing) { 1189 | this.edgeRing = edgeRing; 1190 | }, 1191 | 1192 | isInRing: function() { 1193 | return !!this.edgeRing 1194 | } 1195 | }); 1196 | 1197 | Quadrant = Base.extend({ 1198 | statics: { 1199 | quadrant: function(p) { 1200 | if(p.x == 0 && p.y == 0) { 1201 | print('Cannot compute the quadrant for point' + p); 1202 | return; 1203 | } else if(p.x >= 0) { 1204 | return p.y >=0 ? 0 : 3; 1205 | } else { 1206 | return p.y >= 0 ? 1 : 2; 1207 | } 1208 | } 1209 | } 1210 | }); 1211 | 1212 | Node = Base.extend({ 1213 | initialize: function(pt) { 1214 | this.point = pt; 1215 | this.deStar = new DirectedEdgeStar(); 1216 | }, 1217 | addOutEdge: function(de) { 1218 | this.deStar.add(de); 1219 | }, 1220 | 1221 | getDegree: function() { 1222 | return this.deStar.getDegree(); 1223 | }, 1224 | 1225 | getOutEdges: function() { 1226 | return this.deStar; 1227 | } 1228 | }); 1229 | 1230 | /** 1231 | * A sorted collection of {@link DirectedEdge}s which leave a {@link Node} 1232 | * in a {@link PlanarGraph}. 1233 | * 1234 | * @version 1.7 1235 | */ 1236 | DirectedEdgeStar = Base.extend({ 1237 | initialize: function() { 1238 | this.outEdges = []; 1239 | this.storted = false; 1240 | }, 1241 | add: function(directedEdge) { 1242 | this.outEdges.push(directedEdge); 1243 | this.sorted = false; 1244 | }, 1245 | getDegree: function() { 1246 | return this.outEdges.length; 1247 | }, 1248 | getEdges: function() { 1249 | this.sortEdges(); 1250 | return this.outEdges; 1251 | }, 1252 | sortEdges: function() { 1253 | if(!this.sorted) { 1254 | this.outEdges.sort(function(a, b){ 1255 | return a.compareDirection(b); 1256 | }); 1257 | } 1258 | this.sorted = true; 1259 | } 1260 | }); 1261 | 1262 | CGAlgorithms = { 1263 | computeOrientation: function(p1, p2, q) { 1264 | return CGAlgorithms.orientationIndex(p1, p2, q); 1265 | }, 1266 | orientationIndex: function(p1, p2, q) { 1267 | // travelling along p1->p2, turn counter clockwise to get to q return 1, 1268 | // travelling along p1->p2, turn clockwise to get to q return -1, 1269 | // p1, p2 and q are colinear return 0. 1270 | var dx1 = p2.x - p1.x; 1271 | var dy1 = p2.y - p1.y; 1272 | var dx2 = q.x - p2.x; 1273 | var dy2 = q.y - p2.y; 1274 | return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2); 1275 | }, 1276 | isCCW: function(ring) { 1277 | var nPts = ring.length - 1; 1278 | var hiPt = ring[0]; 1279 | var hiIndex = 0; 1280 | for(var i = 1; i <= nPts; i++) { 1281 | var p = ring[i]; 1282 | if(p.y > hiPt.y) { 1283 | hiPt = p; 1284 | hiIndex = i; 1285 | } 1286 | } 1287 | 1288 | //find distinct point before highest point 1289 | var iPrev = hiIndex; 1290 | do { 1291 | iPrev = iPrev - 1; 1292 | if(iPrev < 0) iPrev = nPts; 1293 | } while (ring[iPrev] == hiPt && iPrev != hiIndex); 1294 | 1295 | //find distinct point after highest point 1296 | var iNext = hiIndex; 1297 | do { 1298 | iNext = (iNext + 1) % nPts; 1299 | } while (ring[iNext] == hiPt && iNext != hiIndex); 1300 | 1301 | var prev = ring[iPrev]; 1302 | var next = ring[iNext]; 1303 | 1304 | /** 1305 | * This check catches cases where the ring contains an A-B-A configuration of points. 1306 | * This can happen if the ring does not contain 3 distinct points 1307 | * (including the case where the input array has fewer than 4 elements), 1308 | * or it contains coincident line segments. 1309 | */ 1310 | if(prev == hiPt || next == hiPt || prev == next) 1311 | return false; 1312 | var disc = CGAlgorithms.computeOrientation(prev, hiPt, next); 1313 | 1314 | /** 1315 | * If disc is exactly 0, lines are collinear. There are two possible cases: 1316 | * (1) the lines lie along the x axis in opposite directions 1317 | * (2) the lines lie on top of one another 1318 | * 1319 | * (1) is handled by checking if next is left of prev ==> CCW 1320 | * (2) will never happen if the ring is valid, so don't check for it 1321 | * (Might want to assert this) 1322 | */ 1323 | var isCCW = false; 1324 | if(disc == 0) { 1325 | // poly is CCW if prev x is right of next x 1326 | isCCW = prev.x > next.x; 1327 | } else { 1328 | // if area is positive, points are ordered CCW 1329 | isCCW = (disc > 0); 1330 | } 1331 | return isCCW; 1332 | } 1333 | }; 1334 | 1335 | RobustDeterminant = { 1336 | signOfDet2x2: function(x1, y1, x2, y2) { 1337 | // returns -1 if the determinant is negative, 1338 | // returns 1 if the determinant is positive, 1339 | // retunrs 0 if the determinant is null. 1340 | var sign, swap, k, count = 0; 1341 | 1342 | //callCount++; // debugging only 1343 | 1344 | sign = 1; 1345 | 1346 | /* 1347 | * testing null entries 1348 | */ 1349 | if ((x1 == 0) || (y2 == 0)) { 1350 | if ((y1 == 0) || (x2 == 0)) { 1351 | return 0; 1352 | } 1353 | else if (y1 > 0) { 1354 | if (x2 > 0) { 1355 | return -sign; 1356 | } 1357 | else { 1358 | return sign; 1359 | } 1360 | } 1361 | else { 1362 | if (x2 > 0) { 1363 | return sign; 1364 | } 1365 | else { 1366 | return -sign; 1367 | } 1368 | } 1369 | } 1370 | if ((y1 == 0) || (x2 == 0)) { 1371 | if (y2 > 0) { 1372 | if (x1 > 0) { 1373 | return sign; 1374 | } 1375 | else { 1376 | return -sign; 1377 | } 1378 | } 1379 | else { 1380 | if (x1 > 0) { 1381 | return -sign; 1382 | } 1383 | else { 1384 | return sign; 1385 | } 1386 | } 1387 | } 1388 | 1389 | /* 1390 | * making y coordinates positive and permuting the entries 1391 | */ 1392 | /* 1393 | * so that y2 is the biggest one 1394 | */ 1395 | if (0 < y1) { 1396 | if (0 < y2) { 1397 | if (y1 <= y2) { 1398 | ; 1399 | } 1400 | else { 1401 | sign = -sign; 1402 | swap = x1; 1403 | x1 = x2; 1404 | x2 = swap; 1405 | swap = y1; 1406 | y1 = y2; 1407 | y2 = swap; 1408 | } 1409 | } 1410 | else { 1411 | if (y1 <= -y2) { 1412 | sign = -sign; 1413 | x2 = -x2; 1414 | y2 = -y2; 1415 | } 1416 | else { 1417 | swap = x1; 1418 | x1 = -x2; 1419 | x2 = swap; 1420 | swap = y1; 1421 | y1 = -y2; 1422 | y2 = swap; 1423 | } 1424 | } 1425 | } 1426 | else { 1427 | if (0 < y2) { 1428 | if (-y1 <= y2) { 1429 | sign = -sign; 1430 | x1 = -x1; 1431 | y1 = -y1; 1432 | } 1433 | else { 1434 | swap = -x1; 1435 | x1 = x2; 1436 | x2 = swap; 1437 | swap = -y1; 1438 | y1 = y2; 1439 | y2 = swap; 1440 | } 1441 | } 1442 | else { 1443 | if (y1 >= y2) { 1444 | x1 = -x1; 1445 | y1 = -y1; 1446 | x2 = -x2; 1447 | y2 = -y2; 1448 | ; 1449 | } 1450 | else { 1451 | sign = -sign; 1452 | swap = -x1; 1453 | x1 = -x2; 1454 | x2 = swap; 1455 | swap = -y1; 1456 | y1 = -y2; 1457 | y2 = swap; 1458 | } 1459 | } 1460 | } 1461 | 1462 | /* 1463 | * making x coordinates positive 1464 | */ 1465 | /* 1466 | * if |x2| < |x1| one can conclude 1467 | */ 1468 | if (0 < x1) { 1469 | if (0 < x2) { 1470 | if (x1 <= x2) { 1471 | ; 1472 | } 1473 | else { 1474 | return sign; 1475 | } 1476 | } 1477 | else { 1478 | return sign; 1479 | } 1480 | } 1481 | else { 1482 | if (0 < x2) { 1483 | return -sign; 1484 | } 1485 | else { 1486 | if (x1 >= x2) { 1487 | sign = -sign; 1488 | x1 = -x1; 1489 | x2 = -x2; 1490 | ; 1491 | } 1492 | else { 1493 | return -sign; 1494 | } 1495 | } 1496 | } 1497 | 1498 | /* 1499 | * all entries strictly positive x1 <= x2 and y1 <= y2 1500 | */ 1501 | while (true) { 1502 | count = count + 1; 1503 | k = Math.floor(x2 / x1); 1504 | x2 = x2 - k * x1; 1505 | y2 = y2 - k * y1; 1506 | 1507 | /* 1508 | * testing if R (new U2) is in U1 rectangle 1509 | */ 1510 | if (y2 < 0) { 1511 | return -sign; 1512 | } 1513 | if (y2 > y1) { 1514 | return sign; 1515 | } 1516 | 1517 | /* 1518 | * finding R' 1519 | */ 1520 | if (x1 > x2 + x2) { 1521 | if (y1 < y2 + y2) { 1522 | return sign; 1523 | } 1524 | } 1525 | else { 1526 | if (y1 > y2 + y2) { 1527 | return -sign; 1528 | } 1529 | else { 1530 | x2 = x1 - x2; 1531 | y2 = y1 - y2; 1532 | sign = -sign; 1533 | } 1534 | } 1535 | if (y2 == 0) { 1536 | if (x2 == 0) { 1537 | return 0; 1538 | } 1539 | else { 1540 | return -sign; 1541 | } 1542 | } 1543 | if (x2 == 0) { 1544 | return sign; 1545 | } 1546 | 1547 | /* 1548 | * exchange 1 and 2 role. 1549 | */ 1550 | k = Math.floor(x1 / x2); 1551 | x1 = x1 - k * x2; 1552 | y1 = y1 - k * y2; 1553 | 1554 | /* 1555 | * testing if R (new U1) is in U2 rectangle 1556 | */ 1557 | if (y1 < 0) { 1558 | return sign; 1559 | } 1560 | if (y1 > y2) { 1561 | return -sign; 1562 | } 1563 | 1564 | /* 1565 | * finding R' 1566 | */ 1567 | if (x2 > x1 + x1) { 1568 | if (y2 < y1 + y1) { 1569 | return -sign; 1570 | } 1571 | } 1572 | else { 1573 | if (y2 > y1 + y1) { 1574 | return sign; 1575 | } 1576 | else { 1577 | x1 = x2 - x1; 1578 | y1 = y2 - y1; 1579 | sign = -sign; 1580 | } 1581 | } 1582 | if (y1 == 0) { 1583 | if (x1 == 0) { 1584 | return 0; 1585 | } 1586 | else { 1587 | return sign; 1588 | } 1589 | } 1590 | if (x1 == 0) { 1591 | return -sign; 1592 | } 1593 | } 1594 | 1595 | } 1596 | } 1597 | 1598 | var structure = new Structure(); 1599 | -------------------------------------------------------------------------------- /Triangular2.js: -------------------------------------------------------------------------------- 1 | // Known bugs: 2 | // Removing lines when there are only one or two triangles removes the polygon group. 3 | 4 | var colorSets = { 5 | 'Icy': ['E1F2F7', '89BECE', '4F8B98', '0A3F47'], 6 | 'Citrus': ['FFDA17', 'EC6227', '6D1F08', '6D1F08'], 7 | 'White to Black': ['FBFBFB', 'ADADAD', '39393A', '000000'], 8 | 'Red Blue Yellow Purple': ['E30613', '602483', 'B5DEE4', 'FFE600'], 9 | 'Eye Candy': ['098e7b', 'fdb606', 'd40144', 'fb4810'], 10 | 'Fourties Lipstick': ['0d061c', 'be1900', '1e1c2c', '720a00'], 11 | 'Colorful Mess': ['0fe1fa', 'f0ff00', '009bf7', 'ff4001'], 12 | 'Plum': ['786262', 'b8988b', 'ebc8ad', '434a4c'], 13 | 'Green': ['044c29', '45bf55', '167f39', '96ed89'], 14 | 'Purple Cream': ['fee293', '380c2a', 'a6243d', 'e85400'], 15 | 'Orange Brown': ['520026', '822a21', 'fc9851', 'c2410a'], 16 | 'Green Gold': ['a99000', 'fffb8d', '298737', '005737'], 17 | 'Revolution': ['5f020d', '9c0413', 'd9cda9', '0d0000'] 18 | }; 19 | 20 | var colorSetNames = []; 21 | colorSets.each(function(colorSet, i) { 22 | colorSetNames.push(i); 23 | }); 24 | 25 | var activeColorSet = colorSets[colorSetNames.first]; 26 | 27 | function onMouseDown(event) { 28 | structure.onMouseDown(event); 29 | } 30 | 31 | function onMouseDrag(event) { 32 | structure.onMouseDrag(event); 33 | } 34 | 35 | function onMouseUp(event) { 36 | structure.onMouseUp(event); 37 | } 38 | 39 | var values = { 40 | maxDistance: 100 41 | } 42 | 43 | var components = { 44 | /* 45 | colorset: { 46 | type: 'list', label: 'Color Set', 47 | options: colorSetNames, 48 | onChange: function(value) { 49 | activeColorSet = colorSets[value]; 50 | structure.polygons.each(function(polygon) { 51 | polygon.colorize(); 52 | }); 53 | } 54 | }, 55 | */ 56 | maxDistance: { 57 | label: 'Maximum Distance', 58 | steppers: true, 59 | increment: 50, 60 | units: 'point' 61 | }, 62 | menuEntry: { 63 | type: 'menu-entry', 64 | value: 'Initialize With Selected', 65 | onSelect: function() { 66 | var selectedGroups = document.getItems({ 67 | type: Group, 68 | selected: true 69 | }); 70 | var structureGroup; 71 | for(var i = 0; i < selectedGroups.length; i++) { 72 | var selectedGroup = selectedGroups[i]; 73 | if(selectedGroup.name == 'Triangular') 74 | structureGroup = selectedGroup; 75 | } 76 | if(structureGroup) { 77 | structure = new Structure(structureGroup); 78 | structureGroup.selected = false; 79 | } else { 80 | Dialog.alert('Please select a Triangular group first'); 81 | } 82 | } 83 | } 84 | }; 85 | 86 | var palette = new Palette('Triangular', components, values); 87 | 88 | Structure = Base.extend({ 89 | initialize: function(group) { 90 | this.lines = []; 91 | this.lastLines = []; 92 | this.maxDistance = 20; 93 | this.polygons = {}; 94 | 95 | if(!group) { 96 | this.group = new Group(); 97 | this.group.name = 'Triangular'; 98 | this._id = Math.floor(Math.random() * 10000); 99 | this.group.data._id = this._id; 100 | this.lineGroup = new Group(); 101 | this.lineGroup.name = 'Lines'; 102 | this.lineGroup.visible = false; 103 | this.group.appendTop(this.lineGroup); 104 | 105 | this.polygonGroup = new Group(); 106 | this.polygonGroup.name = 'Polygons'; 107 | this.group.appendTop(this.polygonGroup); 108 | 109 | this.path = new Path(); 110 | this.path.fillColor = null; 111 | this.path.strokeColor = new GrayColor(1); 112 | this.path.strokeWidth = 1; 113 | this.path.name = 'data'; 114 | this.path.visible = false; 115 | this.group.appendTop(this.path); 116 | } else { 117 | this.group = group; 118 | this._id = this.group.data._id; 119 | this.lineGroup = group.children['Lines']; 120 | this.polygonGroup = group.children['Polygons']; 121 | this.path = group.children['data']; 122 | this.rebuild(); 123 | } 124 | }, 125 | 126 | onMouseDrag: function(event) { 127 | this.lastLines.each(function(line) { 128 | if(line.path) 129 | line.path.remove(); 130 | }); 131 | this.lastLines = []; 132 | if(!Key.isDown('shift')) { 133 | this.path.segments.pop(); 134 | // this.points.pop(); 135 | this.addPoint(event.point); 136 | } 137 | }, 138 | 139 | onMouseDown: function(event) { 140 | this.checkValidity(); 141 | 142 | this.lines = []; 143 | this.lineGroup.children.each(function(linePath) { 144 | this.lines.push(new Line(linePath)); 145 | }, this); 146 | this.points = []; 147 | this.path.segments.each(function(segment) { 148 | this.points.push(segment.point); 149 | }, this); 150 | 151 | this.lineGroup.selected = true; 152 | this.showPoints(); 153 | var newPoint = event.point; 154 | this.lineGroup.visible = true; 155 | var hitResult = this.lineGroup.hitTest(event.point); 156 | if(hitResult) { 157 | // if we hit an anchor, remove that point 158 | if(hitResult.type == 'anchor') { 159 | var found = false; 160 | newPoint = hitResult.point; 161 | this.removePoint(newPoint); 162 | } else { 163 | // if we hit a line, remove it first 164 | var item = hitResult.item; 165 | if(item instanceof Path && item.isParent(this.lineGroup)) { 166 | newPoint = hitResult.point; 167 | this.removeLine(new Line(item)); 168 | } 169 | } 170 | } 171 | // if shift is down don't add a point 172 | if(!Key.isDown('shift')) { 173 | this.addPoint(newPoint); 174 | } 175 | }, 176 | 177 | onMouseUp: function(event) { 178 | this.lineGroup.visible = false; 179 | this.hidePoints(); 180 | this.lineGroup.selected = false; 181 | this.addLastLines(); 182 | this.createPolygons(); 183 | }, 184 | 185 | checkValidity: function() { 186 | var valid = true; 187 | if(!this.group.isValid()) { 188 | var groups = document.getItems({ 189 | type: Group 190 | }); 191 | var triGroup; 192 | groups.each(function(group) { 193 | if(group.name && group.name == 'Triangular' && group.data._id == this._id) { 194 | triGroup = group; 195 | } 196 | }, this); 197 | 198 | this.initialize(triGroup); 199 | // print('recreating everything'); 200 | return 201 | } 202 | if(this.lineGroup.isValid()) { 203 | for(var i = 0, l = this.lines.length; i < l; i++) { 204 | var line = this.lines[i]; 205 | if(!line.getPath().isValid()) { 206 | i = l; 207 | this.rebuild(); 208 | } 209 | } 210 | } else { 211 | print('group is invalid'); 212 | } 213 | }, 214 | 215 | showPoints: function() { 216 | this.pointsPreview = new Layer(); 217 | this.pointsPreview.color = 'black'; 218 | var size = 2 / document.activeView.zoom; 219 | for(var i = 0, l = this.path.segments.length; i < l; i++) { 220 | this.pointsPreview.appendChild(new Path.Circle(this.path.segments[i].point, size)); 221 | } 222 | }, 223 | 224 | hidePoints: function() { 225 | if(this.pointsPreview && this.pointsPreview.isValid()) { 226 | this.pointsPreview.remove(); 227 | } 228 | }, 229 | 230 | rebuild: function() { 231 | this.rebuildLines(); 232 | this.rebuildPolygons(); 233 | }, 234 | 235 | rebuildLines: function() { 236 | this.lines = []; 237 | var children = this.lineGroup.children; 238 | for(var j = 0, l = children.length; j < l; j++) { 239 | var linePath = children[j]; 240 | if(linePath.name == 'line') { 241 | if(linePath.segments.length == 2) { 242 | var line = new Line(linePath); 243 | this.lines.push(line); 244 | } else { 245 | linePath.remove(); 246 | } 247 | } 248 | } 249 | }, 250 | 251 | rebuildPolygons: function() { 252 | this.polygons = {}; 253 | this.polygonGroup.children.each(function(polyGroup) { 254 | var polygon = new Polygon(polyGroup); 255 | this.polygons[polygon.getHash()] = polygon; 256 | }, this); 257 | }, 258 | 259 | createPolygons: function() { 260 | var polygonizer = new Polygonizer(); 261 | polygonizer.add(this.lines); 262 | var polys = polygonizer.getPolygons(); 263 | var path; 264 | if(polys) { 265 | for(var i = 0; i < polys.length; i++ ) { 266 | var poly = polys[i]; 267 | var polygon = new Polygon(poly); 268 | if(!polygon.exists()) { 269 | path = polygon.getPath(); 270 | this.polygonGroup.appendTop(polygon.group); 271 | path.data.exists = true; 272 | this.polygons[polygon.getHash()] = polygon; 273 | } else { 274 | path = this.polygons[polygon.getHash()].path; 275 | path.data.exists = true; 276 | } 277 | } 278 | this.removeUnusedPolygons(); 279 | this.polygons.each(function(polygon) { 280 | if(!polygon.fillColor) 281 | polygon.colorize(); 282 | }); 283 | } 284 | }, 285 | 286 | removeUnusedPolygons: function() { 287 | for(var i in this.polygons) { 288 | var polygon = this.polygons[i]; 289 | var path = polygon.path; 290 | if(path.data.exists) { 291 | path.data.exists = false; 292 | } else { 293 | polygon.group.remove(); 294 | delete this.polygons[i]; 295 | } 296 | } 297 | }, 298 | 299 | removeLine: function(toRemove) { 300 | for(var i = 0, l = this.lines.length; i < l; i++) { 301 | var line = this.lines[i]; 302 | if(line.equals(toRemove)) { 303 | this.lines.splice(i, 1); 304 | line.path && line.path.remove(); 305 | i = l; 306 | } 307 | } 308 | }, 309 | 310 | removePoint: function(toRemove) { 311 | for(var i = this.lines.length; i != 0; i--) { 312 | var line = this.lines[i - 1]; 313 | if(line.hasPoint(toRemove)) { 314 | line.path && line.path.remove(); 315 | this.lines.splice(i - 1, 1); 316 | found = true; 317 | } 318 | } 319 | if(found) { 320 | for(var i = 0, l = this.path.segments.length; i < l; i++) { 321 | var segment = this.path.segments[i]; 322 | if(segment.point == toRemove) { 323 | this.path.segments.splice(i, 1); 324 | i = l; 325 | } 326 | } 327 | } 328 | }, 329 | 330 | addLastLines: function() { 331 | this.lastLines.each(function(line) { 332 | this.lines.push(line); 333 | }, this); 334 | this.lastLines = []; 335 | }, 336 | 337 | addPoint: function(newPoint) { 338 | this.path.add(newPoint); 339 | var path = this.path; 340 | var line = new Line(newPoint, newPoint); 341 | for(var j = 0; j < path.segments.length; j++) { 342 | var segment = path.segments[j]; 343 | var point = segment.point; 344 | var delta = point - newPoint; 345 | if(delta.length > 0 && delta.length < values.maxDistance) { 346 | line.initialize(newPoint, point); 347 | var crosses = false; 348 | for(var k = 0, l = this.lines.length; k < l; k++) { 349 | var oldLine = this.lines[k]; 350 | if(oldLine.intersects(line)) { 351 | crosses = true; 352 | k = l; 353 | } 354 | } 355 | if(!crosses) { 356 | this.lastLines.push(new Line(newPoint, point)); 357 | } 358 | } 359 | } 360 | this.lastLines.each(function(line) { 361 | var path = line.getPath(); 362 | this.lineGroup.appendTop(path); 363 | }, this); 364 | } 365 | }); 366 | 367 | Polygon = Base.extend({ 368 | initialize: function(points) { 369 | this.lines = []; 370 | this.points = []; 371 | if(points instanceof Group) { 372 | this.group = points; 373 | this.path = this.group.children['polyPath']; 374 | this.fillColor = this.path.data.fillColor; 375 | for(var i = 0, l = this.path.segments.length; i < l; i++) { 376 | var segment = this.path.segments[i]; 377 | this.points.push(segment.point); 378 | } 379 | } else { 380 | this.points = []; 381 | for(var i = 0, l = points.length; i < l; i++) { 382 | if(i % 2) 383 | this.points.push(points[i]); 384 | } 385 | } 386 | for(var i = 0, l = this.points.length; i < l; i++) { 387 | var point = this.points[i]; 388 | var nextPoint = this.points[i + 1 < this.points.length ? i + 1 : 0]; 389 | this.lines.push([point, nextPoint]); 390 | } 391 | }, 392 | 393 | exists: function() { 394 | return !!structure.polygons[this.getHash()]; 395 | }, 396 | 397 | getPath: function() { 398 | this.path = new Path(); 399 | this.path.strokeColor = null; 400 | this.path.fillColor = null; 401 | this.path.closed = true; 402 | this.path.name = 'polyPath'; 403 | this.path.data.hash = this.getHash(); 404 | for(var j = 0, l = this.points.length; j < l; j++) { 405 | var point = this.points[j]; 406 | this.path.add(point); 407 | } 408 | 409 | this.group = new Group(); 410 | this.group.name = 'polygon'; 411 | this.group.appendTop(this.path); 412 | /* 413 | var center = this.getInCenter(); 414 | if(center) { 415 | var clone = this.path.clone(); 416 | clone.scale(0.5, center); 417 | clone.name = 'inside'; 418 | this.group.appendTop(clone); 419 | 420 | var clone = clone.clone(); 421 | clone.name = 'shadow'; 422 | var delta = clone.segments[1].point - clone.segments[0].point; 423 | delta.length = Math.random() * delta.length / 2 + (delta.length * 0.1); 424 | clone.segments[1].point = clone.segments[0].point + delta; 425 | } 426 | */ 427 | return this.path; 428 | }, 429 | 430 | getInCenter: function() { 431 | if(!this.center) { 432 | if(this.points.length == 3) { 433 | var lines = []; 434 | for(var i = 0, l = this.points.length; i < l; i++) { 435 | var point = this.points[i]; 436 | var prevPoint = i == 0 ? this.points[l - 1] : this.points[i - 1]; 437 | var nextPoint = i == l - 1 ? this.points[0] : this.points[i + 1]; 438 | var delta1 = nextPoint - point; 439 | var delta2 = prevPoint - point; 440 | delta2.angle = (delta1.angle + delta2.angle) / 2; 441 | lines.push(new Line(point, point + delta2, true)); 442 | } 443 | this.center = lines[0].getIntersectionPoint(lines[1]); 444 | } 445 | } 446 | return this.center; 447 | }, 448 | 449 | getHash: function() { 450 | if(!this.hash) { 451 | if(this.path) { 452 | this.hash = this.path.data.hash; 453 | } else { 454 | var sum = new Point(); 455 | for(var i = 0, l = this.points.length; i < l; i++) { 456 | sum += this.points[i]; 457 | } 458 | sum = (sum * 10000).floor(); 459 | this.hash = sum.x + '' + sum.y; 460 | } 461 | } 462 | return this.hash; 463 | }, 464 | 465 | findShared: function(mLine) { 466 | for(var i in structure.polygons) { 467 | var polygon = structure.polygons[i]; 468 | if(polygon.getHash() != this.getHash()) { 469 | var found = false; 470 | for(var j = 0; j < polygon.lines.length; j++) { 471 | var line = polygon.lines[j]; 472 | if((line[0] == mLine[0] && line[1] == mLine[1]) || (line[1] == mLine[0] && line[0] == mLine[1])) { 473 | return polygon; 474 | } 475 | } 476 | } 477 | } 478 | }, 479 | 480 | colorize: function() { 481 | 482 | rasters = document.getItems({ 483 | type: [Raster, PlacedFile], 484 | selected: true 485 | }); 486 | 487 | if (!rasters) { Dialog.alert("Select one raster"); return }; 488 | 489 | var c = rasters[0].getAverageColor(this.path); 490 | var c1 = c.clone(); 491 | var c2 = c.clone(); 492 | c1.red *= 0.75; 493 | c1.green *= 0.75; 494 | c1.blue *= 0.75; 495 | c2.red *= 1.1; 496 | c2.green *= 1.1; 497 | c2.blue *= 1.1; 498 | var bounds = this.path.bounds; 499 | var gr = new Gradient() { type: 'radial', stops: [ new GradientStop(c2, 0.0), new GradientStop(c1, 1.0)]}; 500 | this.path.fillColor = new GradientColor(gr, bounds.bottomRight, bounds.topLeft); 501 | 502 | /* 503 | var colors = {}; 504 | var intersects = false; 505 | var colorSet = activeColorSet; 506 | for(var i = 0; i < 4; i++) { 507 | colors[colorSet[i]] = { 508 | found: false 509 | } 510 | } 511 | var takenColors = []; 512 | for(var i = 0; i < this.lines.length; i++) { 513 | var sPolygon = this.findShared(this.lines[i]); 514 | if(sPolygon && sPolygon.fillColor) { 515 | if(colors[sPolygon.fillColor]) 516 | colors[sPolygon.fillColor].found = true; 517 | } 518 | } 519 | var selected = false; 520 | var colorsArray = []; 521 | for(var i in colors) { 522 | colors[i].color = i; 523 | colorsArray.push(colors[i]); 524 | } 525 | colorsArray = colorsArray.shuffle(); 526 | for(var i = 0; i < colorsArray.length; i++) { 527 | if(!colorsArray[i].found) { 528 | var color = colorsArray[i].color; 529 | this.path.fillColor = '#' + color; 530 | var index = i + 2 >= colorsArray.length ? (i + 2) % colorsArray.length : i + 2; 531 | this.group.children[0].fillColor = colorsArray[index].color; 532 | if(this.group.children.length >= 2) { 533 | var index = i + 1 == colorsArray.length ? 0 : i + 1; 534 | this.group.children[1].fillColor = colorsArray[index].color; 535 | } 536 | this.fillColor = color; 537 | this.path.data.fillColor = color; 538 | break; 539 | } 540 | } 541 | */ 542 | } 543 | }); 544 | 545 | Line = Base.extend({ 546 | initialize: function(point1, point2, extend) { 547 | if(point1 instanceof Path) { 548 | var path = point1; 549 | this.point1 = path.segments.first.point; 550 | this.point2 = path.segments.last.point; 551 | this.path = path; 552 | } else { 553 | this.point1 = point1; 554 | this.point2 = point2; 555 | } 556 | this.vector = this.point2 - this.point1; 557 | var shortV = this.vector.normalize(0.01); 558 | this.shortVector = this.vector - shortV; 559 | this.shortPoint1 = this.point1 - shortV; 560 | // extend controls wether the line extends beyond the defining points, 561 | // meaning point results outside the line segment are allowed. 562 | this.extend = extend; 563 | }, 564 | 565 | getPath: function() { 566 | if(!this.path) { 567 | this.path = new Path.Line(this.point1, this.point2) { 568 | strokeColor: null, 569 | fillColor: null, 570 | name: 'line' 571 | }; 572 | } 573 | return this.path; 574 | }, 575 | 576 | intersects: function(line) { 577 | var v1 = this.shortVector; 578 | var v2 = line.shortVector; 579 | var cross = v1.cross(v2); 580 | // Epsilon tolerance 581 | if (Math.abs(cross) <= 10e-6) 582 | return null; 583 | var v = line.shortPoint1 - this.shortPoint1; 584 | var t1 = v.cross(v2) / cross; 585 | var t2 = v.cross(v1) / cross; 586 | // Check the ranges of t parameters if the line is not allowed to 587 | // extend beyond the definition points. 588 | if ((this.extend || 0 <= t1 && t1 <= 1) 589 | && (line.extend || 0 <= t2 && t2 <= 1)) 590 | return true; 591 | return null; 592 | }, 593 | 594 | getIntersectionPoint: function(line) { 595 | var v1 = this.shortVector; 596 | var v2 = line.shortVector; 597 | var cross = v1.cross(v2); 598 | // Epsilon tolerance 599 | if (Math.abs(cross) <= 10e-6) 600 | return null; 601 | var v = line.shortPoint1 - this.shortPoint1; 602 | var t1 = v.cross(v2) / cross; 603 | var t2 = v.cross(v1) / cross; 604 | // Check the ranges of t parameters if the line is not allowed to 605 | // extend beyond the definition points. 606 | if ((this.extend || 0 <= t1 && t1 <= 1) 607 | && (line.extend || 0 <= t2 && t2 <= 1)) 608 | return this.point1 + v1 * t1; 609 | return null; 610 | }, 611 | 612 | getSide: function(p) { 613 | var v1 = this.point2 - this.point1; 614 | var v2 = p - this.point1; 615 | var ccw = v2.cross(v1); 616 | if (ccw == 0.0) { 617 | ccw = v2.dot(v1); 618 | if (ccw > 0.0) { 619 | ccw = (v2 - v1).dot(v1); 620 | if (ccw < 0.0) 621 | ccw = 0.0; 622 | } 623 | } 624 | return ccw < 0.0 ? -1 : ccw > 0.0 ? 1 : 0; 625 | }, 626 | 627 | getVector: function() { 628 | return this.point2 - this.point1; 629 | }, 630 | 631 | equals: function(line) { 632 | return this.point1 == line.point1 && this.point2 == line.point2; 633 | }, 634 | 635 | sharesPoint: function(line) { 636 | return line.hasPoint(this.point1) || line.hasPoint(this.point2); 637 | }, 638 | 639 | hasPoint: function(point) { 640 | return this.point1 == point || this.point2 == point; 641 | }, 642 | 643 | toString: function() { 644 | return 'Line: point1: ' + this.point1 + ', point2' + this.point2; 645 | } 646 | }); 647 | 648 | /* 649 | * Adapted to Javascript from the JTS Topology Suite by Jonathan Puckey 2010 650 | * 651 | * This library is free software; you can redistribute it and/or 652 | * modify it under the terms of the GNU Lesser General Public 653 | * License as published by the Free Software Foundation; either 654 | * version 2.1 of the License, or (at your option) any later version. 655 | * 656 | * This library is distributed in the hope that it will be useful, 657 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 658 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 659 | * Lesser General Public License for more details. 660 | * 661 | * You should have received a copy of the GNU Lesser General Public 662 | * License along with this library; if not, write to the Free Software 663 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 664 | * 665 | * For more information, contact: 666 | * 667 | * Vivid Solutions 668 | * Suite #1A 669 | * 2328 Government Street 670 | * Victoria BC V8T 5G5 671 | * Canada 672 | * 673 | * (250)385-6040 674 | * www.vividsolutions.com 675 | */ 676 | 677 | Polygonizer = Base.extend({ 678 | initialize: function() { 679 | this.dangles = []; 680 | this.cutEdges = []; 681 | this.invalidRingLines = []; 682 | this.holeList = null; 683 | this.shellList = null; 684 | this.polyList = null; 685 | }, 686 | 687 | add: function(lines) { 688 | lines.each(function(line) { 689 | if(!this.graph) 690 | this.graph = new PolygonizeGraph(); 691 | this.graph.addEdge(line); 692 | }, this); 693 | }, 694 | 695 | getPolygons: function() { 696 | this.polygonize(); 697 | return this.polyList; 698 | }, 699 | 700 | polygonize: function() { 701 | if(this.polyList != null) return; 702 | this.polyList = []; 703 | if(!this.graph) return; 704 | 705 | this.dangles = this.graph.deleteDangles(); 706 | this.cutEdges = this.graph.deleteCutEdges(); 707 | this.edgeRingList = this.graph.getEdgeRings(); 708 | this.validEdgeRingList = []; 709 | this.invalidRingList = []; 710 | // this.edgeRingList = []; 711 | this.findValidRings(); 712 | this.findShellsAndHoles(); 713 | //this.assignHolesToShells(this.holeList, this.shellList); 714 | this.polyList = []; 715 | for(var i = 0, l = this.shellList.length; i < l; i++) { 716 | var er = this.shellList[i]; 717 | var points = er.getCoordinates(); 718 | this.polyList.push(points); 719 | } 720 | }, 721 | 722 | assignHolesToShells: function(holeList, shellList) { 723 | for(var i = 0, l = holeList.length; i < l; i++) { 724 | var holeER = holeList[i]; 725 | this.assignHoleToShell(holeER, shellList); 726 | } 727 | }, 728 | 729 | assignHoleToShell: function(holeER, shellList) { 730 | var shell = EdgeRing.findEdgeRingContaining(holeER, shellList); 731 | if(shell != null) 732 | shell.addHole(holeER.getRing()); 733 | }, 734 | 735 | findValidRings: function() { 736 | for(var i = 0, l = this.edgeRingList.length; i < l; i++) { 737 | var er = this.edgeRingList[i]; 738 | if(er.isValid()) { 739 | this.validEdgeRingList.push(er); 740 | } else { 741 | this.invalidRingList.push(er); 742 | } 743 | } 744 | }, 745 | 746 | findShellsAndHoles: function() { 747 | this.holeList = []; 748 | this.shellList = []; 749 | for(var i = 0, l = this.edgeRingList.length; i < l; i++) { 750 | var er = this.edgeRingList[i]; 751 | if(er.isHole()) { 752 | this.holeList.push(er); 753 | } else { 754 | this.shellList.push(er); 755 | } 756 | } 757 | }, 758 | }); 759 | 760 | /** 761 | * Represents a planar graph of edges that can be used to compute a 762 | * polygonization, and implements the algorithms to compute the 763 | * {@link EdgeRings} formed by the graph. 764 | *

765 | * The marked flag on {@link DirectedEdge}s is used to indicate that a directed edge 766 | * has be logically deleted from the graph. 767 | * 768 | * @version 1.7 769 | */ 770 | PolygonizeGraph = Base.extend({ 771 | initialize: function() { 772 | this.edges = []; 773 | this.dirEdges = []; 774 | this.nodes = {}; 775 | }, 776 | 777 | addEdge: function(line) { 778 | if(line.point1 != line.point2) { 779 | var startPt = line.point1; 780 | var endPt = line.point2; 781 | 782 | var nStart = this.getNode(startPt); 783 | var nEnd = this.getNode(endPt); 784 | 785 | var de0 = new PolygonizeDirectedEdge(nStart, nEnd, line.point2, true); 786 | var de1 = new PolygonizeDirectedEdge(nEnd, nStart, line.point1, false); 787 | var edge = new PolygonizeEdge(line); 788 | edge.setDirectedEdges(de0, de1); 789 | this.add(edge); 790 | } 791 | //var linePts = CoordinateArrays.removeRepeatedPoints(line.getCoordinates()); 792 | }, 793 | 794 | deleteDangles: function() { 795 | // nodes to remove: 796 | var nodeStack = this.findNodesOfDegree(1); 797 | var dangleLines = []; 798 | while(nodeStack.length) { 799 | // for(var i = 0, l = nodesToRemove.length; i < l; i++) { 800 | var node = nodeStack.pop(); 801 | this.deleteAllEdges(node); 802 | var nodeOutEdges = node.getOutEdges().getEdges(); 803 | for(var j = 0, l = nodeOutEdges.length; j < l; j++) { 804 | var de = nodeOutEdges[j]; 805 | de.marked = true; 806 | var sym = de.getSym(); 807 | if(sym) 808 | sym.marked = true; 809 | 810 | // save the line as a dangle 811 | var e = de.getEdge(); 812 | dangleLines.push(e); 813 | var toNode = de.to; 814 | // add the toNode to the list to be processed, if it is now a dangle 815 | if (this.getDegreeNonDeleted(toNode) == 1) 816 | nodeStack.push(toNode); 817 | } 818 | } 819 | return dangleLines; 820 | }, 821 | 822 | deleteCutEdges: function() { 823 | this.computeNextCWEdges(); 824 | // label the current set of edgerings 825 | this.findLabeledEdgeRings(this.dirEdges); 826 | /** 827 | * Cut Edges are edges where both dirEdges have the same label. 828 | * Delete them, and record them 829 | */ 830 | var cutLines = []; 831 | for(var i = 0, l = this.dirEdges.length; i < l; i++) { 832 | var de = this.dirEdges[i]; 833 | if(de.marked) continue; 834 | var sym = de.getSym(); 835 | if(de.label == sym.label) { 836 | de.marked = true; 837 | sym.marked = true; 838 | 839 | // save the line as a cut edge 840 | var e = de.getEdge(); 841 | cutLines.push(e.line); 842 | } 843 | } 844 | return cutLines; 845 | }, 846 | 847 | computeNextCWEdges: function() { 848 | this.nodes.each(function(node) { 849 | var deStar = node.getOutEdges(); 850 | var startDE = null; 851 | var prevDE = null; 852 | var edges = deStar.getEdges(); 853 | for(var i = 0, l = edges.length; i < l; i++) { 854 | var outDE = edges[i]; 855 | if(outDE.marked) continue; 856 | if(startDE == null) 857 | startDE = outDE; 858 | if(prevDE != null) { 859 | var sym = prevDE.getSym(); 860 | sym.next = outDE; 861 | } 862 | prevDE = outDE 863 | } 864 | if(prevDE != null) { 865 | var sym = prevDE.getSym(); 866 | sym.next = startDE; 867 | } 868 | }, this); 869 | }, 870 | 871 | /** 872 | * Computes the EdgeRings formed by the edges in this graph. 873 | * @return a list of the {@link EdgeRing}s found by the polygonization process. 874 | */ 875 | getEdgeRings: function() { 876 | // maybe could optimize this, since most of these pointers should be set correctly already 877 | // by deleteCutEdges() 878 | this.computeNextCWEdges(); 879 | // clear labels of all edges in graph 880 | this.label(this.dirEdges, -1); 881 | var maximalRings = this.findLabeledEdgeRings(this.dirEdges); 882 | this.convertMaximalToMinimalEdgeRings(maximalRings); 883 | var edgeRingList = []; 884 | for(var i = 0, l = this.dirEdges.length; i < l; i++) { 885 | var de = this.dirEdges[i]; 886 | if(de.marked) continue; 887 | if(de.isInRing()) continue 888 | var er = this.findEdgeRing(de); 889 | edgeRingList.push(er); 890 | } 891 | return edgeRingList; 892 | }, 893 | 894 | label: function(dirEdges, label) { 895 | for(var i = 0, l = dirEdges.length; i < l; i++) 896 | dirEdges[i].label = label; 897 | }, 898 | 899 | findEdgeRing: function(startDE) { 900 | var de = startDE; 901 | er = new EdgeRing(); 902 | do { 903 | er.add(de); 904 | de.setRing(er); 905 | de = de.next; 906 | } while (de != startDE); 907 | return er; 908 | }, 909 | 910 | findLabeledEdgeRings: function(dirEdges) { 911 | var edgeRingStarts = []; 912 | var currLabel = 1; 913 | for(var i = 0, l = dirEdges.length; i < l; i++) { 914 | var de = dirEdges[i]; 915 | if(de.marked) continue; 916 | if(de.label >= 0) continue; 917 | 918 | edgeRingStarts.push(de); 919 | var edges = this.findDirEdgesInRing(de); 920 | this.label(edges, currLabel); 921 | currLabel++; 922 | } 923 | return edgeRingStarts; 924 | }, 925 | 926 | findDirEdgesInRing: function(startDE) { 927 | var de = startDE; 928 | var edges = []; 929 | do { 930 | edges.push(de); 931 | de = de.next; 932 | } while (de != startDE); 933 | return edges; 934 | }, 935 | 936 | convertMaximalToMinimalEdgeRings: function(ringEdges) { 937 | for(var i = 0, l = ringEdges.length; i < l; i++) { 938 | var de = ringEdges[i]; 939 | var label = de.label; 940 | var intNodes = this.findIntersectionNodes(de, label); 941 | if(intNodes == null) continue; 942 | // flip the next pointers on the intersection nodes to create minimal edge rings 943 | for(var j = 0, l = intNodes.length; j < l; j++) { 944 | var node = intNodes[j]; 945 | this.computeNextCCWEdges(node, label); 946 | } 947 | } 948 | }, 949 | 950 | getDegree: function(node, label) { 951 | var edges = node.getOutEdges().getEdges(); 952 | var degree = 0; 953 | for(var i = 0, l = edges.length; i < l; i++) { 954 | var de = edges[i]; 955 | if(de.label == label) degree++; 956 | } 957 | return degree; 958 | }, 959 | 960 | findIntersectionNodes: function(startDE, label) { 961 | var intNodes = null; 962 | var de = startDE; 963 | do { 964 | var node = de.from; 965 | if(this.getDegree(node, label) > 1) { 966 | if(intNodes == null) 967 | intNodes = []; 968 | intNodes.push(node); 969 | } 970 | 971 | de = de.next; 972 | // if(!de) { 973 | // print('found null DE in ring'); 974 | // } 975 | // if(de == startDE || !de.isInRing()) { 976 | // print('found DE already in ring'); 977 | // } 978 | } while (de != startDE); 979 | 980 | return intNodes; 981 | }, 982 | 983 | /** 984 | * Computes the next edge pointers going CCW around the given node, for the 985 | * given edgering label. 986 | * This algorithm has the effect of converting maximal edgerings into minimal edgerings 987 | */ 988 | computeNextCCWEdges: function(node, label) { 989 | var deStar = node.getOutEdges(); 990 | var firstOutDE = null; 991 | var prevInDE = null 992 | 993 | // the edges are stored in CCW order around the star 994 | var edges = deStar.getEdges(); 995 | for(var i = edges.length - 1; i >= 0; i--) { 996 | var de = edges[i]; 997 | var sym = de.getSym(); 998 | 999 | var outDE = null; 1000 | if(de.label == label) outDE = de; 1001 | var inDE = null; 1002 | if(sym.label == label) inDE = sym; 1003 | if(outDE == null && inDE == null) continue; // this edge is not in edgering 1004 | if(inDE != null) 1005 | prevInDE = inDE; 1006 | if(outDE != null) { 1007 | if(prevInDE != null) { 1008 | prevInDE.next = outDE; 1009 | prevInDE = null 1010 | } 1011 | if(firstOutDE == null) 1012 | firstOutDE = outDE; 1013 | } 1014 | } 1015 | if(prevInDE != null) { 1016 | prevInDE.next = firstOutDE; 1017 | } 1018 | }, 1019 | 1020 | getDegreeNonDeleted: function(node) { 1021 | var edges = node.getOutEdges().getEdges(); 1022 | var degree = 0; 1023 | edges.each(function(de) { 1024 | if(!de.marked) degree++; 1025 | }); 1026 | return degree; 1027 | }, 1028 | 1029 | findNodesOfDegree: function(degree) { 1030 | var nodesFound = []; 1031 | this.nodes.each(function(node) { 1032 | if(node.getDegree() == degree) 1033 | nodesFound.push(node); 1034 | }); 1035 | return nodesFound; 1036 | }, 1037 | 1038 | deleteAllEdges: function(node) { 1039 | var edges = node.getOutEdges().getEdges(); 1040 | for(var i = 0, l = edges.length; i < l; i++) { 1041 | var de = edges[i]; 1042 | de.marked = true; 1043 | var sym = de.getSym(); 1044 | if(sym) 1045 | sym.marked = true; 1046 | } 1047 | }, 1048 | 1049 | /** 1050 | * Adds the Edge and its DirectedEdges with this PlanarGraph. 1051 | * Assumes that the Edge has already been created with its associated DirectEdges. 1052 | * Only subclasses can add Edges, to ensure the edges added are of the right class. 1053 | */ 1054 | add: function(edge) { 1055 | this.edges.push(edge); 1056 | this.dirEdges.push(edge.getDirEdge(0)); 1057 | this.dirEdges.push(edge.getDirEdge(1)); 1058 | }, 1059 | 1060 | getNode: function(point) { 1061 | var node = this.nodes[point.toString()]; 1062 | if(!node) 1063 | node = this.nodes[point.toString()] = new Node(point); 1064 | return node; 1065 | } 1066 | }); 1067 | 1068 | EdgeRing = Base.extend({ 1069 | initialize: function() { 1070 | this.deList = []; 1071 | this.ringPts = null; 1072 | }, 1073 | add: function(de) { 1074 | this.deList.push(de); 1075 | }, 1076 | isValid: function() { 1077 | this.getCoordinates(); 1078 | if(this.ringPts.length <= 3) return false; 1079 | return true; 1080 | this.getRing(); 1081 | return this.ring.isValid(); 1082 | }, 1083 | getCoordinates: function() { 1084 | if(!this.ringPts) { 1085 | var coordList = []; 1086 | for(var i = 0, l = this.deList.length; i < l; i++) { 1087 | var de = this.deList[i]; 1088 | var edge = de.getEdge(); 1089 | var line = edge.line; 1090 | coordList = this.addEdge([line.point1, line.point2], de.edgeDirection, coordList); 1091 | } 1092 | this.ringPts = coordList; 1093 | } 1094 | return this.ringPts; 1095 | }, 1096 | addEdge: function(coords, isForward, coordList) { 1097 | if(isForward) { 1098 | for(var i = 0; i < coords.length; i++) { 1099 | coordList.push(coords[i]); 1100 | } 1101 | } else { 1102 | for(var i = coords.length - 1; i >= 0; i--) { 1103 | coordList.push(coords[i]); 1104 | } 1105 | } 1106 | return coordList; 1107 | }, 1108 | addHole: function(hole) { 1109 | if(!this.holes) 1110 | this.holes = []; 1111 | this.holes.push(hole); 1112 | }, 1113 | getRing: function() { 1114 | if(this.ring) return this.ring; 1115 | this.getCoordinates(); 1116 | return this.ringPts; 1117 | }, 1118 | isHole: function() { 1119 | var ring = this.getRing(); 1120 | return CGAlgorithms.isCCW(ring); 1121 | }, 1122 | statics: { 1123 | findEdgeRingContaining: function(testEr, shellList) { 1124 | var testRing = testEr.getRing(); 1125 | var testEnv = testRing.getEnvelopeInternal(); 1126 | var testPt = testRing.getCoordinateN(0); 1127 | 1128 | var minShell = null; 1129 | var minEnv = null; 1130 | for(var i = 0, l = shellList.length; i < l; i++) { 1131 | var tryShell = shellList[i]; 1132 | var tryRing = tryShell.getRing(); 1133 | var tryEnv = tryRing.getEnvelopeInternal(); 1134 | if(minShell != null) minEnv = minShell.getRing().getEnvelopeInternal(); 1135 | var isContained = false; 1136 | if(tryEnv == testEnv) 1137 | continue; 1138 | var testPt = CoordinateArrays.ptNotInList(testRing.getCoordinates(), tryRing.getCoordinates()); 1139 | var isContained = tryEnv.contains(testEnv) && cga.isPointInRing(testPt, tryRing.getCoordinates()); 1140 | if(isContained) { 1141 | if(minShell = null || minEnv.contains(tryEnv)) { 1142 | minShell = tryShell; 1143 | } 1144 | } 1145 | } 1146 | return minShell; 1147 | } 1148 | } 1149 | }); 1150 | 1151 | Edge = Base.extend({ 1152 | /** 1153 | * Initializes this Edge's two DirectedEdges, and for each DirectedEdge: sets the 1154 | * Edge, sets the symmetric DirectedEdge, and adds this Edge to its from-Node. 1155 | */ 1156 | setDirectedEdges: function(de0, de1) { 1157 | this.dirEdge = [de0, de1]; 1158 | de0.setEdge(this); 1159 | de1.setEdge(this); 1160 | de0.setSym(de1); 1161 | de1.setSym(de0); 1162 | de0.from.addOutEdge(de0); 1163 | de1.from.addOutEdge(de1); 1164 | }, 1165 | getDirEdge: function(i) { 1166 | return this.dirEdge[i]; 1167 | } 1168 | }); 1169 | 1170 | PolygonizeEdge = Edge.extend({ 1171 | initialize: function(line) { 1172 | this.line = line; 1173 | } 1174 | }); 1175 | 1176 | PolygonizeDirectedEdge = Edge.extend({ 1177 | initialize: function(from, to, directionPoint, edgeDirection) { 1178 | this.from = from; 1179 | this.to = to; 1180 | this.edgeDirection = edgeDirection; 1181 | this.p0 = from.point; 1182 | this.p1 = directionPoint; 1183 | // var dx = this.p1.x - this.p0.x; 1184 | // var dy = this.p1.y - this.p0.y; 1185 | var delta = this.p1 - this.p0; 1186 | this.quadrant = Quadrant.quadrant(delta); 1187 | this.angle = Math.atan2(delta.y, delta.x); 1188 | }, 1189 | setEdge: function(parentEdge) { 1190 | this.parentEdge = parentEdge; 1191 | }, 1192 | getEdge: function() { 1193 | return this.parentEdge; 1194 | }, 1195 | /** 1196 | * Sets this DirectedEdge's symmetric DirectedEdge, which runs in the opposite 1197 | * direction. 1198 | */ 1199 | setSym: function(sym) { 1200 | this.sym = sym; 1201 | }, 1202 | getSym: function() { 1203 | return this.sym; 1204 | }, 1205 | 1206 | compareDirection: function(e) { 1207 | if(this.quadrant > e.quadrant) return 1; 1208 | if(this.quadrant < e.quadrant) return -1; 1209 | // vectors are in the same quadrant - check relative orientation of direction vectors 1210 | // this is > e if it is CCW of e 1211 | return CGAlgorithms.computeOrientation(e.p0, e.p1, this.p1); 1212 | }, 1213 | 1214 | setRing: function(edgeRing) { 1215 | this.edgeRing = edgeRing; 1216 | }, 1217 | 1218 | isInRing: function() { 1219 | return !!this.edgeRing 1220 | } 1221 | }); 1222 | 1223 | Quadrant = Base.extend({ 1224 | statics: { 1225 | quadrant: function(p) { 1226 | if(p.x == 0 && p.y == 0) { 1227 | print('Cannot compute the quadrant for point' + p); 1228 | return; 1229 | } else if(p.x >= 0) { 1230 | return p.y >=0 ? 0 : 3; 1231 | } else { 1232 | return p.y >= 0 ? 1 : 2; 1233 | } 1234 | } 1235 | } 1236 | }); 1237 | 1238 | Node = Base.extend({ 1239 | initialize: function(pt) { 1240 | this.point = pt; 1241 | this.deStar = new DirectedEdgeStar(); 1242 | }, 1243 | addOutEdge: function(de) { 1244 | this.deStar.add(de); 1245 | }, 1246 | 1247 | getDegree: function() { 1248 | return this.deStar.getDegree(); 1249 | }, 1250 | 1251 | getOutEdges: function() { 1252 | return this.deStar; 1253 | } 1254 | }); 1255 | 1256 | /** 1257 | * A sorted collection of {@link DirectedEdge}s which leave a {@link Node} 1258 | * in a {@link PlanarGraph}. 1259 | * 1260 | * @version 1.7 1261 | */ 1262 | DirectedEdgeStar = Base.extend({ 1263 | initialize: function() { 1264 | this.outEdges = []; 1265 | this.storted = false; 1266 | }, 1267 | add: function(directedEdge) { 1268 | this.outEdges.push(directedEdge); 1269 | this.sorted = false; 1270 | }, 1271 | getDegree: function() { 1272 | return this.outEdges.length; 1273 | }, 1274 | getEdges: function() { 1275 | this.sortEdges(); 1276 | return this.outEdges; 1277 | }, 1278 | sortEdges: function() { 1279 | if(!this.sorted) { 1280 | this.outEdges.sort(function(a, b){ 1281 | return a.compareDirection(b); 1282 | }); 1283 | } 1284 | this.sorted = true; 1285 | } 1286 | }); 1287 | 1288 | CGAlgorithms = { 1289 | computeOrientation: function(p1, p2, q) { 1290 | return CGAlgorithms.orientationIndex(p1, p2, q); 1291 | }, 1292 | orientationIndex: function(p1, p2, q) { 1293 | // travelling along p1->p2, turn counter clockwise to get to q return 1, 1294 | // travelling along p1->p2, turn clockwise to get to q return -1, 1295 | // p1, p2 and q are colinear return 0. 1296 | var dx1 = p2.x - p1.x; 1297 | var dy1 = p2.y - p1.y; 1298 | var dx2 = q.x - p2.x; 1299 | var dy2 = q.y - p2.y; 1300 | return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2); 1301 | }, 1302 | isCCW: function(ring) { 1303 | var nPts = ring.length - 1; 1304 | var hiPt = ring[0]; 1305 | var hiIndex = 0; 1306 | for(var i = 1; i <= nPts; i++) { 1307 | var p = ring[i]; 1308 | if(p.y > hiPt.y) { 1309 | hiPt = p; 1310 | hiIndex = i; 1311 | } 1312 | } 1313 | 1314 | //find distinct point before highest point 1315 | var iPrev = hiIndex; 1316 | do { 1317 | iPrev = iPrev - 1; 1318 | if(iPrev < 0) iPrev = nPts; 1319 | } while (ring[iPrev] == hiPt && iPrev != hiIndex); 1320 | 1321 | //find distinct point after highest point 1322 | var iNext = hiIndex; 1323 | do { 1324 | iNext = (iNext + 1) % nPts; 1325 | } while (ring[iNext] == hiPt && iNext != hiIndex); 1326 | 1327 | var prev = ring[iPrev]; 1328 | var next = ring[iNext]; 1329 | 1330 | /** 1331 | * This check catches cases where the ring contains an A-B-A configuration of points. 1332 | * This can happen if the ring does not contain 3 distinct points 1333 | * (including the case where the input array has fewer than 4 elements), 1334 | * or it contains coincident line segments. 1335 | */ 1336 | if(prev == hiPt || next == hiPt || prev == next) 1337 | return false; 1338 | var disc = CGAlgorithms.computeOrientation(prev, hiPt, next); 1339 | 1340 | /** 1341 | * If disc is exactly 0, lines are collinear. There are two possible cases: 1342 | * (1) the lines lie along the x axis in opposite directions 1343 | * (2) the lines lie on top of one another 1344 | * 1345 | * (1) is handled by checking if next is left of prev ==> CCW 1346 | * (2) will never happen if the ring is valid, so don't check for it 1347 | * (Might want to assert this) 1348 | */ 1349 | var isCCW = false; 1350 | if(disc == 0) { 1351 | // poly is CCW if prev x is right of next x 1352 | isCCW = prev.x > next.x; 1353 | } else { 1354 | // if area is positive, points are ordered CCW 1355 | isCCW = (disc > 0); 1356 | } 1357 | return isCCW; 1358 | } 1359 | }; 1360 | 1361 | RobustDeterminant = { 1362 | signOfDet2x2: function(x1, y1, x2, y2) { 1363 | // returns -1 if the determinant is negative, 1364 | // returns 1 if the determinant is positive, 1365 | // retunrs 0 if the determinant is null. 1366 | var sign, swap, k, count = 0; 1367 | 1368 | //callCount++; // debugging only 1369 | 1370 | sign = 1; 1371 | 1372 | /* 1373 | * testing null entries 1374 | */ 1375 | if ((x1 == 0) || (y2 == 0)) { 1376 | if ((y1 == 0) || (x2 == 0)) { 1377 | return 0; 1378 | } 1379 | else if (y1 > 0) { 1380 | if (x2 > 0) { 1381 | return -sign; 1382 | } 1383 | else { 1384 | return sign; 1385 | } 1386 | } 1387 | else { 1388 | if (x2 > 0) { 1389 | return sign; 1390 | } 1391 | else { 1392 | return -sign; 1393 | } 1394 | } 1395 | } 1396 | if ((y1 == 0) || (x2 == 0)) { 1397 | if (y2 > 0) { 1398 | if (x1 > 0) { 1399 | return sign; 1400 | } 1401 | else { 1402 | return -sign; 1403 | } 1404 | } 1405 | else { 1406 | if (x1 > 0) { 1407 | return -sign; 1408 | } 1409 | else { 1410 | return sign; 1411 | } 1412 | } 1413 | } 1414 | 1415 | /* 1416 | * making y coordinates positive and permuting the entries 1417 | */ 1418 | /* 1419 | * so that y2 is the biggest one 1420 | */ 1421 | if (0 < y1) { 1422 | if (0 < y2) { 1423 | if (y1 <= y2) { 1424 | ; 1425 | } 1426 | else { 1427 | sign = -sign; 1428 | swap = x1; 1429 | x1 = x2; 1430 | x2 = swap; 1431 | swap = y1; 1432 | y1 = y2; 1433 | y2 = swap; 1434 | } 1435 | } 1436 | else { 1437 | if (y1 <= -y2) { 1438 | sign = -sign; 1439 | x2 = -x2; 1440 | y2 = -y2; 1441 | } 1442 | else { 1443 | swap = x1; 1444 | x1 = -x2; 1445 | x2 = swap; 1446 | swap = y1; 1447 | y1 = -y2; 1448 | y2 = swap; 1449 | } 1450 | } 1451 | } 1452 | else { 1453 | if (0 < y2) { 1454 | if (-y1 <= y2) { 1455 | sign = -sign; 1456 | x1 = -x1; 1457 | y1 = -y1; 1458 | } 1459 | else { 1460 | swap = -x1; 1461 | x1 = x2; 1462 | x2 = swap; 1463 | swap = -y1; 1464 | y1 = y2; 1465 | y2 = swap; 1466 | } 1467 | } 1468 | else { 1469 | if (y1 >= y2) { 1470 | x1 = -x1; 1471 | y1 = -y1; 1472 | x2 = -x2; 1473 | y2 = -y2; 1474 | ; 1475 | } 1476 | else { 1477 | sign = -sign; 1478 | swap = -x1; 1479 | x1 = -x2; 1480 | x2 = swap; 1481 | swap = -y1; 1482 | y1 = -y2; 1483 | y2 = swap; 1484 | } 1485 | } 1486 | } 1487 | 1488 | /* 1489 | * making x coordinates positive 1490 | */ 1491 | /* 1492 | * if |x2| < |x1| one can conclude 1493 | */ 1494 | if (0 < x1) { 1495 | if (0 < x2) { 1496 | if (x1 <= x2) { 1497 | ; 1498 | } 1499 | else { 1500 | return sign; 1501 | } 1502 | } 1503 | else { 1504 | return sign; 1505 | } 1506 | } 1507 | else { 1508 | if (0 < x2) { 1509 | return -sign; 1510 | } 1511 | else { 1512 | if (x1 >= x2) { 1513 | sign = -sign; 1514 | x1 = -x1; 1515 | x2 = -x2; 1516 | ; 1517 | } 1518 | else { 1519 | return -sign; 1520 | } 1521 | } 1522 | } 1523 | 1524 | /* 1525 | * all entries strictly positive x1 <= x2 and y1 <= y2 1526 | */ 1527 | while (true) { 1528 | count = count + 1; 1529 | k = Math.floor(x2 / x1); 1530 | x2 = x2 - k * x1; 1531 | y2 = y2 - k * y1; 1532 | 1533 | /* 1534 | * testing if R (new U2) is in U1 rectangle 1535 | */ 1536 | if (y2 < 0) { 1537 | return -sign; 1538 | } 1539 | if (y2 > y1) { 1540 | return sign; 1541 | } 1542 | 1543 | /* 1544 | * finding R' 1545 | */ 1546 | if (x1 > x2 + x2) { 1547 | if (y1 < y2 + y2) { 1548 | return sign; 1549 | } 1550 | } 1551 | else { 1552 | if (y1 > y2 + y2) { 1553 | return -sign; 1554 | } 1555 | else { 1556 | x2 = x1 - x2; 1557 | y2 = y1 - y2; 1558 | sign = -sign; 1559 | } 1560 | } 1561 | if (y2 == 0) { 1562 | if (x2 == 0) { 1563 | return 0; 1564 | } 1565 | else { 1566 | return -sign; 1567 | } 1568 | } 1569 | if (x2 == 0) { 1570 | return sign; 1571 | } 1572 | 1573 | /* 1574 | * exchange 1 and 2 role. 1575 | */ 1576 | k = Math.floor(x1 / x2); 1577 | x1 = x1 - k * x2; 1578 | y1 = y1 - k * y2; 1579 | 1580 | /* 1581 | * testing if R (new U1) is in U2 rectangle 1582 | */ 1583 | if (y1 < 0) { 1584 | return sign; 1585 | } 1586 | if (y1 > y2) { 1587 | return -sign; 1588 | } 1589 | 1590 | /* 1591 | * finding R' 1592 | */ 1593 | if (x2 > x1 + x1) { 1594 | if (y2 < y1 + y1) { 1595 | return -sign; 1596 | } 1597 | } 1598 | else { 1599 | if (y2 > y1 + y1) { 1600 | return sign; 1601 | } 1602 | else { 1603 | x1 = x2 - x1; 1604 | y1 = y2 - y1; 1605 | sign = -sign; 1606 | } 1607 | } 1608 | if (y1 == 0) { 1609 | if (x1 == 0) { 1610 | return 0; 1611 | } 1612 | else { 1613 | return sign; 1614 | } 1615 | } 1616 | if (x1 == 0) { 1617 | return -sign; 1618 | } 1619 | } 1620 | 1621 | } 1622 | } 1623 | 1624 | var structure = new Structure(); 1625 | -------------------------------------------------------------------------------- /colors.js: -------------------------------------------------------------------------------- 1 | for(var y=0; y<=100; y+=10) { 2 | 3 | for(var m=0; m<=100; m+=10) { 4 | for(var c=0; c<=100; c+=10) { 5 | var r = new Path.Rectangle(new Point(c*1.1 + y%30*13, m*1.1+ Math.floor(y/30)*130), new Size(10, 10)); 6 | r.fillColor = new CMYKColor(c/100, m/100, y/100, 0); 7 | r.strokeColor = null; 8 | } 9 | } 10 | 11 | } 12 | 13 | for(var y=0; y<=110; y+=10) { 14 | for(var m=0; m<=100; m+=10) { 15 | var text = new PointText(new Point( y%30*13 - 5, m*1.1 + Math.floor(y/30)*130+7) ); 16 | text.content = ""+m; 17 | text.characterStyle.fontSize = 6; 18 | text.paragraphStyle.justification = 'center'; 19 | } 20 | 21 | for(var c=0; c<=100; c+=10) { 22 | var text = new PointText(new Point( c*1.1 + y%30*13+5, Math.floor(y/30)*130-3) ); 23 | text.content = ""+c; 24 | text.characterStyle.fontSize = 6; 25 | text.paragraphStyle.justification = 'center'; 26 | } 27 | } --------------------------------------------------------------------------------