├── .DS_Store ├── .appcast.xml ├── .gitignore ├── Angle.playground ├── Contents.swift ├── Resources │ └── screen.png ├── contents.xcplayground ├── playground.xcworkspace │ └── contents.xcworkspacedata └── timeline.xctimeline ├── Angle.sketchplugin └── Contents │ ├── Resources │ ├── icon.png │ └── logo.png │ └── Sketch │ ├── apply.js │ ├── manifest.json │ ├── reset.js │ ├── rotate.js │ ├── symmetry.js │ └── symmetry.js~HEAD ├── README.md ├── assets ├── .DS_Store ├── icon.png └── logo.png ├── package-lock.json ├── package.json ├── src ├── Angle.js ├── CompositionAngle.js ├── CompressionRatio.js ├── Error.js ├── PixelDensity.js ├── ShapeAngle.js ├── SymbolicAngle.js ├── apply.js ├── manifest.json ├── reset.js ├── rotate.js ├── shared.js └── symmetry.js └── workspace.code-workspace /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Angle-Sketch-Plugin/c5145c524a2a463611fbb0aa6312943a861c6992/.DS_Store -------------------------------------------------------------------------------- /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Created by https://www.gitignore.io/api/node 4 | 5 | ### Node ### 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (http://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Typescript v1 declaration files 45 | typings/ 46 | 47 | # Optional npm cache directory 48 | .npm 49 | 50 | # Optional eslint cache 51 | .eslintcache 52 | 53 | # Optional REPL history 54 | .node_repl_history 55 | 56 | # Output of 'npm pack' 57 | *.tgz 58 | 59 | # Yarn Integrity file 60 | .yarn-integrity 61 | 62 | # dotenv environment variables file 63 | .env 64 | 65 | 66 | 67 | # End of https://www.gitignore.io/api/node 68 | 69 | 70 | # Created by https://www.gitignore.io/api/xcode 71 | 72 | ### Xcode ### 73 | # Xcode 74 | # 75 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 76 | 77 | ## Build generated 78 | build/ 79 | DerivedData/ 80 | 81 | ## Various settings 82 | *.pbxuser 83 | !default.pbxuser 84 | *.mode1v3 85 | !default.mode1v3 86 | *.mode2v3 87 | !default.mode2v3 88 | *.perspectivev3 89 | !default.perspectivev3 90 | xcuserdata/ 91 | 92 | ## Other 93 | *.moved-aside 94 | *.xccheckout 95 | *.xcscmblueprint 96 | 97 | ### Xcode Patch ### 98 | *.xcodeproj/* 99 | !*.xcodeproj/project.pbxproj 100 | !*.xcodeproj/xcshareddata/ 101 | !*.xcworkspace/contents.xcworkspacedata 102 | /*.gcno 103 | 104 | 105 | # End of https://www.gitignore.io/api/xcode 106 | 107 | Angle.sketchplugin 108 | .DS_Store -------------------------------------------------------------------------------- /Angle.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | import CoreImage 2 | import AppKit 3 | 4 | let screenImage = #imageLiteral(resourceName: "screen.png") 5 | let screenTiff = screenImage.tiffRepresentation! 6 | let screenbitmap = NSBitmapImageRep(data: screenTiff)! 7 | let screen = CIImage(bitmapImageRep: screenbitmap) 8 | 9 | let rect = ( 10 | topLeft: CGPoint(x: 1040.0, y: 1410.0 - 0.0), 11 | topRight: CGPoint(x: 1670.0, y: 1410.0 - 260.0), 12 | bottomRight: CGPoint(x: 650.0, y: 1410.0 - 1350.0), 13 | bottomLeft: CGPoint(x: 25.0, y: 1410.0 - 960.0) 14 | ) 15 | 16 | let perspectiveTransform = CIFilter(name: "CIPerspectiveTransform")! 17 | 18 | perspectiveTransform.setValue(CIVector(cgPoint: rect.topLeft), forKey: "inputTopLeft") 19 | perspectiveTransform.setValue(CIVector(cgPoint: rect.topRight), forKey: "inputTopRight") 20 | perspectiveTransform.setValue(CIVector(cgPoint: rect.bottomRight), forKey: "inputBottomRight") 21 | perspectiveTransform.setValue(CIVector(cgPoint: rect.bottomLeft), forKey: "inputBottomLeft") 22 | perspectiveTransform.setValue(screen, forKey: "inputImage") 23 | 24 | perspectiveTransform.value(forKey: "outputImage") 25 | 26 | let representation = NSCIImageRep(ciImage: perspectiveTransform.outputImage!) 27 | let image = NSImage(size: representation.size) 28 | image.addRepresentation(representation) 29 | -------------------------------------------------------------------------------- /Angle.playground/Resources/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Angle-Sketch-Plugin/c5145c524a2a463611fbb0aa6312943a861c6992/Angle.playground/Resources/screen.png -------------------------------------------------------------------------------- /Angle.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Angle.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Angle.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Angle.sketchplugin/Contents/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Angle-Sketch-Plugin/c5145c524a2a463611fbb0aa6312943a861c6992/Angle.sketchplugin/Contents/Resources/icon.png -------------------------------------------------------------------------------- /Angle.sketchplugin/Contents/Resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Angle-Sketch-Plugin/c5145c524a2a463611fbb0aa6312943a861c6992/Angle.sketchplugin/Contents/Resources/logo.png -------------------------------------------------------------------------------- /Angle.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "bundleVersion": 1, 3 | "version": "1.1.7", 4 | "icon": "icon.png", 5 | "identifier": "io.designcode.Angle", 6 | "description": "Important note: use version 1.1.7 for Sketch 86+, version 1.1.6 for Sketch 72-85, version 1.1.5 for Sketch 66-71 and version 1.1.4 for Sketch 65 and earlier. Apply perspective transforms on screen mockups. Auto-detect screens by resolution and works on shapes and symbols.", 7 | "homepage": "https://angle.sh", 8 | "author": "Design+Code", 9 | "authorEmail": "angle@designcode.io", 10 | "compatibleVersion": 50, 11 | "commands": [ 12 | { 13 | "name": "Rotate Mockup", 14 | "identifier": "Angle-Rotate-Mockup", 15 | "script": "__rotate.js", 16 | "shortcut": "control \\" 17 | }, 18 | { 19 | "name": "Flip Mockup", 20 | "identifier": "Angle-Rotate-Flip", 21 | "script": "__symmetry.js", 22 | "shortcut": "cmd shift \\" 23 | }, 24 | { 25 | "name": "Apply Mockup", 26 | "identifier": "Angle-Apply-Mockup", 27 | "script": "__apply.js", 28 | "shortcut": "cmd \\" 29 | }, 30 | { 31 | "name": "Reset Mockup", 32 | "identifier": "Angle-Reset-Metadata", 33 | "script": "__reset.js", 34 | "shortcut": "cmd control \\" 35 | } 36 | ], 37 | "menu": { 38 | "title": "Angle", 39 | "items": [ 40 | "Angle-Apply-Mockup", 41 | "Angle-Rotate-Flip", 42 | "Angle-Rotate-Mockup", 43 | "Angle-Reset-Metadata" 44 | ] 45 | }, 46 | "name": "Angle", 47 | "disableCocoaScriptPreprocessor": true, 48 | "appcast": "https://raw.githubusercontent.com/MengTo/Angle-Sketch-Plugin/master/.appcast.xml" 49 | } -------------------------------------------------------------------------------- /Angle.sketchplugin/Contents/Sketch/reset.js: -------------------------------------------------------------------------------- 1 | var that = this; 2 | function __skpm_run(e, t) { 3 | that.context = t; 4 | var r = (function (e) { 5 | var t = {}; 6 | function r(n) { 7 | if (t[n]) return t[n].exports; 8 | var o = (t[n] = { i: n, l: !1, exports: {} }); 9 | return e[n].call(o.exports, o, o.exports, r), (o.l = !0), o.exports; 10 | } 11 | return ( 12 | (r.m = e), 13 | (r.c = t), 14 | (r.d = function (e, t, n) { 15 | r.o(e, t) || 16 | Object.defineProperty(e, t, { 17 | configurable: !1, 18 | enumerable: !0, 19 | get: n, 20 | }); 21 | }), 22 | (r.n = function (e) { 23 | var t = 24 | e && e.__esModule 25 | ? function () { 26 | return e.default; 27 | } 28 | : function () { 29 | return e; 30 | }; 31 | return r.d(t, "a", t), t; 32 | }), 33 | (r.o = function (e, t) { 34 | return Object.prototype.hasOwnProperty.call(e, t); 35 | }), 36 | (r.p = ""), 37 | r((r.s = 3)) 38 | ); 39 | })([ 40 | function (e, r, n) { 41 | Object.defineProperty(r, "__esModule", { value: !0 }); 42 | var o = (function () { 43 | function e(e, t) { 44 | for (var r = 0; r < t.length; r++) { 45 | var n = t[r]; 46 | (n.enumerable = n.enumerable || !1), 47 | (n.configurable = !0), 48 | "value" in n && (n.writable = !0), 49 | Object.defineProperty(e, n.key, n); 50 | } 51 | } 52 | return function (t, r, n) { 53 | return r && e(t.prototype, r), n && e(t, n), t; 54 | }; 55 | })(), 56 | i = n(4); 57 | function a(e) { 58 | if (Array.isArray(e)) { 59 | for (var t = 0, r = Array(e.length); t < e.length; t++) r[t] = e[t]; 60 | return r; 61 | } 62 | return Array.from(e); 63 | } 64 | var s = { linear: 0, quadratic: 1, cubic: 2 }; 65 | Array.prototype.rotated = function (e) { 66 | return this.slice(e, this.length).concat(this.slice(0, e)); 67 | }; 68 | var u = (function () { 69 | function e() { 70 | var t = 71 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 72 | !(function (e, t) { 73 | if (!(e instanceof t)) 74 | throw new TypeError("Cannot call a class as a function"); 75 | })(this, e), 76 | (this.context = t.context), 77 | (this.selectedLayer = t.selectedLayer); 78 | } 79 | return ( 80 | o(e, [ 81 | { 82 | key: "imprintValue_forKey", 83 | value: (function () { 84 | return function (e, t) { 85 | null != this.selectedLayer 86 | ? this.context.command.setValue_forKey_onLayer( 87 | e, 88 | t, 89 | this.selectedLayer 90 | ) 91 | : print( 92 | "🛑 Imprinting value before selected layer assignment" 93 | ); 94 | }; 95 | })(), 96 | }, 97 | { 98 | key: "loadValueForKey", 99 | value: (function () { 100 | return function (e) { 101 | return null == this.selectedLayer 102 | ? (print( 103 | "🛑 Loading value before selected layer assignment" 104 | ), 105 | null) 106 | : this.context.command.valueForKey_onLayer( 107 | e, 108 | this.selectedLayer 109 | ); 110 | }; 111 | })(), 112 | }, 113 | { 114 | key: "artboardID", 115 | get: (function () { 116 | return function () { 117 | return void 0 != this._artboardID 118 | ? this._artboardID 119 | : ((this._artboardID = 120 | this.loadValueForKey("artboard-id") + ""), 121 | this._artboardID); 122 | }; 123 | })(), 124 | set: (function () { 125 | return function (e) {}; 126 | })(), 127 | }, 128 | { 129 | key: "artboard", 130 | get: (function () { 131 | return function () { 132 | if (void 0 != this._artboard) return this._artboard; 133 | if (void 0 != this.artboardID) { 134 | for ( 135 | var e = this.context.document.artboards(), t = 0; 136 | t < e.count(); 137 | t++ 138 | ) 139 | e[t].objectID() == this.artboardID && 140 | (this._artboard = e[t]); 141 | if (void 0 != this._artboard) return this._artboard; 142 | print( 143 | "🛑 Not able to retrieve artboard from id in document" 144 | ); 145 | } else print("🛑 No artboard ID registered"); 146 | }; 147 | })(), 148 | set: (function () { 149 | return function (e) { 150 | (this._artboard = e), 151 | this.imprintValue_forKey(e.objectID(), "artboard-id"); 152 | }; 153 | })(), 154 | }, 155 | { 156 | key: "rotation", 157 | get: (function () { 158 | return function () { 159 | return ( 160 | void 0 == this._rotation && 161 | (this._rotation = this.loadValueForKey("rotation")), 162 | this._rotation 163 | ); 164 | }; 165 | })(), 166 | set: (function () { 167 | return function (e) { 168 | (this._rotation = e), this.imprintValue_forKey(e, "rotation"); 169 | }; 170 | })(), 171 | }, 172 | { 173 | key: "pixelDensity", 174 | get: (function () { 175 | return function () { 176 | if ( 177 | (void 0 == this._pixelDensity && 178 | (this._pixelDensity = 179 | this.loadValueForKey("pixel-density") + 0), 180 | 0 == this._pixelDensity) 181 | ) { 182 | var e = Math.round(2 * this.estimatePixelDensity() + 0.5); 183 | return 0 == e ? 1 : e; 184 | } 185 | return this._pixelDensity; 186 | }; 187 | })(), 188 | set: (function () { 189 | return function (e) { 190 | (this._pixelDensity = e), 191 | this.imprintValue_forKey(e, "pixel-density"); 192 | }; 193 | })(), 194 | }, 195 | { 196 | key: "compressionRatio", 197 | get: (function () { 198 | return function () { 199 | return ( 200 | void 0 == this._compressionRatio && 201 | (this._compressionRatio = 202 | this.loadValueForKey("compression-ratio") + 0), 203 | this._compressionRatio 204 | ); 205 | }; 206 | })(), 207 | set: (function () { 208 | return function (e) { 209 | (this._compressionRatio = e), 210 | this.imprintValue_forKey(e, "compression-ratio"); 211 | }; 212 | })(), 213 | }, 214 | { 215 | key: "reversed", 216 | get: (function () { 217 | return function () { 218 | return ( 219 | void 0 == this._reversed && 220 | (this._reversed = 1 == this.loadValueForKey("reversed")), 221 | this._reversed 222 | ); 223 | }; 224 | })(), 225 | set: (function () { 226 | return function (e) { 227 | (this._reversed = e), this.imprintValue_forKey(e, "reversed"); 228 | }; 229 | })(), 230 | }, 231 | ]), 232 | o(e, [ 233 | { 234 | key: "exportRequest_lessThan52", 235 | value: (function () { 236 | return function () { 237 | var e = NSClassFromString( 238 | "SketchModel.MSImmutableLayerAncestry" 239 | ) 240 | .alloc() 241 | .initWithMutableLayer(t.selection[0]), 242 | r = MSExportFormat.formatWithScale_name_fileFormat( 243 | this.pixelDensity, 244 | "Angle", 245 | "png" 246 | ); 247 | return MSExportRequest.exportRequestsFromLayerAncestry_exportFormats( 248 | e, 249 | [r] 250 | ).firstObject(); 251 | }; 252 | })(), 253 | }, 254 | { 255 | key: "exporter", 256 | value: (function () { 257 | return function () { 258 | var e = this.context.document.colorSpace(); 259 | if (BCSketchInfo.shared().metadata().appVersion < 52) 260 | return MSExporter.exporterForRequest_colorSpace( 261 | this.exportRequest_lessThan52(), 262 | e 263 | ); 264 | var t = MSExportFormat.alloc().init(); 265 | (t.fileFormat = "png"), (t.scale = this.pixelDensity); 266 | var r = 267 | MSExportRequest.exportRequestsFromExportableLayer_exportFormats_useIDForName( 268 | this.artboard, 269 | [t], 270 | !0 271 | ).firstObject(); 272 | return MSExporter.exporterForRequest_colorSpace(r, e); 273 | }; 274 | })(), 275 | }, 276 | { 277 | key: "ciImage", 278 | value: (function () { 279 | return function () { 280 | var e = this.exporter().bitmapImageRep(); 281 | return CIImage.alloc().initWithCGImage(e.CGImage()); 282 | }; 283 | })(), 284 | }, 285 | { 286 | key: "guessRotationAndReversion", 287 | value: (function () { 288 | return function () { 289 | if (1 === this.loadValueForKey("guessed-rotation")) 290 | print( 291 | "⚠️ Angle has already guessed rotation and symmetry for this shape" 292 | ); 293 | else { 294 | var e = this.verticesLengths, 295 | t = void 0; 296 | if (this.artboard.class() != MSSymbolMaster) { 297 | t = this.artboard.frame(); 298 | var r = e[0] > e[1], 299 | n = t.width() > t.height(); 300 | r && (print("🛑 HORIZONTAL"), this.rotate()), 301 | n && 302 | (print("🛑 HAS HORIZONTAL ARTBOARD"), this.rotate()); 303 | var o = this.pointsFromBezierPath, 304 | i = Math.min.apply( 305 | Math, 306 | a( 307 | o.map(function (e) { 308 | return e.y; 309 | }) 310 | ) 311 | ), 312 | s = o[this.mappedIndexFor(0)], 313 | u = o[this.mappedIndexFor(1)]; 314 | s.y != i && 315 | u.y != i && 316 | (print("🛑 UPSIDE DOWN"), this.rotate(), this.rotate()); 317 | var l = BCSketchInfo.shared().metadata().appVersion; 318 | if (l < 50 || l >= 52) { 319 | var c = this.shorlaceSum(); 320 | c < 0 321 | ? (print("🛑 COUNTERCLOCKWISE"), 322 | this.reverseSymmetry()) 323 | : c > 0 324 | ? print("🛑 CLOCKWISE") 325 | : print("🛑 UNDEFINED CHIRALITY"); 326 | } else 327 | l < 52 && 328 | (1 === 329 | this.targetPath.contours().firstObject().isClockwise() 330 | ? (this.reverseSymmetry(), print("🛑 CLOCKWISE")) 331 | : print("🛑 COUNTERCLOCKWISE"), 332 | print("🛑 UNDEFINED CHIRALITY")); 333 | print( 334 | "🔄↔️ Angle has just guessed rotation and symmetry for this shape" 335 | ), 336 | this.imprintValue_forKey(!0, "guessed-rotation"); 337 | } 338 | } 339 | }; 340 | })(), 341 | }, 342 | { 343 | key: "shorlaceSum", 344 | value: (function () { 345 | return function () { 346 | var e = this.pointsFromBezierPath, 347 | t = Math.max.apply( 348 | Math, 349 | a( 350 | e.map(function (e) { 351 | return e.y; 352 | }) 353 | ) 354 | ); 355 | return Array.from({ length: 4 }, function (e, t) { 356 | return t; 357 | }).reduce(function (r, n) { 358 | return ( 359 | r + 360 | (-e[n].x + e[(n + 1) % 4].x) * 361 | (2 * t - e[n].y - e[(n + 1) % 4].y) 362 | ); 363 | }, 0); 364 | }; 365 | })(), 366 | }, 367 | { 368 | key: "maximumVerticesWidthAndHeight", 369 | value: (function () { 370 | return function () { 371 | var e = this.verticesLengths.rotated(this.rotation % 2); 372 | return [Math.max(e[0], e[2]), Math.max(e[1], e[3])]; 373 | }; 374 | })(), 375 | }, 376 | { 377 | key: "rotate", 378 | value: (function () { 379 | return function () { 380 | this.rotation = (this.rotation + (this.reversed ? 1 : 3)) % 4; 381 | }; 382 | })(), 383 | }, 384 | { 385 | key: "reverseSymmetry", 386 | value: (function () { 387 | return function () { 388 | this.rotate(), (this.reversed = !this.reversed); 389 | }; 390 | })(), 391 | }, 392 | { 393 | key: "mappedIndexFor", 394 | value: (function () { 395 | return function (e) { 396 | return this.reversed 397 | ? [0, 3, 2, 1][(e + this.rotation) % 4] 398 | : (e + this.rotation) % 4; 399 | }; 400 | })(), 401 | }, 402 | { 403 | key: "lossyCompressionOfImage_atRate", 404 | value: (function () { 405 | return function (e, t) { 406 | var r = NSBitmapImageRep.alloc().initWithCIImage(e), 407 | n = NSMutableDictionary.dictionary(); 408 | n.setObject_forKey( 409 | NSTIFFCompressionJPEG, 410 | NSImageCompressionMethod 411 | ), 412 | n.setObject_forKey(t, NSImageCompressionFactor), 413 | n.setObject_forKey( 414 | NSColor.whiteColor(), 415 | NSImageFallbackBackgroundColor 416 | ); 417 | var o = r.representationUsingType_properties( 418 | NSJPEGFileType, 419 | n 420 | ); 421 | return NSImage.alloc().initWithData(o); 422 | }; 423 | })(), 424 | }, 425 | { 426 | key: "pixelAccurateRepresentationOfImage", 427 | value: (function () { 428 | return function (e) { 429 | var t = NSCIImageRep.alloc().initWithCIImage(e), 430 | r = NSImage.alloc().initWithSize(t.size()); 431 | return r.addRepresentation(t), r; 432 | }; 433 | })(), 434 | }, 435 | { 436 | key: "pointsAreValid_lessThan50", 437 | get: (function () { 438 | return function () { 439 | var e = this.pointsFromBezierPath; 440 | return null !== e && 7 === e.length; 441 | }; 442 | })(), 443 | }, 444 | { 445 | key: "pointsAreValid_lessThan52", 446 | get: (function () { 447 | return function () { 448 | var e = this.targetPath.contours().firstObject(), 449 | t = Array.fromNSArray(e.segments()); 450 | return ( 451 | null !== t && 452 | 4 === t.length && 453 | !t.some(function (e) { 454 | return e.segmentType() != s.linear; 455 | }) 456 | ); 457 | }; 458 | })(), 459 | }, 460 | { 461 | key: "pointsAreValid", 462 | get: (function () { 463 | return function () { 464 | var e = BCSketchInfo.shared().metadata().appVersion; 465 | if (e < 50) return this.pointsAreValid_lessThan50; 466 | if (e < 52) return this.pointsAreValid_lessThan52; 467 | var t = this.targetLayer.points(); 468 | return ( 469 | null !== t && 470 | 4 === t.length && 471 | !t.some(function (e) { 472 | return !e.isStraight(); 473 | }) 474 | ); 475 | }; 476 | })(), 477 | }, 478 | { 479 | key: "pointsFromBezierPath", 480 | get: (function () { 481 | return function () { 482 | var e = this, 483 | t = BCSketchInfo.shared().metadata().appVersion; 484 | if (t < 50) { 485 | var r = this.targetPath.elementCount(); 486 | return 7 != r 487 | ? null 488 | : Array.from({ length: r }, function (e, t) { 489 | return t; 490 | }).map(function (t) { 491 | var r = MOPointer.alloc().initWithValue_( 492 | CGPointMake(0, 0) 493 | ); 494 | return ( 495 | e.targetPath.elementAtIndex_associatedPoints_(t, r), 496 | r.value() 497 | ); 498 | }); 499 | } 500 | if (t < 52) { 501 | var n = this.targetPath.contours().firstObject(); 502 | return Array.fromNSArray(n.segments()).map(function (e) { 503 | return e.endPoint1(); 504 | }); 505 | } 506 | var o = this.targetLayer.rect().size; 507 | return Array.fromNSArray(this.targetLayer.points()) 508 | .map(function (e) { 509 | return e.point(); 510 | }) 511 | .map(function (e) { 512 | return { 513 | x: Number(e.x) * Number(o.width), 514 | y: Number(e.y) * Number(o.height), 515 | }; 516 | }); 517 | }; 518 | })(), 519 | }, 520 | { 521 | key: "verticesLengths", 522 | get: (function () { 523 | return function () { 524 | var e = this.pointsFromBezierPath; 525 | return Array.from({ length: 4 }, function (e, t) { 526 | return t; 527 | }).map(function (t) { 528 | var r = (t + 1) % 4, 529 | n = e[t].x - e[r].x, 530 | o = e[t].y - e[r].y; 531 | return Math.sqrt(Math.pow(n, 2) + Math.pow(o, 2)); 532 | }); 533 | }; 534 | })(), 535 | }, 536 | { 537 | key: "normalizedCIVectors", 538 | get: (function () { 539 | return function () { 540 | var e = this.pointsFromBezierPath, 541 | t = Math.max.apply( 542 | Math, 543 | a( 544 | e.map(function (e) { 545 | return e.y; 546 | }) 547 | ) 548 | ), 549 | r = this.pixelDensity; 550 | return e.map(function (e) { 551 | return CIVector.vectorWithX_Y(e.x * r, (t - e.y) * r); 552 | }); 553 | }; 554 | })(), 555 | }, 556 | { 557 | key: "transformedImage", 558 | get: (function () { 559 | return function () { 560 | var e = this.normalizedCIVectors, 561 | t = CIFilter.filterWithName("CIPerspectiveTransform"); 562 | t.setValue_forKey(e[this.mappedIndexFor(0)], "inputTopLeft"), 563 | t.setValue_forKey( 564 | e[this.mappedIndexFor(1)], 565 | "inputTopRight" 566 | ), 567 | t.setValue_forKey( 568 | e[this.mappedIndexFor(2)], 569 | "inputBottomRight" 570 | ), 571 | t.setValue_forKey( 572 | e[this.mappedIndexFor(3)], 573 | "inputBottomLeft" 574 | ); 575 | var r = this.ciImage(); 576 | t.setValue_forKey(r, "inputImage"); 577 | var n = t.valueForKey("outputImage"); 578 | if (n) { 579 | var o = void 0, 580 | a = i.CompressionRatio[this.compressionRatio].ratio; 581 | return ( 582 | (o = 583 | 1 != a 584 | ? this.lossyCompressionOfImage_atRate(n, a) 585 | : this.pixelAccurateRepresentationOfImage(n)), 586 | BCSketchInfo.shared().metadata().appVersion < 47 587 | ? MSImageData.alloc().initWithImage_convertColorSpace( 588 | o, 589 | !0 590 | ) 591 | : MSImageData.alloc().initWithImage_(o) 592 | ); 593 | } 594 | print("🛑 Unable to form perspective image"); 595 | }; 596 | })(), 597 | }, 598 | ]), 599 | e 600 | ); 601 | })(); 602 | r.default = u; 603 | }, 604 | function (e, t) { 605 | Object.defineProperty(t, "__esModule", { value: !0 }); 606 | t.Error = { 607 | unsupportedSymbol: { 608 | message: "This does not seem to be a supported symbol.", 609 | }, 610 | unsupportedShapePath: { 611 | message: 612 | "There seems to be an issue with the shape we are trying to apply.", 613 | }, 614 | emptySelection: { 615 | message: "Please, select a Shape, Angle Mockup or Angle Composition", 616 | }, 617 | unsupportedElement: { 618 | message: "Please, select a Shape, Angle Mockup or Angle Composition", 619 | }, 620 | noImageOverrideOnSymbol: { 621 | message: "There is no image override for the selected symbol", 622 | }, 623 | symbolWithBitMapLayer: { 624 | message: "Bitmat overrides are not supported", 625 | }, 626 | }; 627 | }, 628 | function (e, t, r) { 629 | Object.defineProperty(t, "__esModule", { value: !0 }); 630 | var n, 631 | o = (function () { 632 | return function (e, t) { 633 | if (Array.isArray(e)) return e; 634 | if (Symbol.iterator in Object(e)) 635 | return (function (e, t) { 636 | var r = [], 637 | n = !0, 638 | o = !1, 639 | i = void 0; 640 | try { 641 | for ( 642 | var a, s = e[Symbol.iterator](); 643 | !(n = (a = s.next()).done) && 644 | (r.push(a.value), !t || r.length !== t); 645 | n = !0 646 | ); 647 | } catch (e) { 648 | (o = !0), (i = e); 649 | } finally { 650 | try { 651 | !n && s.return && s.return(); 652 | } finally { 653 | if (o) throw i; 654 | } 655 | } 656 | return r; 657 | })(e, t); 658 | throw new TypeError( 659 | "Invalid attempt to destructure non-iterable instance" 660 | ); 661 | }; 662 | })(), 663 | i = (function () { 664 | function e(e, t) { 665 | for (var r = 0; r < t.length; r++) { 666 | var n = t[r]; 667 | (n.enumerable = n.enumerable || !1), 668 | (n.configurable = !0), 669 | "value" in n && (n.writable = !0), 670 | Object.defineProperty(e, n.key, n); 671 | } 672 | } 673 | return function (t, r, n) { 674 | return r && e(t.prototype, r), n && e(t, n), t; 675 | }; 676 | })(), 677 | a = r(0), 678 | s = (n = a) && n.__esModule ? n : { default: n }, 679 | u = r(1); 680 | function l(e, t) { 681 | if (!e) 682 | throw new ReferenceError( 683 | "this hasn't been initialised - super() hasn't been called" 684 | ); 685 | return !t || ("object" != typeof t && "function" != typeof t) ? e : t; 686 | } 687 | var c = (function (e) { 688 | function t() { 689 | var e = 690 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 691 | !(function (e, t) { 692 | if (!(e instanceof t)) 693 | throw new TypeError("Cannot call a class as a function"); 694 | })(this, t); 695 | var r = l( 696 | this, 697 | (t.__proto__ || Object.getPrototypeOf(t)).call(this, e) 698 | ); 699 | if ( 700 | ((r.targetLayer = e.override.affectedLayer()), 701 | r.targetLayer.class() === MSImmutableBitmapLayer) 702 | ) 703 | return l(r, u.Error.symbolWithBitMapLayer); 704 | var n = BCSketchInfo.shared().metadata().appVersion; 705 | n < 50 706 | ? (r.targetPath = e.override.affectedLayer().bezierPath()) 707 | : n < 52 && 708 | (r.targetPath = e.override 709 | .affectedLayer() 710 | .pathInFrameWithTransforms()); 711 | var o = e.override.overridePoint().parent(); 712 | return ( 713 | null !== o && (r.overrideLayer = o), 714 | r.pointsAreValid ? r : l(r, u.Error.unsupportedShapePath) 715 | ); 716 | } 717 | return ( 718 | (function (e, t) { 719 | if ("function" != typeof t && null !== t) 720 | throw new TypeError( 721 | "Super expression must either be null or a function, not " + 722 | typeof t 723 | ); 724 | (e.prototype = Object.create(t && t.prototype, { 725 | constructor: { 726 | value: e, 727 | enumerable: !1, 728 | writable: !0, 729 | configurable: !0, 730 | }, 731 | })), 732 | t && 733 | (Object.setPrototypeOf 734 | ? Object.setPrototypeOf(e, t) 735 | : (e.__proto__ = t)); 736 | })(t, s["default"]), 737 | i(t, [ 738 | { 739 | key: "applyImage", 740 | value: (function () { 741 | return function () { 742 | var e = this.targetLayer.objectID(), 743 | t = 744 | this.selectedLayer.overrides() || 745 | NSDictionary.dictionary(), 746 | r = NSMutableDictionary.dictionaryWithDictionary(t); 747 | r.setObject_forKey(this.transformedImage, e), 748 | (this.selectedLayer.overrides = r); 749 | }; 750 | })(), 751 | }, 752 | { 753 | key: "estimatePixelDensity", 754 | value: (function () { 755 | return function () { 756 | var e = this.maximumVerticesWidthAndHeight(), 757 | t = o(e, 2), 758 | r = t[0], 759 | n = t[1], 760 | i = 761 | (this.selectedLayer.rect().size.width * r) / 762 | (this.selectedLayer.naturalSize().width * 763 | this.artboard.rect().size.width), 764 | a = 765 | (this.selectedLayer.rect().size.height * n) / 766 | (this.selectedLayer.naturalSize().height * 767 | this.artboard.rect().size.height); 768 | return i > a ? i : a; 769 | }; 770 | })(), 771 | }, 772 | { 773 | key: "description", 774 | value: (function () { 775 | return function () { 776 | return ( 777 | this.selectedLayer.name() + " " + this.targetLayer.name() 778 | ); 779 | }; 780 | })(), 781 | }, 782 | ]), 783 | t 784 | ); 785 | })(); 786 | t.default = c; 787 | }, 788 | function (e, r, n) { 789 | Object.defineProperty(r, "__esModule", { value: !0 }), 790 | (r.default = function (e) { 791 | var r = e.document, 792 | n = e.selection, 793 | o = e.command; 794 | if (void 0 != n && 1 == n.count()) { 795 | var i = n.firstObject(); 796 | a.default.tryCreating({ 797 | for: [i], 798 | in: { document: r, selection: n, command: o }, 799 | }) instanceof a.default 800 | ? (t.command.setValue_forKey_onLayer(null, "pixel-density", i), 801 | t.command.setValue_forKey_onLayer(null, "rotation", i), 802 | t.command.setValue_forKey_onLayer(null, "artboard-id", i), 803 | t.command.setValue_forKey_onLayer(null, "compression-ratio", i), 804 | t.command.setValue_forKey_onLayer(null, "reversed", i), 805 | t.command.setValue_forKey_onLayer(null, "guessed-rotation", i), 806 | s.show({ 807 | message: "Angle Mockup metadata reset.", 808 | inDocument: r, 809 | })) 810 | : s.show({ 811 | message: "Reset only works on shapes and symbols.", 812 | inDocument: r, 813 | }); 814 | } else 815 | s.show({ 816 | message: "Please, select 1️⃣ element to reset", 817 | inDocument: r, 818 | }); 819 | }); 820 | var o, 821 | i = n(0), 822 | a = (o = i) && o.__esModule ? o : { default: o }, 823 | s = (function (e) { 824 | if (e && e.__esModule) return e; 825 | var t = {}; 826 | if (null != e) 827 | for (var r in e) 828 | Object.prototype.hasOwnProperty.call(e, r) && (t[r] = e[r]); 829 | return (t.default = e), t; 830 | })(n(5)); 831 | }, 832 | function (e, t) { 833 | Object.defineProperty(t, "__esModule", { value: !0 }); 834 | t.CompressionRatio = [ 835 | { selectionLabel: "Best", ratio: 1 }, 836 | { selectionLabel: "Better", ratio: 0.9 }, 837 | { selectionLabel: "Good", ratio: 0.8 }, 838 | { selectionLabel: "Average", ratio: 0.7 }, 839 | ]; 840 | }, 841 | function (e, t, r) { 842 | Object.defineProperty(t, "__esModule", { value: !0 }), 843 | (t.show = function (e) { 844 | var t = e.message, 845 | r = e.inDocument; 846 | void 0 != r && void 0 != r.showMessage && r.showMessage(t); 847 | print(t); 848 | }), 849 | (t.filterPossibleArtboards = function (e) { 850 | var t = e.class(); 851 | switch (t) { 852 | case MSArtboardGroup: 853 | var r = e, 854 | n = r.frame(); 855 | if (n.width() < 250 || n.height() < 250) return !1; 856 | var o = n.width() / n.height(); 857 | if ((o > 1 && (o = 1 / o), o < 0.4)) return !1; 858 | break; 859 | case MSSymbolMaster: 860 | return !1; 861 | default: 862 | return print(t), !1; 863 | } 864 | return !0; 865 | }), 866 | (t.compareByRatioAndAlphabet = function (e, t) { 867 | var r = e.frame(), 868 | n = t.frame(), 869 | o = r.width() / r.height(); 870 | o > 1 && (o = 1 / o); 871 | var i = o > 0.4 && o < 0.8, 872 | a = n.width() / n.height(); 873 | a > 1 && (a = 1 / a); 874 | var s = a > 0.4 && a < 0.8; 875 | if (i && !s) return !1; 876 | if (s && !i) return !0; 877 | if (o == a) return e.name() > t.name(); 878 | return o > a; 879 | }), 880 | (t.introspect = function (e) { 881 | var t = e.class().mocha(); 882 | print("-----------------------------------------------"), 883 | print("PROPERTIES-------------------------------------"), 884 | print("-----------------------------------------------"), 885 | print(t.properties()), 886 | print(t.propertiesWithAncestors()), 887 | print("-----------------------------------------------"), 888 | print("INSTANCE METHODS-------------------------------"), 889 | print("-----------------------------------------------"), 890 | print(t.instanceMethods()), 891 | print(t.instanceMethodsWithAncestors()), 892 | print("-----------------------------------------------"), 893 | print("CLASS METHODS----------------------------------"), 894 | print("-----------------------------------------------"), 895 | print(t.classMethods()), 896 | print(t.classMethodsWithAncestors()), 897 | print("-----------------------------------------------"), 898 | print("PROTOCOLS--------------------------------------"), 899 | print("-----------------------------------------------"), 900 | print(t.protocols()), 901 | print(t.protocolsWithAncestors()); 902 | }), 903 | (t.createLabel = function (e, t, r) { 904 | var n = NSTextField.alloc().initWithFrame(r); 905 | return ( 906 | n.setStringValue(e), 907 | n.setFont(NSFont.boldSystemFontOfSize(t)), 908 | n.setBezeled(!1), 909 | n.setDrawsBackground(!1), 910 | n.setEditable(!1), 911 | n.setSelectable(!1), 912 | n 913 | ); 914 | }), 915 | (t.popUpButtonsforRectangleIndexer_withTitleIndexer_andImageIndexer_defaultSelected_onIndex = 916 | function (e, t, r, n) { 917 | var o = NSPopUpButton.alloc().initWithFrame(e(n)); 918 | o.addItemsWithTitles(t), 919 | null != r && 920 | ((o.imageScaling = NSImageScaleProportionallyUpOrDown), 921 | Array.fromNSArray(o.itemArray()).forEach(function (e, t, n) { 922 | e.image = r[t]; 923 | })); 924 | return o; 925 | }), 926 | (t.smallImagesFromArtboard = function (e) { 927 | if (e.class() == MSSymbolMaster) return print(e), null; 928 | void 0 == e.frame && print(e); 929 | var t = e.frame().width(), 930 | r = e.frame().height(), 931 | n = t / r; 932 | n > 1 && (n = 1 / n); 933 | if (n > 0.8 || n < 0.4) return null; 934 | var o = NSClassFromString("SketchModel.MSImmutableLayerAncestry") 935 | .alloc() 936 | .initWithMutableLayer(e), 937 | i = 48 / (t > r ? t : r), 938 | a = MSExportFormat.formatWithScale_name_fileFormat(i, "", "png"), 939 | s = MSExportRequest.exportRequestsFromLayerAncestry_exportFormats( 940 | o, 941 | [a] 942 | ).firstObject(); 943 | return MSExporter.exporterForRequest_colorSpace( 944 | s, 945 | NSColorSpace.sRGBColorSpace() 946 | ).previewImage(); 947 | }); 948 | var n = u(r(0)), 949 | o = u(r(6)), 950 | i = u(r(2)), 951 | a = u(r(7)), 952 | s = r(1); 953 | function u(e) { 954 | return e && e.__esModule ? e : { default: e }; 955 | } 956 | (n.default.tryCreating = function (e) { 957 | var t = e.for, 958 | r = e.in; 959 | return t 960 | .map(function (e) { 961 | switch (e.class()) { 962 | case MSSymbolInstance: 963 | var t = Array.fromNSArray(e.availableOverrides()) || [], 964 | n = t 965 | .filter(function (e) { 966 | return e.currentValue().class() == MSImageData; 967 | }) 968 | .map(function (t) { 969 | return new i.default({ 970 | selectedLayer: e, 971 | context: r, 972 | override: t, 973 | }); 974 | }), 975 | u = t 976 | .map(function (e) { 977 | return e.children(); 978 | }) 979 | .filter(function (e) { 980 | return null != e; 981 | }) 982 | .map(Array.fromNSArray) 983 | .reduce(function (e, t) { 984 | return e.concat(t); 985 | }, []) 986 | .filter(function (e) { 987 | return e.class() == MSAvailableOverride; 988 | }) 989 | .map(function (t) { 990 | return new o.default({ 991 | override: t, 992 | selectedLayer: e, 993 | context: r, 994 | }); 995 | }); 996 | return n.concat(u); 997 | case MSShapeGroup: 998 | case MSShapePathLayer: 999 | case MSRectangleShape: 1000 | return new a.default({ selectedLayer: e, context: r }); 1001 | default: 1002 | return s.Error.unsupportedElement; 1003 | } 1004 | }) 1005 | .reduce(function (e, t, r, n) { 1006 | return e.concat(t); 1007 | }, []); 1008 | }), 1009 | (Array.fromNSArray = function (e) { 1010 | for (var t = [], r = 0; r < e.length; r++) t.push(e[r]); 1011 | return t; 1012 | }), 1013 | (Array.prototype.print = function () { 1014 | return this.map(function (e) { 1015 | return print(e), e; 1016 | }); 1017 | }); 1018 | }, 1019 | function (e, t, r) { 1020 | Object.defineProperty(t, "__esModule", { value: !0 }); 1021 | var n, 1022 | o = (function () { 1023 | function e(e, t) { 1024 | for (var r = 0; r < t.length; r++) { 1025 | var n = t[r]; 1026 | (n.enumerable = n.enumerable || !1), 1027 | (n.configurable = !0), 1028 | "value" in n && (n.writable = !0), 1029 | Object.defineProperty(e, n.key, n); 1030 | } 1031 | } 1032 | return function (t, r, n) { 1033 | return r && e(t.prototype, r), n && e(t, n), t; 1034 | }; 1035 | })(), 1036 | i = (function () { 1037 | return function e(t, r, n) { 1038 | null === t && (t = Function.prototype); 1039 | var o = Object.getOwnPropertyDescriptor(t, r); 1040 | if (void 0 === o) { 1041 | var i = Object.getPrototypeOf(t); 1042 | return null === i ? void 0 : e(i, r, n); 1043 | } 1044 | if ("value" in o) return o.value; 1045 | var a = o.get; 1046 | return void 0 !== a ? a.call(n) : void 0; 1047 | }; 1048 | })(), 1049 | a = r(2), 1050 | s = (n = a) && n.__esModule ? n : { default: n }; 1051 | r(1); 1052 | var u = (function (e) { 1053 | function t() { 1054 | var e = 1055 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 1056 | !(function (e, t) { 1057 | if (!(e instanceof t)) 1058 | throw new TypeError("Cannot call a class as a function"); 1059 | })(this, t); 1060 | var r = (function (e, t) { 1061 | if (!e) 1062 | throw new ReferenceError( 1063 | "this hasn't been initialised - super() hasn't been called" 1064 | ); 1065 | return !t || ("object" != typeof t && "function" != typeof t) 1066 | ? e 1067 | : t; 1068 | })(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)); 1069 | return (r.override = e.override), r; 1070 | } 1071 | return ( 1072 | (function (e, t) { 1073 | if ("function" != typeof t && null !== t) 1074 | throw new TypeError( 1075 | "Super expression must either be null or a function, not " + 1076 | typeof t 1077 | ); 1078 | (e.prototype = Object.create(t && t.prototype, { 1079 | constructor: { 1080 | value: e, 1081 | enumerable: !1, 1082 | writable: !0, 1083 | configurable: !0, 1084 | }, 1085 | })), 1086 | t && 1087 | (Object.setPrototypeOf 1088 | ? Object.setPrototypeOf(e, t) 1089 | : (e.__proto__ = t)); 1090 | })(t, s["default"]), 1091 | o(t, [ 1092 | { 1093 | key: "loadValueForKey", 1094 | value: (function () { 1095 | return function (e) { 1096 | return i( 1097 | t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), 1098 | "loadValueForKey", 1099 | this 1100 | ).call(this, this.targetLayer.objectID() + "-" + e); 1101 | }; 1102 | })(), 1103 | }, 1104 | { 1105 | key: "imprintValue_forKey", 1106 | value: (function () { 1107 | return function (e, r) { 1108 | i( 1109 | t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), 1110 | "imprintValue_forKey", 1111 | this 1112 | ).call(this, e, this.targetLayer.objectID() + "-" + r); 1113 | }; 1114 | })(), 1115 | }, 1116 | { 1117 | key: "applyImage", 1118 | value: (function () { 1119 | return function () { 1120 | var e = 1121 | this.selectedLayer.overrides() || 1122 | NSDictionary.dictionary(), 1123 | t = NSMutableDictionary.dictionaryWithDictionary(e), 1124 | r = (this.overrideLayer + "").replace("_symbolID", ""), 1125 | n = t.objectForKey(r) || NSMutableDictionary.dictionary(), 1126 | o = NSMutableDictionary.dictionaryWithDictionary(n), 1127 | i = this.targetLayer.objectID(); 1128 | o.setObject_forKey(this.transformedImage, i), 1129 | t.setObject_forKey(o, r), 1130 | (this.selectedLayer.overrides = t); 1131 | }; 1132 | })(), 1133 | }, 1134 | { 1135 | key: "description", 1136 | value: (function () { 1137 | return function () { 1138 | return ( 1139 | this.override.parent().affectedLayer().name() + 1140 | " " + 1141 | this.targetLayer.name() 1142 | ); 1143 | }; 1144 | })(), 1145 | }, 1146 | ]), 1147 | t 1148 | ); 1149 | })(); 1150 | t.default = u; 1151 | }, 1152 | function (e, t, r) { 1153 | Object.defineProperty(t, "__esModule", { value: !0 }); 1154 | var n, 1155 | o = (function () { 1156 | return function (e, t) { 1157 | if (Array.isArray(e)) return e; 1158 | if (Symbol.iterator in Object(e)) 1159 | return (function (e, t) { 1160 | var r = [], 1161 | n = !0, 1162 | o = !1, 1163 | i = void 0; 1164 | try { 1165 | for ( 1166 | var a, s = e[Symbol.iterator](); 1167 | !(n = (a = s.next()).done) && 1168 | (r.push(a.value), !t || r.length !== t); 1169 | n = !0 1170 | ); 1171 | } catch (e) { 1172 | (o = !0), (i = e); 1173 | } finally { 1174 | try { 1175 | !n && s.return && s.return(); 1176 | } finally { 1177 | if (o) throw i; 1178 | } 1179 | } 1180 | return r; 1181 | })(e, t); 1182 | throw new TypeError( 1183 | "Invalid attempt to destructure non-iterable instance" 1184 | ); 1185 | }; 1186 | })(), 1187 | i = (function () { 1188 | function e(e, t) { 1189 | for (var r = 0; r < t.length; r++) { 1190 | var n = t[r]; 1191 | (n.enumerable = n.enumerable || !1), 1192 | (n.configurable = !0), 1193 | "value" in n && (n.writable = !0), 1194 | Object.defineProperty(e, n.key, n); 1195 | } 1196 | } 1197 | return function (t, r, n) { 1198 | return r && e(t.prototype, r), n && e(t, n), t; 1199 | }; 1200 | })(), 1201 | a = r(0), 1202 | s = (n = a) && n.__esModule ? n : { default: n }, 1203 | u = r(1); 1204 | function l(e, t) { 1205 | if (!e) 1206 | throw new ReferenceError( 1207 | "this hasn't been initialised - super() hasn't been called" 1208 | ); 1209 | return !t || ("object" != typeof t && "function" != typeof t) ? e : t; 1210 | } 1211 | var c = { solid: 0, gradient: 1, pattern: 4, noise: 5 }, 1212 | f = (function (e) { 1213 | function t() { 1214 | var e = 1215 | arguments.length > 0 && void 0 !== arguments[0] 1216 | ? arguments[0] 1217 | : {}; 1218 | !(function (e, t) { 1219 | if (!(e instanceof t)) 1220 | throw new TypeError("Cannot call a class as a function"); 1221 | })(this, t); 1222 | var r = l( 1223 | this, 1224 | (t.__proto__ || Object.getPrototypeOf(t)).call(this, e) 1225 | ); 1226 | r.targetLayer = r.selectedLayer; 1227 | var n = BCSketchInfo.shared().metadata().appVersion; 1228 | return ( 1229 | n < 50 1230 | ? (r.targetPath = r.selectedLayer.bezierPath()) 1231 | : n < 52 && 1232 | (r.targetPath = r.selectedLayer.pathInFrameWithTransforms()), 1233 | r.pointsAreValid ? r : l(r, u.Error.unsupportedShapePath) 1234 | ); 1235 | } 1236 | return ( 1237 | (function (e, t) { 1238 | if ("function" != typeof t && null !== t) 1239 | throw new TypeError( 1240 | "Super expression must either be null or a function, not " + 1241 | typeof t 1242 | ); 1243 | (e.prototype = Object.create(t && t.prototype, { 1244 | constructor: { 1245 | value: e, 1246 | enumerable: !1, 1247 | writable: !0, 1248 | configurable: !0, 1249 | }, 1250 | })), 1251 | t && 1252 | (Object.setPrototypeOf 1253 | ? Object.setPrototypeOf(e, t) 1254 | : (e.__proto__ = t)); 1255 | })(t, s["default"]), 1256 | i(t, [ 1257 | { 1258 | key: "applyImage", 1259 | value: (function () { 1260 | return function () { 1261 | var e = MSStyleFill.alloc().init(); 1262 | e.setImage(this.transformedImage), 1263 | (e.fillType = c.pattern), 1264 | this.targetLayer.style().removeAllStyleFills(), 1265 | this.targetLayer.style().addStyleFill(e); 1266 | }; 1267 | })(), 1268 | }, 1269 | { 1270 | key: "estimatePixelDensity", 1271 | value: (function () { 1272 | return function () { 1273 | var e = this.maximumVerticesWidthAndHeight(), 1274 | t = o(e, 2), 1275 | r = t[0], 1276 | n = t[1], 1277 | i = r / this.artboard.rect().size.width, 1278 | a = n / this.artboard.rect().size.height; 1279 | return i > a ? i : a; 1280 | }; 1281 | })(), 1282 | }, 1283 | { 1284 | key: "description", 1285 | value: (function () { 1286 | return function () { 1287 | return this.targetLayer.name(); 1288 | }; 1289 | })(), 1290 | }, 1291 | ]), 1292 | t 1293 | ); 1294 | })(); 1295 | t.default = f; 1296 | }, 1297 | ]); 1298 | "default" === e && "function" == typeof r ? r(t) : r[e](t); 1299 | } 1300 | that.onRun = __skpm_run.bind(this, "default"); 1301 | -------------------------------------------------------------------------------- /Angle.sketchplugin/Contents/Sketch/rotate.js: -------------------------------------------------------------------------------- 1 | var that = this; 2 | function __skpm_run(e, t) { 3 | that.context = t; 4 | var r = (function (e) { 5 | var t = {}; 6 | function r(n) { 7 | if (t[n]) return t[n].exports; 8 | var i = (t[n] = { i: n, l: !1, exports: {} }); 9 | return e[n].call(i.exports, i, i.exports, r), (i.l = !0), i.exports; 10 | } 11 | return ( 12 | (r.m = e), 13 | (r.c = t), 14 | (r.d = function (e, t, n) { 15 | r.o(e, t) || 16 | Object.defineProperty(e, t, { 17 | configurable: !1, 18 | enumerable: !0, 19 | get: n, 20 | }); 21 | }), 22 | (r.n = function (e) { 23 | var t = 24 | e && e.__esModule 25 | ? function () { 26 | return e.default; 27 | } 28 | : function () { 29 | return e; 30 | }; 31 | return r.d(t, "a", t), t; 32 | }), 33 | (r.o = function (e, t) { 34 | return Object.prototype.hasOwnProperty.call(e, t); 35 | }), 36 | (r.p = ""), 37 | r((r.s = 3)) 38 | ); 39 | })([ 40 | function (e, t) { 41 | Object.defineProperty(t, "__esModule", { value: !0 }); 42 | t.Error = { 43 | unsupportedSymbol: { 44 | message: "This does not seem to be a supported symbol.", 45 | }, 46 | unsupportedShapePath: { 47 | message: 48 | "There seems to be an issue with the shape we are trying to apply.", 49 | }, 50 | emptySelection: { 51 | message: "Please, select a Shape, Angle Mockup or Angle Composition", 52 | }, 53 | unsupportedElement: { 54 | message: "Please, select a Shape, Angle Mockup or Angle Composition", 55 | }, 56 | noImageOverrideOnSymbol: { 57 | message: "There is no image override for the selected symbol", 58 | }, 59 | symbolWithBitMapLayer: { 60 | message: "Bitmat overrides are not supported", 61 | }, 62 | }; 63 | }, 64 | function (e, r, n) { 65 | Object.defineProperty(r, "__esModule", { value: !0 }); 66 | var i = (function () { 67 | function e(e, t) { 68 | for (var r = 0; r < t.length; r++) { 69 | var n = t[r]; 70 | (n.enumerable = n.enumerable || !1), 71 | (n.configurable = !0), 72 | "value" in n && (n.writable = !0), 73 | Object.defineProperty(e, n.key, n); 74 | } 75 | } 76 | return function (t, r, n) { 77 | return r && e(t.prototype, r), n && e(t, n), t; 78 | }; 79 | })(), 80 | o = n(4); 81 | function a(e) { 82 | if (Array.isArray(e)) { 83 | for (var t = 0, r = Array(e.length); t < e.length; t++) r[t] = e[t]; 84 | return r; 85 | } 86 | return Array.from(e); 87 | } 88 | var s = { linear: 0, quadratic: 1, cubic: 2 }; 89 | Array.prototype.rotated = function (e) { 90 | return this.slice(e, this.length).concat(this.slice(0, e)); 91 | }; 92 | var u = (function () { 93 | function e() { 94 | var t = 95 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 96 | !(function (e, t) { 97 | if (!(e instanceof t)) 98 | throw new TypeError("Cannot call a class as a function"); 99 | })(this, e), 100 | (this.context = t.context), 101 | (this.selectedLayer = t.selectedLayer); 102 | } 103 | return ( 104 | i(e, [ 105 | { 106 | key: "imprintValue_forKey", 107 | value: (function () { 108 | return function (e, t) { 109 | null != this.selectedLayer 110 | ? this.context.command.setValue_forKey_onLayer( 111 | e, 112 | t, 113 | this.selectedLayer 114 | ) 115 | : print( 116 | "🛑 Imprinting value before selected layer assignment" 117 | ); 118 | }; 119 | })(), 120 | }, 121 | { 122 | key: "loadValueForKey", 123 | value: (function () { 124 | return function (e) { 125 | return null == this.selectedLayer 126 | ? (print( 127 | "🛑 Loading value before selected layer assignment" 128 | ), 129 | null) 130 | : this.context.command.valueForKey_onLayer( 131 | e, 132 | this.selectedLayer 133 | ); 134 | }; 135 | })(), 136 | }, 137 | { 138 | key: "artboardID", 139 | get: (function () { 140 | return function () { 141 | return void 0 != this._artboardID 142 | ? this._artboardID 143 | : ((this._artboardID = 144 | this.loadValueForKey("artboard-id") + ""), 145 | this._artboardID); 146 | }; 147 | })(), 148 | set: (function () { 149 | return function (e) {}; 150 | })(), 151 | }, 152 | { 153 | key: "artboard", 154 | get: (function () { 155 | return function () { 156 | if (void 0 != this._artboard) return this._artboard; 157 | if (void 0 != this.artboardID) { 158 | for ( 159 | var e = this.context.document.artboards(), t = 0; 160 | t < e.count(); 161 | t++ 162 | ) 163 | e[t].objectID() == this.artboardID && 164 | (this._artboard = e[t]); 165 | if (void 0 != this._artboard) return this._artboard; 166 | print( 167 | "🛑 Not able to retrieve artboard from id in document" 168 | ); 169 | } else print("🛑 No artboard ID registered"); 170 | }; 171 | })(), 172 | set: (function () { 173 | return function (e) { 174 | (this._artboard = e), 175 | this.imprintValue_forKey(e.objectID(), "artboard-id"); 176 | }; 177 | })(), 178 | }, 179 | { 180 | key: "rotation", 181 | get: (function () { 182 | return function () { 183 | return ( 184 | void 0 == this._rotation && 185 | (this._rotation = this.loadValueForKey("rotation")), 186 | this._rotation 187 | ); 188 | }; 189 | })(), 190 | set: (function () { 191 | return function (e) { 192 | (this._rotation = e), this.imprintValue_forKey(e, "rotation"); 193 | }; 194 | })(), 195 | }, 196 | { 197 | key: "pixelDensity", 198 | get: (function () { 199 | return function () { 200 | if ( 201 | (void 0 == this._pixelDensity && 202 | (this._pixelDensity = 203 | this.loadValueForKey("pixel-density") + 0), 204 | 0 == this._pixelDensity) 205 | ) { 206 | var e = Math.round(2 * this.estimatePixelDensity() + 0.5); 207 | return 0 == e ? 1 : e; 208 | } 209 | return this._pixelDensity; 210 | }; 211 | })(), 212 | set: (function () { 213 | return function (e) { 214 | (this._pixelDensity = e), 215 | this.imprintValue_forKey(e, "pixel-density"); 216 | }; 217 | })(), 218 | }, 219 | { 220 | key: "compressionRatio", 221 | get: (function () { 222 | return function () { 223 | return ( 224 | void 0 == this._compressionRatio && 225 | (this._compressionRatio = 226 | this.loadValueForKey("compression-ratio") + 0), 227 | this._compressionRatio 228 | ); 229 | }; 230 | })(), 231 | set: (function () { 232 | return function (e) { 233 | (this._compressionRatio = e), 234 | this.imprintValue_forKey(e, "compression-ratio"); 235 | }; 236 | })(), 237 | }, 238 | { 239 | key: "reversed", 240 | get: (function () { 241 | return function () { 242 | return ( 243 | void 0 == this._reversed && 244 | (this._reversed = 1 == this.loadValueForKey("reversed")), 245 | this._reversed 246 | ); 247 | }; 248 | })(), 249 | set: (function () { 250 | return function (e) { 251 | (this._reversed = e), this.imprintValue_forKey(e, "reversed"); 252 | }; 253 | })(), 254 | }, 255 | ]), 256 | i(e, [ 257 | { 258 | key: "exportRequest_lessThan52", 259 | value: (function () { 260 | return function () { 261 | var e = NSClassFromString( 262 | "SketchModel.MSImmutableLayerAncestry" 263 | ) 264 | .alloc() 265 | .initWithMutableLayer(t.selection[0]), 266 | r = MSExportFormat.formatWithScale_name_fileFormat( 267 | this.pixelDensity, 268 | "Angle", 269 | "png" 270 | ); 271 | return MSExportRequest.exportRequestsFromLayerAncestry_exportFormats( 272 | e, 273 | [r] 274 | ).firstObject(); 275 | }; 276 | })(), 277 | }, 278 | { 279 | key: "exporter", 280 | value: (function () { 281 | return function () { 282 | var e = this.context.document.colorSpace(); 283 | if (BCSketchInfo.shared().metadata().appVersion < 52) 284 | return MSExporter.exporterForRequest_colorSpace( 285 | this.exportRequest_lessThan52(), 286 | e 287 | ); 288 | var t = MSExportFormat.alloc().init(); 289 | (t.fileFormat = "png"), (t.scale = this.pixelDensity); 290 | var r = 291 | MSExportRequest.exportRequestsFromExportableLayer_exportFormats_useIDForName( 292 | this.artboard, 293 | [t], 294 | !0 295 | ).firstObject(); 296 | return MSExporter.exporterForRequest_colorSpace(r, e); 297 | }; 298 | })(), 299 | }, 300 | { 301 | key: "ciImage", 302 | value: (function () { 303 | return function () { 304 | var e = this.exporter().bitmapImageRep(); 305 | return CIImage.alloc().initWithCGImage(e.CGImage()); 306 | }; 307 | })(), 308 | }, 309 | { 310 | key: "guessRotationAndReversion", 311 | value: (function () { 312 | return function () { 313 | if (1 === this.loadValueForKey("guessed-rotation")) 314 | print( 315 | "⚠️ Angle has already guessed rotation and symmetry for this shape" 316 | ); 317 | else { 318 | var e = this.verticesLengths, 319 | t = void 0; 320 | if (this.artboard.class() != MSSymbolMaster) { 321 | t = this.artboard.frame(); 322 | var r = e[0] > e[1], 323 | n = t.width() > t.height(); 324 | r && (print("🛑 HORIZONTAL"), this.rotate()), 325 | n && 326 | (print("🛑 HAS HORIZONTAL ARTBOARD"), this.rotate()); 327 | var i = this.pointsFromBezierPath, 328 | o = Math.min.apply( 329 | Math, 330 | a( 331 | i.map(function (e) { 332 | return e.y; 333 | }) 334 | ) 335 | ), 336 | s = i[this.mappedIndexFor(0)], 337 | u = i[this.mappedIndexFor(1)]; 338 | s.y != o && 339 | u.y != o && 340 | (print("🛑 UPSIDE DOWN"), this.rotate(), this.rotate()); 341 | var c = BCSketchInfo.shared().metadata().appVersion; 342 | if (c < 50 || c >= 52) { 343 | var l = this.shorlaceSum(); 344 | l < 0 345 | ? (print("🛑 COUNTERCLOCKWISE"), 346 | this.reverseSymmetry()) 347 | : l > 0 348 | ? print("🛑 CLOCKWISE") 349 | : print("🛑 UNDEFINED CHIRALITY"); 350 | } else 351 | c < 52 && 352 | (1 === 353 | this.targetPath.contours().firstObject().isClockwise() 354 | ? (this.reverseSymmetry(), print("🛑 CLOCKWISE")) 355 | : print("🛑 COUNTERCLOCKWISE"), 356 | print("🛑 UNDEFINED CHIRALITY")); 357 | print( 358 | "🔄↔️ Angle has just guessed rotation and symmetry for this shape" 359 | ), 360 | this.imprintValue_forKey(!0, "guessed-rotation"); 361 | } 362 | } 363 | }; 364 | })(), 365 | }, 366 | { 367 | key: "shorlaceSum", 368 | value: (function () { 369 | return function () { 370 | var e = this.pointsFromBezierPath, 371 | t = Math.max.apply( 372 | Math, 373 | a( 374 | e.map(function (e) { 375 | return e.y; 376 | }) 377 | ) 378 | ); 379 | return Array.from({ length: 4 }, function (e, t) { 380 | return t; 381 | }).reduce(function (r, n) { 382 | return ( 383 | r + 384 | (-e[n].x + e[(n + 1) % 4].x) * 385 | (2 * t - e[n].y - e[(n + 1) % 4].y) 386 | ); 387 | }, 0); 388 | }; 389 | })(), 390 | }, 391 | { 392 | key: "maximumVerticesWidthAndHeight", 393 | value: (function () { 394 | return function () { 395 | var e = this.verticesLengths.rotated(this.rotation % 2); 396 | return [Math.max(e[0], e[2]), Math.max(e[1], e[3])]; 397 | }; 398 | })(), 399 | }, 400 | { 401 | key: "rotate", 402 | value: (function () { 403 | return function () { 404 | this.rotation = (this.rotation + (this.reversed ? 1 : 3)) % 4; 405 | }; 406 | })(), 407 | }, 408 | { 409 | key: "reverseSymmetry", 410 | value: (function () { 411 | return function () { 412 | this.rotate(), (this.reversed = !this.reversed); 413 | }; 414 | })(), 415 | }, 416 | { 417 | key: "mappedIndexFor", 418 | value: (function () { 419 | return function (e) { 420 | return this.reversed 421 | ? [0, 3, 2, 1][(e + this.rotation) % 4] 422 | : (e + this.rotation) % 4; 423 | }; 424 | })(), 425 | }, 426 | { 427 | key: "lossyCompressionOfImage_atRate", 428 | value: (function () { 429 | return function (e, t) { 430 | var r = NSBitmapImageRep.alloc().initWithCIImage(e), 431 | n = NSMutableDictionary.dictionary(); 432 | n.setObject_forKey( 433 | NSTIFFCompressionJPEG, 434 | NSImageCompressionMethod 435 | ), 436 | n.setObject_forKey(t, NSImageCompressionFactor), 437 | n.setObject_forKey( 438 | NSColor.whiteColor(), 439 | NSImageFallbackBackgroundColor 440 | ); 441 | var i = r.representationUsingType_properties( 442 | NSJPEGFileType, 443 | n 444 | ); 445 | return NSImage.alloc().initWithData(i); 446 | }; 447 | })(), 448 | }, 449 | { 450 | key: "pixelAccurateRepresentationOfImage", 451 | value: (function () { 452 | return function (e) { 453 | var t = NSCIImageRep.alloc().initWithCIImage(e), 454 | r = NSImage.alloc().initWithSize(t.size()); 455 | return r.addRepresentation(t), r; 456 | }; 457 | })(), 458 | }, 459 | { 460 | key: "pointsAreValid_lessThan50", 461 | get: (function () { 462 | return function () { 463 | var e = this.pointsFromBezierPath; 464 | return null !== e && 7 === e.length; 465 | }; 466 | })(), 467 | }, 468 | { 469 | key: "pointsAreValid_lessThan52", 470 | get: (function () { 471 | return function () { 472 | var e = this.targetPath.contours().firstObject(), 473 | t = Array.fromNSArray(e.segments()); 474 | return ( 475 | null !== t && 476 | 4 === t.length && 477 | !t.some(function (e) { 478 | return e.segmentType() != s.linear; 479 | }) 480 | ); 481 | }; 482 | })(), 483 | }, 484 | { 485 | key: "pointsAreValid", 486 | get: (function () { 487 | return function () { 488 | var e = BCSketchInfo.shared().metadata().appVersion; 489 | if (e < 50) return this.pointsAreValid_lessThan50; 490 | if (e < 52) return this.pointsAreValid_lessThan52; 491 | var t = this.targetLayer.points(); 492 | return ( 493 | null !== t && 494 | 4 === t.length && 495 | !t.some(function (e) { 496 | return !e.isStraight(); 497 | }) 498 | ); 499 | }; 500 | })(), 501 | }, 502 | { 503 | key: "pointsFromBezierPath", 504 | get: (function () { 505 | return function () { 506 | var e = this, 507 | t = BCSketchInfo.shared().metadata().appVersion; 508 | if (t < 50) { 509 | var r = this.targetPath.elementCount(); 510 | return 7 != r 511 | ? null 512 | : Array.from({ length: r }, function (e, t) { 513 | return t; 514 | }).map(function (t) { 515 | var r = MOPointer.alloc().initWithValue_( 516 | CGPointMake(0, 0) 517 | ); 518 | return ( 519 | e.targetPath.elementAtIndex_associatedPoints_(t, r), 520 | r.value() 521 | ); 522 | }); 523 | } 524 | if (t < 52) { 525 | var n = this.targetPath.contours().firstObject(); 526 | return Array.fromNSArray(n.segments()).map(function (e) { 527 | return e.endPoint1(); 528 | }); 529 | } 530 | var i = this.targetLayer.rect().size; 531 | return Array.fromNSArray(this.targetLayer.points()) 532 | .map(function (e) { 533 | return e.point(); 534 | }) 535 | .map(function (e) { 536 | return { 537 | x: Number(e.x) * Number(i.width), 538 | y: Number(e.y) * Number(i.height), 539 | }; 540 | }); 541 | }; 542 | })(), 543 | }, 544 | { 545 | key: "verticesLengths", 546 | get: (function () { 547 | return function () { 548 | var e = this.pointsFromBezierPath; 549 | return Array.from({ length: 4 }, function (e, t) { 550 | return t; 551 | }).map(function (t) { 552 | var r = (t + 1) % 4, 553 | n = e[t].x - e[r].x, 554 | i = e[t].y - e[r].y; 555 | return Math.sqrt(Math.pow(n, 2) + Math.pow(i, 2)); 556 | }); 557 | }; 558 | })(), 559 | }, 560 | { 561 | key: "normalizedCIVectors", 562 | get: (function () { 563 | return function () { 564 | var e = this.pointsFromBezierPath, 565 | t = Math.max.apply( 566 | Math, 567 | a( 568 | e.map(function (e) { 569 | return e.y; 570 | }) 571 | ) 572 | ), 573 | r = this.pixelDensity; 574 | return e.map(function (e) { 575 | return CIVector.vectorWithX_Y(e.x * r, (t - e.y) * r); 576 | }); 577 | }; 578 | })(), 579 | }, 580 | { 581 | key: "transformedImage", 582 | get: (function () { 583 | return function () { 584 | var e = this.normalizedCIVectors, 585 | t = CIFilter.filterWithName("CIPerspectiveTransform"); 586 | t.setValue_forKey(e[this.mappedIndexFor(0)], "inputTopLeft"), 587 | t.setValue_forKey( 588 | e[this.mappedIndexFor(1)], 589 | "inputTopRight" 590 | ), 591 | t.setValue_forKey( 592 | e[this.mappedIndexFor(2)], 593 | "inputBottomRight" 594 | ), 595 | t.setValue_forKey( 596 | e[this.mappedIndexFor(3)], 597 | "inputBottomLeft" 598 | ); 599 | var r = this.ciImage(); 600 | t.setValue_forKey(r, "inputImage"); 601 | var n = t.valueForKey("outputImage"); 602 | if (n) { 603 | var i = void 0, 604 | a = o.CompressionRatio[this.compressionRatio].ratio; 605 | return ( 606 | (i = 607 | 1 != a 608 | ? this.lossyCompressionOfImage_atRate(n, a) 609 | : this.pixelAccurateRepresentationOfImage(n)), 610 | BCSketchInfo.shared().metadata().appVersion < 47 611 | ? MSImageData.alloc().initWithImage_convertColorSpace( 612 | i, 613 | !0 614 | ) 615 | : MSImageData.alloc().initWithImage_(i) 616 | ); 617 | } 618 | print("🛑 Unable to form perspective image"); 619 | }; 620 | })(), 621 | }, 622 | ]), 623 | e 624 | ); 625 | })(); 626 | r.default = u; 627 | }, 628 | function (e, t, r) { 629 | Object.defineProperty(t, "__esModule", { value: !0 }); 630 | var n, 631 | i = (function () { 632 | return function (e, t) { 633 | if (Array.isArray(e)) return e; 634 | if (Symbol.iterator in Object(e)) 635 | return (function (e, t) { 636 | var r = [], 637 | n = !0, 638 | i = !1, 639 | o = void 0; 640 | try { 641 | for ( 642 | var a, s = e[Symbol.iterator](); 643 | !(n = (a = s.next()).done) && 644 | (r.push(a.value), !t || r.length !== t); 645 | n = !0 646 | ); 647 | } catch (e) { 648 | (i = !0), (o = e); 649 | } finally { 650 | try { 651 | !n && s.return && s.return(); 652 | } finally { 653 | if (i) throw o; 654 | } 655 | } 656 | return r; 657 | })(e, t); 658 | throw new TypeError( 659 | "Invalid attempt to destructure non-iterable instance" 660 | ); 661 | }; 662 | })(), 663 | o = (function () { 664 | function e(e, t) { 665 | for (var r = 0; r < t.length; r++) { 666 | var n = t[r]; 667 | (n.enumerable = n.enumerable || !1), 668 | (n.configurable = !0), 669 | "value" in n && (n.writable = !0), 670 | Object.defineProperty(e, n.key, n); 671 | } 672 | } 673 | return function (t, r, n) { 674 | return r && e(t.prototype, r), n && e(t, n), t; 675 | }; 676 | })(), 677 | a = r(1), 678 | s = (n = a) && n.__esModule ? n : { default: n }, 679 | u = r(0); 680 | function c(e, t) { 681 | if (!e) 682 | throw new ReferenceError( 683 | "this hasn't been initialised - super() hasn't been called" 684 | ); 685 | return !t || ("object" != typeof t && "function" != typeof t) ? e : t; 686 | } 687 | var l = (function (e) { 688 | function t() { 689 | var e = 690 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 691 | !(function (e, t) { 692 | if (!(e instanceof t)) 693 | throw new TypeError("Cannot call a class as a function"); 694 | })(this, t); 695 | var r = c( 696 | this, 697 | (t.__proto__ || Object.getPrototypeOf(t)).call(this, e) 698 | ); 699 | if ( 700 | ((r.targetLayer = e.override.affectedLayer()), 701 | r.targetLayer.class() === MSImmutableBitmapLayer) 702 | ) 703 | return c(r, u.Error.symbolWithBitMapLayer); 704 | var n = BCSketchInfo.shared().metadata().appVersion; 705 | n < 50 706 | ? (r.targetPath = e.override.affectedLayer().bezierPath()) 707 | : n < 52 && 708 | (r.targetPath = e.override 709 | .affectedLayer() 710 | .pathInFrameWithTransforms()); 711 | var i = e.override.overridePoint().parent(); 712 | return ( 713 | null !== i && (r.overrideLayer = i), 714 | r.pointsAreValid ? r : c(r, u.Error.unsupportedShapePath) 715 | ); 716 | } 717 | return ( 718 | (function (e, t) { 719 | if ("function" != typeof t && null !== t) 720 | throw new TypeError( 721 | "Super expression must either be null or a function, not " + 722 | typeof t 723 | ); 724 | (e.prototype = Object.create(t && t.prototype, { 725 | constructor: { 726 | value: e, 727 | enumerable: !1, 728 | writable: !0, 729 | configurable: !0, 730 | }, 731 | })), 732 | t && 733 | (Object.setPrototypeOf 734 | ? Object.setPrototypeOf(e, t) 735 | : (e.__proto__ = t)); 736 | })(t, s["default"]), 737 | o(t, [ 738 | { 739 | key: "applyImage", 740 | value: (function () { 741 | return function () { 742 | var e = this.targetLayer.objectID(), 743 | t = 744 | this.selectedLayer.overrides() || 745 | NSDictionary.dictionary(), 746 | r = NSMutableDictionary.dictionaryWithDictionary(t); 747 | r.setObject_forKey(this.transformedImage, e), 748 | (this.selectedLayer.overrides = r); 749 | }; 750 | })(), 751 | }, 752 | { 753 | key: "estimatePixelDensity", 754 | value: (function () { 755 | return function () { 756 | var e = this.maximumVerticesWidthAndHeight(), 757 | t = i(e, 2), 758 | r = t[0], 759 | n = t[1], 760 | o = 761 | (this.selectedLayer.rect().size.width * r) / 762 | (this.selectedLayer.naturalSize().width * 763 | this.artboard.rect().size.width), 764 | a = 765 | (this.selectedLayer.rect().size.height * n) / 766 | (this.selectedLayer.naturalSize().height * 767 | this.artboard.rect().size.height); 768 | return o > a ? o : a; 769 | }; 770 | })(), 771 | }, 772 | { 773 | key: "description", 774 | value: (function () { 775 | return function () { 776 | return ( 777 | this.selectedLayer.name() + " " + this.targetLayer.name() 778 | ); 779 | }; 780 | })(), 781 | }, 782 | ]), 783 | t 784 | ); 785 | })(); 786 | t.default = l; 787 | }, 788 | function (e, t, r) { 789 | Object.defineProperty(t, "__esModule", { value: !0 }), 790 | (t.default = function (e) { 791 | var t = e.selection[0]; 792 | if (null != t) { 793 | var r = Array.fromNSArray(t); 794 | if (0 != r.length) { 795 | var n = o.default.tryCreating({ for: r, in: e }), 796 | i = n.filter(function (e) { 797 | return e instanceof o.default; 798 | }), 799 | u = n.filter(function (e) { 800 | return !(e instanceof o.default); 801 | }); 802 | if (0 != i.length) 803 | i.forEach(function (e) { 804 | e.rotate(), e.applyImage(); 805 | }), 806 | a.show({ 807 | message: "Angle rotated! 🔄", 808 | inDocument: e.document, 809 | }); 810 | else { 811 | var c = u[0]; 812 | a.show({ message: c.message, inDocument: e.document }); 813 | } 814 | } else 815 | a.show({ 816 | message: s.Error.emptySelection.message, 817 | inDocument: e.document, 818 | }); 819 | } else 820 | a.show({ 821 | message: s.Error.emptySelection.message, 822 | inDocument: e.document, 823 | }); 824 | }); 825 | var n, 826 | i = r(1), 827 | o = (n = i) && n.__esModule ? n : { default: n }, 828 | a = (function (e) { 829 | if (e && e.__esModule) return e; 830 | var t = {}; 831 | if (null != e) 832 | for (var r in e) 833 | Object.prototype.hasOwnProperty.call(e, r) && (t[r] = e[r]); 834 | return (t.default = e), t; 835 | })(r(5)), 836 | s = r(0); 837 | }, 838 | function (e, t) { 839 | Object.defineProperty(t, "__esModule", { value: !0 }); 840 | t.CompressionRatio = [ 841 | { selectionLabel: "Best", ratio: 1 }, 842 | { selectionLabel: "Better", ratio: 0.9 }, 843 | { selectionLabel: "Good", ratio: 0.8 }, 844 | { selectionLabel: "Average", ratio: 0.7 }, 845 | ]; 846 | }, 847 | function (e, t, r) { 848 | Object.defineProperty(t, "__esModule", { value: !0 }), 849 | (t.show = function (e) { 850 | var t = e.message, 851 | r = e.inDocument; 852 | void 0 != r && void 0 != r.showMessage && r.showMessage(t); 853 | print(t); 854 | }), 855 | (t.filterPossibleArtboards = function (e) { 856 | var t = e.class(); 857 | switch (t) { 858 | case MSArtboardGroup: 859 | var r = e, 860 | n = r.frame(); 861 | if (n.width() < 250 || n.height() < 250) return !1; 862 | var i = n.width() / n.height(); 863 | if ((i > 1 && (i = 1 / i), i < 0.4)) return !1; 864 | break; 865 | case MSSymbolMaster: 866 | return !1; 867 | default: 868 | return print(t), !1; 869 | } 870 | return !0; 871 | }), 872 | (t.compareByRatioAndAlphabet = function (e, t) { 873 | var r = e.frame(), 874 | n = t.frame(), 875 | i = r.width() / r.height(); 876 | i > 1 && (i = 1 / i); 877 | var o = i > 0.4 && i < 0.8, 878 | a = n.width() / n.height(); 879 | a > 1 && (a = 1 / a); 880 | var s = a > 0.4 && a < 0.8; 881 | if (o && !s) return !1; 882 | if (s && !o) return !0; 883 | if (i == a) return e.name() > t.name(); 884 | return i > a; 885 | }), 886 | (t.introspect = function (e) { 887 | var t = e.class().mocha(); 888 | print("-----------------------------------------------"), 889 | print("PROPERTIES-------------------------------------"), 890 | print("-----------------------------------------------"), 891 | print(t.properties()), 892 | print(t.propertiesWithAncestors()), 893 | print("-----------------------------------------------"), 894 | print("INSTANCE METHODS-------------------------------"), 895 | print("-----------------------------------------------"), 896 | print(t.instanceMethods()), 897 | print(t.instanceMethodsWithAncestors()), 898 | print("-----------------------------------------------"), 899 | print("CLASS METHODS----------------------------------"), 900 | print("-----------------------------------------------"), 901 | print(t.classMethods()), 902 | print(t.classMethodsWithAncestors()), 903 | print("-----------------------------------------------"), 904 | print("PROTOCOLS--------------------------------------"), 905 | print("-----------------------------------------------"), 906 | print(t.protocols()), 907 | print(t.protocolsWithAncestors()); 908 | }), 909 | (t.createLabel = function (e, t, r) { 910 | var n = NSTextField.alloc().initWithFrame(r); 911 | return ( 912 | n.setStringValue(e), 913 | n.setFont(NSFont.boldSystemFontOfSize(t)), 914 | n.setBezeled(!1), 915 | n.setDrawsBackground(!1), 916 | n.setEditable(!1), 917 | n.setSelectable(!1), 918 | n 919 | ); 920 | }), 921 | (t.popUpButtonsforRectangleIndexer_withTitleIndexer_andImageIndexer_defaultSelected_onIndex = 922 | function (e, t, r, n) { 923 | var i = NSPopUpButton.alloc().initWithFrame(e(n)); 924 | i.addItemsWithTitles(t), 925 | null != r && 926 | ((i.imageScaling = NSImageScaleProportionallyUpOrDown), 927 | Array.fromNSArray(i.itemArray()).forEach(function (e, t, n) { 928 | e.image = r[t]; 929 | })); 930 | return i; 931 | }), 932 | (t.smallImagesFromArtboard = function (e) { 933 | if (e.class() == MSSymbolMaster) return print(e), null; 934 | void 0 == e.frame && print(e); 935 | var t = e.frame().width(), 936 | r = e.frame().height(), 937 | n = t / r; 938 | n > 1 && (n = 1 / n); 939 | if (n > 0.8 || n < 0.4) return null; 940 | var i = NSClassFromString("SketchModel.MSImmutableLayerAncestry") 941 | .alloc() 942 | .initWithMutableLayer(e), 943 | o = 48 / (t > r ? t : r), 944 | a = MSExportFormat.formatWithScale_name_fileFormat(o, "", "png"), 945 | s = MSExportRequest.exportRequestsFromLayerAncestry_exportFormats( 946 | i, 947 | [a] 948 | ).firstObject(); 949 | return MSExporter.exporterForRequest_colorSpace( 950 | s, 951 | NSColorSpace.sRGBColorSpace() 952 | ).previewImage(); 953 | }); 954 | var n = u(r(1)), 955 | i = u(r(6)), 956 | o = u(r(2)), 957 | a = u(r(7)), 958 | s = r(0); 959 | function u(e) { 960 | return e && e.__esModule ? e : { default: e }; 961 | } 962 | (n.default.tryCreating = function (e) { 963 | var t = e.for, 964 | r = e.in; 965 | return t 966 | .map(function (e) { 967 | switch (e.class()) { 968 | case MSSymbolInstance: 969 | var t = Array.fromNSArray(e.availableOverrides()) || [], 970 | n = t 971 | .filter(function (e) { 972 | return e.currentValue().class() == MSImageData; 973 | }) 974 | .map(function (t) { 975 | return new o.default({ 976 | selectedLayer: e, 977 | context: r, 978 | override: t, 979 | }); 980 | }), 981 | u = t 982 | .map(function (e) { 983 | return e.children(); 984 | }) 985 | .filter(function (e) { 986 | return null != e; 987 | }) 988 | .map(Array.fromNSArray) 989 | .reduce(function (e, t) { 990 | return e.concat(t); 991 | }, []) 992 | .filter(function (e) { 993 | return e.class() == MSAvailableOverride; 994 | }) 995 | .map(function (t) { 996 | return new i.default({ 997 | override: t, 998 | selectedLayer: e, 999 | context: r, 1000 | }); 1001 | }); 1002 | return n.concat(u); 1003 | case MSShapeGroup: 1004 | case MSShapePathLayer: 1005 | case MSRectangleShape: 1006 | return new a.default({ selectedLayer: e, context: r }); 1007 | default: 1008 | return s.Error.unsupportedElement; 1009 | } 1010 | }) 1011 | .reduce(function (e, t, r, n) { 1012 | return e.concat(t); 1013 | }, []); 1014 | }), 1015 | (Array.fromNSArray = function (e) { 1016 | for (var t = [], r = 0; r < e.length; r++) t.push(e[r]); 1017 | return t; 1018 | }), 1019 | (Array.prototype.print = function () { 1020 | return this.map(function (e) { 1021 | return print(e), e; 1022 | }); 1023 | }); 1024 | }, 1025 | function (e, t, r) { 1026 | Object.defineProperty(t, "__esModule", { value: !0 }); 1027 | var n, 1028 | i = (function () { 1029 | function e(e, t) { 1030 | for (var r = 0; r < t.length; r++) { 1031 | var n = t[r]; 1032 | (n.enumerable = n.enumerable || !1), 1033 | (n.configurable = !0), 1034 | "value" in n && (n.writable = !0), 1035 | Object.defineProperty(e, n.key, n); 1036 | } 1037 | } 1038 | return function (t, r, n) { 1039 | return r && e(t.prototype, r), n && e(t, n), t; 1040 | }; 1041 | })(), 1042 | o = (function () { 1043 | return function e(t, r, n) { 1044 | null === t && (t = Function.prototype); 1045 | var i = Object.getOwnPropertyDescriptor(t, r); 1046 | if (void 0 === i) { 1047 | var o = Object.getPrototypeOf(t); 1048 | return null === o ? void 0 : e(o, r, n); 1049 | } 1050 | if ("value" in i) return i.value; 1051 | var a = i.get; 1052 | return void 0 !== a ? a.call(n) : void 0; 1053 | }; 1054 | })(), 1055 | a = r(2), 1056 | s = (n = a) && n.__esModule ? n : { default: n }; 1057 | r(0); 1058 | var u = (function (e) { 1059 | function t() { 1060 | var e = 1061 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 1062 | !(function (e, t) { 1063 | if (!(e instanceof t)) 1064 | throw new TypeError("Cannot call a class as a function"); 1065 | })(this, t); 1066 | var r = (function (e, t) { 1067 | if (!e) 1068 | throw new ReferenceError( 1069 | "this hasn't been initialised - super() hasn't been called" 1070 | ); 1071 | return !t || ("object" != typeof t && "function" != typeof t) 1072 | ? e 1073 | : t; 1074 | })(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)); 1075 | return (r.override = e.override), r; 1076 | } 1077 | return ( 1078 | (function (e, t) { 1079 | if ("function" != typeof t && null !== t) 1080 | throw new TypeError( 1081 | "Super expression must either be null or a function, not " + 1082 | typeof t 1083 | ); 1084 | (e.prototype = Object.create(t && t.prototype, { 1085 | constructor: { 1086 | value: e, 1087 | enumerable: !1, 1088 | writable: !0, 1089 | configurable: !0, 1090 | }, 1091 | })), 1092 | t && 1093 | (Object.setPrototypeOf 1094 | ? Object.setPrototypeOf(e, t) 1095 | : (e.__proto__ = t)); 1096 | })(t, s["default"]), 1097 | i(t, [ 1098 | { 1099 | key: "loadValueForKey", 1100 | value: (function () { 1101 | return function (e) { 1102 | return o( 1103 | t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), 1104 | "loadValueForKey", 1105 | this 1106 | ).call(this, this.targetLayer.objectID() + "-" + e); 1107 | }; 1108 | })(), 1109 | }, 1110 | { 1111 | key: "imprintValue_forKey", 1112 | value: (function () { 1113 | return function (e, r) { 1114 | o( 1115 | t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), 1116 | "imprintValue_forKey", 1117 | this 1118 | ).call(this, e, this.targetLayer.objectID() + "-" + r); 1119 | }; 1120 | })(), 1121 | }, 1122 | { 1123 | key: "applyImage", 1124 | value: (function () { 1125 | return function () { 1126 | var e = 1127 | this.selectedLayer.overrides() || 1128 | NSDictionary.dictionary(), 1129 | t = NSMutableDictionary.dictionaryWithDictionary(e), 1130 | r = (this.overrideLayer + "").replace("_symbolID", ""), 1131 | n = t.objectForKey(r) || NSMutableDictionary.dictionary(), 1132 | i = NSMutableDictionary.dictionaryWithDictionary(n), 1133 | o = this.targetLayer.objectID(); 1134 | i.setObject_forKey(this.transformedImage, o), 1135 | t.setObject_forKey(i, r), 1136 | (this.selectedLayer.overrides = t); 1137 | }; 1138 | })(), 1139 | }, 1140 | { 1141 | key: "description", 1142 | value: (function () { 1143 | return function () { 1144 | return ( 1145 | this.override.parent().affectedLayer().name() + 1146 | " " + 1147 | this.targetLayer.name() 1148 | ); 1149 | }; 1150 | })(), 1151 | }, 1152 | ]), 1153 | t 1154 | ); 1155 | })(); 1156 | t.default = u; 1157 | }, 1158 | function (e, t, r) { 1159 | Object.defineProperty(t, "__esModule", { value: !0 }); 1160 | var n, 1161 | i = (function () { 1162 | return function (e, t) { 1163 | if (Array.isArray(e)) return e; 1164 | if (Symbol.iterator in Object(e)) 1165 | return (function (e, t) { 1166 | var r = [], 1167 | n = !0, 1168 | i = !1, 1169 | o = void 0; 1170 | try { 1171 | for ( 1172 | var a, s = e[Symbol.iterator](); 1173 | !(n = (a = s.next()).done) && 1174 | (r.push(a.value), !t || r.length !== t); 1175 | n = !0 1176 | ); 1177 | } catch (e) { 1178 | (i = !0), (o = e); 1179 | } finally { 1180 | try { 1181 | !n && s.return && s.return(); 1182 | } finally { 1183 | if (i) throw o; 1184 | } 1185 | } 1186 | return r; 1187 | })(e, t); 1188 | throw new TypeError( 1189 | "Invalid attempt to destructure non-iterable instance" 1190 | ); 1191 | }; 1192 | })(), 1193 | o = (function () { 1194 | function e(e, t) { 1195 | for (var r = 0; r < t.length; r++) { 1196 | var n = t[r]; 1197 | (n.enumerable = n.enumerable || !1), 1198 | (n.configurable = !0), 1199 | "value" in n && (n.writable = !0), 1200 | Object.defineProperty(e, n.key, n); 1201 | } 1202 | } 1203 | return function (t, r, n) { 1204 | return r && e(t.prototype, r), n && e(t, n), t; 1205 | }; 1206 | })(), 1207 | a = r(1), 1208 | s = (n = a) && n.__esModule ? n : { default: n }, 1209 | u = r(0); 1210 | function c(e, t) { 1211 | if (!e) 1212 | throw new ReferenceError( 1213 | "this hasn't been initialised - super() hasn't been called" 1214 | ); 1215 | return !t || ("object" != typeof t && "function" != typeof t) ? e : t; 1216 | } 1217 | var l = { solid: 0, gradient: 1, pattern: 4, noise: 5 }, 1218 | f = (function (e) { 1219 | function t() { 1220 | var e = 1221 | arguments.length > 0 && void 0 !== arguments[0] 1222 | ? arguments[0] 1223 | : {}; 1224 | !(function (e, t) { 1225 | if (!(e instanceof t)) 1226 | throw new TypeError("Cannot call a class as a function"); 1227 | })(this, t); 1228 | var r = c( 1229 | this, 1230 | (t.__proto__ || Object.getPrototypeOf(t)).call(this, e) 1231 | ); 1232 | r.targetLayer = r.selectedLayer; 1233 | var n = BCSketchInfo.shared().metadata().appVersion; 1234 | return ( 1235 | n < 50 1236 | ? (r.targetPath = r.selectedLayer.bezierPath()) 1237 | : n < 52 && 1238 | (r.targetPath = r.selectedLayer.pathInFrameWithTransforms()), 1239 | r.pointsAreValid ? r : c(r, u.Error.unsupportedShapePath) 1240 | ); 1241 | } 1242 | return ( 1243 | (function (e, t) { 1244 | if ("function" != typeof t && null !== t) 1245 | throw new TypeError( 1246 | "Super expression must either be null or a function, not " + 1247 | typeof t 1248 | ); 1249 | (e.prototype = Object.create(t && t.prototype, { 1250 | constructor: { 1251 | value: e, 1252 | enumerable: !1, 1253 | writable: !0, 1254 | configurable: !0, 1255 | }, 1256 | })), 1257 | t && 1258 | (Object.setPrototypeOf 1259 | ? Object.setPrototypeOf(e, t) 1260 | : (e.__proto__ = t)); 1261 | })(t, s["default"]), 1262 | o(t, [ 1263 | { 1264 | key: "applyImage", 1265 | value: (function () { 1266 | return function () { 1267 | var e = MSStyleFill.alloc().init(); 1268 | e.setImage(this.transformedImage), 1269 | (e.fillType = l.pattern), 1270 | this.targetLayer.style().removeAllStyleFills(), 1271 | this.targetLayer.style().addStyleFill(e); 1272 | }; 1273 | })(), 1274 | }, 1275 | { 1276 | key: "estimatePixelDensity", 1277 | value: (function () { 1278 | return function () { 1279 | var e = this.maximumVerticesWidthAndHeight(), 1280 | t = i(e, 2), 1281 | r = t[0], 1282 | n = t[1], 1283 | o = r / this.artboard.rect().size.width, 1284 | a = n / this.artboard.rect().size.height; 1285 | return o > a ? o : a; 1286 | }; 1287 | })(), 1288 | }, 1289 | { 1290 | key: "description", 1291 | value: (function () { 1292 | return function () { 1293 | return this.targetLayer.name(); 1294 | }; 1295 | })(), 1296 | }, 1297 | ]), 1298 | t 1299 | ); 1300 | })(); 1301 | t.default = f; 1302 | }, 1303 | ]); 1304 | "default" === e && "function" == typeof r ? r(t) : r[e](t); 1305 | } 1306 | that.onRun = __skpm_run.bind(this, "default"); 1307 | -------------------------------------------------------------------------------- /Angle.sketchplugin/Contents/Sketch/symmetry.js: -------------------------------------------------------------------------------- 1 | var that = this; 2 | function __skpm_run(e, t) { 3 | that.context = t; 4 | var r = (function (e) { 5 | var t = {}; 6 | function r(n) { 7 | if (t[n]) return t[n].exports; 8 | var i = (t[n] = { i: n, l: !1, exports: {} }); 9 | return e[n].call(i.exports, i, i.exports, r), (i.l = !0), i.exports; 10 | } 11 | return ( 12 | (r.m = e), 13 | (r.c = t), 14 | (r.d = function (e, t, n) { 15 | r.o(e, t) || 16 | Object.defineProperty(e, t, { 17 | configurable: !1, 18 | enumerable: !0, 19 | get: n, 20 | }); 21 | }), 22 | (r.n = function (e) { 23 | var t = 24 | e && e.__esModule 25 | ? function () { 26 | return e.default; 27 | } 28 | : function () { 29 | return e; 30 | }; 31 | return r.d(t, "a", t), t; 32 | }), 33 | (r.o = function (e, t) { 34 | return Object.prototype.hasOwnProperty.call(e, t); 35 | }), 36 | (r.p = ""), 37 | r((r.s = 3)) 38 | ); 39 | })([ 40 | function (e, t) { 41 | Object.defineProperty(t, "__esModule", { value: !0 }); 42 | t.Error = { 43 | unsupportedSymbol: { 44 | message: "This does not seem to be a supported symbol.", 45 | }, 46 | unsupportedShapePath: { 47 | message: 48 | "There seems to be an issue with the shape we are trying to apply.", 49 | }, 50 | emptySelection: { 51 | message: "Please, select a Shape, Angle Mockup or Angle Composition", 52 | }, 53 | unsupportedElement: { 54 | message: "Please, select a Shape, Angle Mockup or Angle Composition", 55 | }, 56 | noImageOverrideOnSymbol: { 57 | message: "There is no image override for the selected symbol", 58 | }, 59 | symbolWithBitMapLayer: { 60 | message: "Bitmat overrides are not supported", 61 | }, 62 | }; 63 | }, 64 | function (e, r, n) { 65 | Object.defineProperty(r, "__esModule", { value: !0 }); 66 | var i = (function () { 67 | function e(e, t) { 68 | for (var r = 0; r < t.length; r++) { 69 | var n = t[r]; 70 | (n.enumerable = n.enumerable || !1), 71 | (n.configurable = !0), 72 | "value" in n && (n.writable = !0), 73 | Object.defineProperty(e, n.key, n); 74 | } 75 | } 76 | return function (t, r, n) { 77 | return r && e(t.prototype, r), n && e(t, n), t; 78 | }; 79 | })(), 80 | o = n(4); 81 | function a(e) { 82 | if (Array.isArray(e)) { 83 | for (var t = 0, r = Array(e.length); t < e.length; t++) r[t] = e[t]; 84 | return r; 85 | } 86 | return Array.from(e); 87 | } 88 | var s = { linear: 0, quadratic: 1, cubic: 2 }; 89 | Array.prototype.rotated = function (e) { 90 | return this.slice(e, this.length).concat(this.slice(0, e)); 91 | }; 92 | var u = (function () { 93 | function e() { 94 | var t = 95 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 96 | !(function (e, t) { 97 | if (!(e instanceof t)) 98 | throw new TypeError("Cannot call a class as a function"); 99 | })(this, e), 100 | (this.context = t.context), 101 | (this.selectedLayer = t.selectedLayer); 102 | } 103 | return ( 104 | i(e, [ 105 | { 106 | key: "imprintValue_forKey", 107 | value: (function () { 108 | return function (e, t) { 109 | null != this.selectedLayer 110 | ? this.context.command.setValue_forKey_onLayer( 111 | e, 112 | t, 113 | this.selectedLayer 114 | ) 115 | : print( 116 | "🛑 Imprinting value before selected layer assignment" 117 | ); 118 | }; 119 | })(), 120 | }, 121 | { 122 | key: "loadValueForKey", 123 | value: (function () { 124 | return function (e) { 125 | return null == this.selectedLayer 126 | ? (print( 127 | "🛑 Loading value before selected layer assignment" 128 | ), 129 | null) 130 | : this.context.command.valueForKey_onLayer( 131 | e, 132 | this.selectedLayer 133 | ); 134 | }; 135 | })(), 136 | }, 137 | { 138 | key: "artboardID", 139 | get: (function () { 140 | return function () { 141 | return void 0 != this._artboardID 142 | ? this._artboardID 143 | : ((this._artboardID = 144 | this.loadValueForKey("artboard-id") + ""), 145 | this._artboardID); 146 | }; 147 | })(), 148 | set: (function () { 149 | return function (e) {}; 150 | })(), 151 | }, 152 | { 153 | key: "artboard", 154 | get: (function () { 155 | return function () { 156 | if (void 0 != this._artboard) return this._artboard; 157 | if (void 0 != this.artboardID) { 158 | for ( 159 | var e = this.context.document.artboards(), t = 0; 160 | t < e.count(); 161 | t++ 162 | ) 163 | e[t].objectID() == this.artboardID && 164 | (this._artboard = e[t]); 165 | if (void 0 != this._artboard) return this._artboard; 166 | print( 167 | "🛑 Not able to retrieve artboard from id in document" 168 | ); 169 | } else print("🛑 No artboard ID registered"); 170 | }; 171 | })(), 172 | set: (function () { 173 | return function (e) { 174 | (this._artboard = e), 175 | this.imprintValue_forKey(e.objectID(), "artboard-id"); 176 | }; 177 | })(), 178 | }, 179 | { 180 | key: "rotation", 181 | get: (function () { 182 | return function () { 183 | return ( 184 | void 0 == this._rotation && 185 | (this._rotation = this.loadValueForKey("rotation")), 186 | this._rotation 187 | ); 188 | }; 189 | })(), 190 | set: (function () { 191 | return function (e) { 192 | (this._rotation = e), this.imprintValue_forKey(e, "rotation"); 193 | }; 194 | })(), 195 | }, 196 | { 197 | key: "pixelDensity", 198 | get: (function () { 199 | return function () { 200 | if ( 201 | (void 0 == this._pixelDensity && 202 | (this._pixelDensity = 203 | this.loadValueForKey("pixel-density") + 0), 204 | 0 == this._pixelDensity) 205 | ) { 206 | var e = Math.round(2 * this.estimatePixelDensity() + 0.5); 207 | return 0 == e ? 1 : e; 208 | } 209 | return this._pixelDensity; 210 | }; 211 | })(), 212 | set: (function () { 213 | return function (e) { 214 | (this._pixelDensity = e), 215 | this.imprintValue_forKey(e, "pixel-density"); 216 | }; 217 | })(), 218 | }, 219 | { 220 | key: "compressionRatio", 221 | get: (function () { 222 | return function () { 223 | return ( 224 | void 0 == this._compressionRatio && 225 | (this._compressionRatio = 226 | this.loadValueForKey("compression-ratio") + 0), 227 | this._compressionRatio 228 | ); 229 | }; 230 | })(), 231 | set: (function () { 232 | return function (e) { 233 | (this._compressionRatio = e), 234 | this.imprintValue_forKey(e, "compression-ratio"); 235 | }; 236 | })(), 237 | }, 238 | { 239 | key: "reversed", 240 | get: (function () { 241 | return function () { 242 | return ( 243 | void 0 == this._reversed && 244 | (this._reversed = 1 == this.loadValueForKey("reversed")), 245 | this._reversed 246 | ); 247 | }; 248 | })(), 249 | set: (function () { 250 | return function (e) { 251 | (this._reversed = e), this.imprintValue_forKey(e, "reversed"); 252 | }; 253 | })(), 254 | }, 255 | ]), 256 | i(e, [ 257 | { 258 | key: "exportRequest_lessThan52", 259 | value: (function () { 260 | return function () { 261 | var e = NSClassFromString( 262 | "SketchModel.MSImmutableLayerAncestry" 263 | ) 264 | .alloc() 265 | .initWithMutableLayer(t.selection[0]), 266 | r = MSExportFormat.formatWithScale_name_fileFormat( 267 | this.pixelDensity, 268 | "Angle", 269 | "png" 270 | ); 271 | return MSExportRequest.exportRequestsFromLayerAncestry_exportFormats( 272 | e, 273 | [r] 274 | ).firstObject(); 275 | }; 276 | })(), 277 | }, 278 | { 279 | key: "exporter", 280 | value: (function () { 281 | return function () { 282 | var e = this.context.document.colorSpace(); 283 | if (BCSketchInfo.shared().metadata().appVersion < 52) 284 | return MSExporter.exporterForRequest_colorSpace( 285 | this.exportRequest_lessThan52(), 286 | e 287 | ); 288 | var t = MSExportFormat.alloc().init(); 289 | (t.fileFormat = "png"), (t.scale = this.pixelDensity); 290 | var r = 291 | MSExportRequest.exportRequestsFromExportableLayer_exportFormats_useIDForName( 292 | this.artboard, 293 | [t], 294 | !0 295 | ).firstObject(); 296 | return MSExporter.exporterForRequest_colorSpace(r, e); 297 | }; 298 | })(), 299 | }, 300 | { 301 | key: "ciImage", 302 | value: (function () { 303 | return function () { 304 | var e = this.exporter().bitmapImageRep(); 305 | return CIImage.alloc().initWithCGImage(e.CGImage()); 306 | }; 307 | })(), 308 | }, 309 | { 310 | key: "guessRotationAndReversion", 311 | value: (function () { 312 | return function () { 313 | if (1 === this.loadValueForKey("guessed-rotation")) 314 | print( 315 | "⚠️ Angle has already guessed rotation and symmetry for this shape" 316 | ); 317 | else { 318 | var e = this.verticesLengths, 319 | t = void 0; 320 | if (this.artboard.class() != MSSymbolMaster) { 321 | t = this.artboard.frame(); 322 | var r = e[0] > e[1], 323 | n = t.width() > t.height(); 324 | r && (print("🛑 HORIZONTAL"), this.rotate()), 325 | n && 326 | (print("🛑 HAS HORIZONTAL ARTBOARD"), this.rotate()); 327 | var i = this.pointsFromBezierPath, 328 | o = Math.min.apply( 329 | Math, 330 | a( 331 | i.map(function (e) { 332 | return e.y; 333 | }) 334 | ) 335 | ), 336 | s = i[this.mappedIndexFor(0)], 337 | u = i[this.mappedIndexFor(1)]; 338 | s.y != o && 339 | u.y != o && 340 | (print("🛑 UPSIDE DOWN"), this.rotate(), this.rotate()); 341 | var c = BCSketchInfo.shared().metadata().appVersion; 342 | if (c < 50 || c >= 52) { 343 | var l = this.shorlaceSum(); 344 | l < 0 345 | ? (print("🛑 COUNTERCLOCKWISE"), 346 | this.reverseSymmetry()) 347 | : l > 0 348 | ? print("🛑 CLOCKWISE") 349 | : print("🛑 UNDEFINED CHIRALITY"); 350 | } else 351 | c < 52 && 352 | (1 === 353 | this.targetPath.contours().firstObject().isClockwise() 354 | ? (this.reverseSymmetry(), print("🛑 CLOCKWISE")) 355 | : print("🛑 COUNTERCLOCKWISE"), 356 | print("🛑 UNDEFINED CHIRALITY")); 357 | print( 358 | "🔄↔️ Angle has just guessed rotation and symmetry for this shape" 359 | ), 360 | this.imprintValue_forKey(!0, "guessed-rotation"); 361 | } 362 | } 363 | }; 364 | })(), 365 | }, 366 | { 367 | key: "shorlaceSum", 368 | value: (function () { 369 | return function () { 370 | var e = this.pointsFromBezierPath, 371 | t = Math.max.apply( 372 | Math, 373 | a( 374 | e.map(function (e) { 375 | return e.y; 376 | }) 377 | ) 378 | ); 379 | return Array.from({ length: 4 }, function (e, t) { 380 | return t; 381 | }).reduce(function (r, n) { 382 | return ( 383 | r + 384 | (-e[n].x + e[(n + 1) % 4].x) * 385 | (2 * t - e[n].y - e[(n + 1) % 4].y) 386 | ); 387 | }, 0); 388 | }; 389 | })(), 390 | }, 391 | { 392 | key: "maximumVerticesWidthAndHeight", 393 | value: (function () { 394 | return function () { 395 | var e = this.verticesLengths.rotated(this.rotation % 2); 396 | return [Math.max(e[0], e[2]), Math.max(e[1], e[3])]; 397 | }; 398 | })(), 399 | }, 400 | { 401 | key: "rotate", 402 | value: (function () { 403 | return function () { 404 | this.rotation = (this.rotation + (this.reversed ? 1 : 3)) % 4; 405 | }; 406 | })(), 407 | }, 408 | { 409 | key: "reverseSymmetry", 410 | value: (function () { 411 | return function () { 412 | this.rotate(), (this.reversed = !this.reversed); 413 | }; 414 | })(), 415 | }, 416 | { 417 | key: "mappedIndexFor", 418 | value: (function () { 419 | return function (e) { 420 | return this.reversed 421 | ? [0, 3, 2, 1][(e + this.rotation) % 4] 422 | : (e + this.rotation) % 4; 423 | }; 424 | })(), 425 | }, 426 | { 427 | key: "lossyCompressionOfImage_atRate", 428 | value: (function () { 429 | return function (e, t) { 430 | var r = NSBitmapImageRep.alloc().initWithCIImage(e), 431 | n = NSMutableDictionary.dictionary(); 432 | n.setObject_forKey( 433 | NSTIFFCompressionJPEG, 434 | NSImageCompressionMethod 435 | ), 436 | n.setObject_forKey(t, NSImageCompressionFactor), 437 | n.setObject_forKey( 438 | NSColor.whiteColor(), 439 | NSImageFallbackBackgroundColor 440 | ); 441 | var i = r.representationUsingType_properties( 442 | NSJPEGFileType, 443 | n 444 | ); 445 | return NSImage.alloc().initWithData(i); 446 | }; 447 | })(), 448 | }, 449 | { 450 | key: "pixelAccurateRepresentationOfImage", 451 | value: (function () { 452 | return function (e) { 453 | var t = NSCIImageRep.alloc().initWithCIImage(e), 454 | r = NSImage.alloc().initWithSize(t.size()); 455 | return r.addRepresentation(t), r; 456 | }; 457 | })(), 458 | }, 459 | { 460 | key: "pointsAreValid_lessThan50", 461 | get: (function () { 462 | return function () { 463 | var e = this.pointsFromBezierPath; 464 | return null !== e && 7 === e.length; 465 | }; 466 | })(), 467 | }, 468 | { 469 | key: "pointsAreValid_lessThan52", 470 | get: (function () { 471 | return function () { 472 | var e = this.targetPath.contours().firstObject(), 473 | t = Array.fromNSArray(e.segments()); 474 | return ( 475 | null !== t && 476 | 4 === t.length && 477 | !t.some(function (e) { 478 | return e.segmentType() != s.linear; 479 | }) 480 | ); 481 | }; 482 | })(), 483 | }, 484 | { 485 | key: "pointsAreValid", 486 | get: (function () { 487 | return function () { 488 | var e = BCSketchInfo.shared().metadata().appVersion; 489 | if (e < 50) return this.pointsAreValid_lessThan50; 490 | if (e < 52) return this.pointsAreValid_lessThan52; 491 | var t = this.targetLayer.points(); 492 | return ( 493 | null !== t && 494 | 4 === t.length && 495 | !t.some(function (e) { 496 | return !e.isStraight(); 497 | }) 498 | ); 499 | }; 500 | })(), 501 | }, 502 | { 503 | key: "pointsFromBezierPath", 504 | get: (function () { 505 | return function () { 506 | var e = this, 507 | t = BCSketchInfo.shared().metadata().appVersion; 508 | if (t < 50) { 509 | var r = this.targetPath.elementCount(); 510 | return 7 != r 511 | ? null 512 | : Array.from({ length: r }, function (e, t) { 513 | return t; 514 | }).map(function (t) { 515 | var r = MOPointer.alloc().initWithValue_( 516 | CGPointMake(0, 0) 517 | ); 518 | return ( 519 | e.targetPath.elementAtIndex_associatedPoints_(t, r), 520 | r.value() 521 | ); 522 | }); 523 | } 524 | if (t < 52) { 525 | var n = this.targetPath.contours().firstObject(); 526 | return Array.fromNSArray(n.segments()).map(function (e) { 527 | return e.endPoint1(); 528 | }); 529 | } 530 | var i = this.targetLayer.rect().size; 531 | return Array.fromNSArray(this.targetLayer.points()) 532 | .map(function (e) { 533 | return e.point(); 534 | }) 535 | .map(function (e) { 536 | return { 537 | x: Number(e.x) * Number(i.width), 538 | y: Number(e.y) * Number(i.height), 539 | }; 540 | }); 541 | }; 542 | })(), 543 | }, 544 | { 545 | key: "verticesLengths", 546 | get: (function () { 547 | return function () { 548 | var e = this.pointsFromBezierPath; 549 | return Array.from({ length: 4 }, function (e, t) { 550 | return t; 551 | }).map(function (t) { 552 | var r = (t + 1) % 4, 553 | n = e[t].x - e[r].x, 554 | i = e[t].y - e[r].y; 555 | return Math.sqrt(Math.pow(n, 2) + Math.pow(i, 2)); 556 | }); 557 | }; 558 | })(), 559 | }, 560 | { 561 | key: "normalizedCIVectors", 562 | get: (function () { 563 | return function () { 564 | var e = this.pointsFromBezierPath, 565 | t = Math.max.apply( 566 | Math, 567 | a( 568 | e.map(function (e) { 569 | return e.y; 570 | }) 571 | ) 572 | ), 573 | r = this.pixelDensity; 574 | return e.map(function (e) { 575 | return CIVector.vectorWithX_Y(e.x * r, (t - e.y) * r); 576 | }); 577 | }; 578 | })(), 579 | }, 580 | { 581 | key: "transformedImage", 582 | get: (function () { 583 | return function () { 584 | var e = this.normalizedCIVectors, 585 | t = CIFilter.filterWithName("CIPerspectiveTransform"); 586 | t.setValue_forKey(e[this.mappedIndexFor(0)], "inputTopLeft"), 587 | t.setValue_forKey( 588 | e[this.mappedIndexFor(1)], 589 | "inputTopRight" 590 | ), 591 | t.setValue_forKey( 592 | e[this.mappedIndexFor(2)], 593 | "inputBottomRight" 594 | ), 595 | t.setValue_forKey( 596 | e[this.mappedIndexFor(3)], 597 | "inputBottomLeft" 598 | ); 599 | var r = this.ciImage(); 600 | t.setValue_forKey(r, "inputImage"); 601 | var n = t.valueForKey("outputImage"); 602 | if (n) { 603 | var i = void 0, 604 | a = o.CompressionRatio[this.compressionRatio].ratio; 605 | return ( 606 | (i = 607 | 1 != a 608 | ? this.lossyCompressionOfImage_atRate(n, a) 609 | : this.pixelAccurateRepresentationOfImage(n)), 610 | BCSketchInfo.shared().metadata().appVersion < 47 611 | ? MSImageData.alloc().initWithImage_convertColorSpace( 612 | i, 613 | !0 614 | ) 615 | : MSImageData.alloc().initWithImage_(i) 616 | ); 617 | } 618 | print("🛑 Unable to form perspective image"); 619 | }; 620 | })(), 621 | }, 622 | ]), 623 | e 624 | ); 625 | })(); 626 | r.default = u; 627 | }, 628 | function (e, t, r) { 629 | Object.defineProperty(t, "__esModule", { value: !0 }); 630 | var n, 631 | i = (function () { 632 | return function (e, t) { 633 | if (Array.isArray(e)) return e; 634 | if (Symbol.iterator in Object(e)) 635 | return (function (e, t) { 636 | var r = [], 637 | n = !0, 638 | i = !1, 639 | o = void 0; 640 | try { 641 | for ( 642 | var a, s = e[Symbol.iterator](); 643 | !(n = (a = s.next()).done) && 644 | (r.push(a.value), !t || r.length !== t); 645 | n = !0 646 | ); 647 | } catch (e) { 648 | (i = !0), (o = e); 649 | } finally { 650 | try { 651 | !n && s.return && s.return(); 652 | } finally { 653 | if (i) throw o; 654 | } 655 | } 656 | return r; 657 | })(e, t); 658 | throw new TypeError( 659 | "Invalid attempt to destructure non-iterable instance" 660 | ); 661 | }; 662 | })(), 663 | o = (function () { 664 | function e(e, t) { 665 | for (var r = 0; r < t.length; r++) { 666 | var n = t[r]; 667 | (n.enumerable = n.enumerable || !1), 668 | (n.configurable = !0), 669 | "value" in n && (n.writable = !0), 670 | Object.defineProperty(e, n.key, n); 671 | } 672 | } 673 | return function (t, r, n) { 674 | return r && e(t.prototype, r), n && e(t, n), t; 675 | }; 676 | })(), 677 | a = r(1), 678 | s = (n = a) && n.__esModule ? n : { default: n }, 679 | u = r(0); 680 | function c(e, t) { 681 | if (!e) 682 | throw new ReferenceError( 683 | "this hasn't been initialised - super() hasn't been called" 684 | ); 685 | return !t || ("object" != typeof t && "function" != typeof t) ? e : t; 686 | } 687 | var l = (function (e) { 688 | function t() { 689 | var e = 690 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 691 | !(function (e, t) { 692 | if (!(e instanceof t)) 693 | throw new TypeError("Cannot call a class as a function"); 694 | })(this, t); 695 | var r = c( 696 | this, 697 | (t.__proto__ || Object.getPrototypeOf(t)).call(this, e) 698 | ); 699 | if ( 700 | ((r.targetLayer = e.override.affectedLayer()), 701 | r.targetLayer.class() === MSImmutableBitmapLayer) 702 | ) 703 | return c(r, u.Error.symbolWithBitMapLayer); 704 | var n = BCSketchInfo.shared().metadata().appVersion; 705 | n < 50 706 | ? (r.targetPath = e.override.affectedLayer().bezierPath()) 707 | : n < 52 && 708 | (r.targetPath = e.override 709 | .affectedLayer() 710 | .pathInFrameWithTransforms()); 711 | var i = e.override.overridePoint().parent(); 712 | return ( 713 | null !== i && (r.overrideLayer = i), 714 | r.pointsAreValid ? r : c(r, u.Error.unsupportedShapePath) 715 | ); 716 | } 717 | return ( 718 | (function (e, t) { 719 | if ("function" != typeof t && null !== t) 720 | throw new TypeError( 721 | "Super expression must either be null or a function, not " + 722 | typeof t 723 | ); 724 | (e.prototype = Object.create(t && t.prototype, { 725 | constructor: { 726 | value: e, 727 | enumerable: !1, 728 | writable: !0, 729 | configurable: !0, 730 | }, 731 | })), 732 | t && 733 | (Object.setPrototypeOf 734 | ? Object.setPrototypeOf(e, t) 735 | : (e.__proto__ = t)); 736 | })(t, s["default"]), 737 | o(t, [ 738 | { 739 | key: "applyImage", 740 | value: (function () { 741 | return function () { 742 | var e = this.targetLayer.objectID(), 743 | t = 744 | this.selectedLayer.overrides() || 745 | NSDictionary.dictionary(), 746 | r = NSMutableDictionary.dictionaryWithDictionary(t); 747 | r.setObject_forKey(this.transformedImage, e), 748 | (this.selectedLayer.overrides = r); 749 | }; 750 | })(), 751 | }, 752 | { 753 | key: "estimatePixelDensity", 754 | value: (function () { 755 | return function () { 756 | var e = this.maximumVerticesWidthAndHeight(), 757 | t = i(e, 2), 758 | r = t[0], 759 | n = t[1], 760 | o = 761 | (this.selectedLayer.rect().size.width * r) / 762 | (this.selectedLayer.naturalSize().width * 763 | this.artboard.rect().size.width), 764 | a = 765 | (this.selectedLayer.rect().size.height * n) / 766 | (this.selectedLayer.naturalSize().height * 767 | this.artboard.rect().size.height); 768 | return o > a ? o : a; 769 | }; 770 | })(), 771 | }, 772 | { 773 | key: "description", 774 | value: (function () { 775 | return function () { 776 | return ( 777 | this.selectedLayer.name() + " " + this.targetLayer.name() 778 | ); 779 | }; 780 | })(), 781 | }, 782 | ]), 783 | t 784 | ); 785 | })(); 786 | t.default = l; 787 | }, 788 | function (e, t, r) { 789 | Object.defineProperty(t, "__esModule", { value: !0 }), 790 | (t.default = function (e) { 791 | var t = e.selection[0]; 792 | if (null != t) { 793 | var r = Array.fromNSArray(t); 794 | if (0 != r.length) { 795 | var n = o.default.tryCreating({ for: r, in: e }), 796 | i = n.filter(function (e) { 797 | return e instanceof o.default; 798 | }), 799 | u = n.filter(function (e) { 800 | return !(e instanceof o.default); 801 | }); 802 | 0 != i.length 803 | ? (i.forEach(function (e) { 804 | e.reverseSymmetry(), e.applyImage(); 805 | }), 806 | a.show({ 807 | message: "Angle flipped! ↔️", 808 | inDocument: e.document, 809 | })) 810 | : a.show({ message: u[0].message, inDocument: e.document }); 811 | } else 812 | a.show({ 813 | message: s.Error.emptySelection.message, 814 | inDocument: e.document, 815 | }); 816 | } else 817 | a.show({ 818 | message: s.Error.emptySelection.message, 819 | inDocument: e.document, 820 | }); 821 | }); 822 | var n, 823 | i = r(1), 824 | o = (n = i) && n.__esModule ? n : { default: n }, 825 | a = (function (e) { 826 | if (e && e.__esModule) return e; 827 | var t = {}; 828 | if (null != e) 829 | for (var r in e) 830 | Object.prototype.hasOwnProperty.call(e, r) && (t[r] = e[r]); 831 | return (t.default = e), t; 832 | })(r(5)), 833 | s = r(0); 834 | }, 835 | function (e, t) { 836 | Object.defineProperty(t, "__esModule", { value: !0 }); 837 | t.CompressionRatio = [ 838 | { selectionLabel: "Best", ratio: 1 }, 839 | { selectionLabel: "Better", ratio: 0.9 }, 840 | { selectionLabel: "Good", ratio: 0.8 }, 841 | { selectionLabel: "Average", ratio: 0.7 }, 842 | ]; 843 | }, 844 | function (e, t, r) { 845 | Object.defineProperty(t, "__esModule", { value: !0 }), 846 | (t.show = function (e) { 847 | var t = e.message, 848 | r = e.inDocument; 849 | void 0 != r && void 0 != r.showMessage && r.showMessage(t); 850 | print(t); 851 | }), 852 | (t.filterPossibleArtboards = function (e) { 853 | var t = e.class(); 854 | switch (t) { 855 | case MSArtboardGroup: 856 | var r = e, 857 | n = r.frame(); 858 | if (n.width() < 250 || n.height() < 250) return !1; 859 | var i = n.width() / n.height(); 860 | if ((i > 1 && (i = 1 / i), i < 0.4)) return !1; 861 | break; 862 | case MSSymbolMaster: 863 | return !1; 864 | default: 865 | return print(t), !1; 866 | } 867 | return !0; 868 | }), 869 | (t.compareByRatioAndAlphabet = function (e, t) { 870 | var r = e.frame(), 871 | n = t.frame(), 872 | i = r.width() / r.height(); 873 | i > 1 && (i = 1 / i); 874 | var o = i > 0.4 && i < 0.8, 875 | a = n.width() / n.height(); 876 | a > 1 && (a = 1 / a); 877 | var s = a > 0.4 && a < 0.8; 878 | if (o && !s) return !1; 879 | if (s && !o) return !0; 880 | if (i == a) return e.name() > t.name(); 881 | return i > a; 882 | }), 883 | (t.introspect = function (e) { 884 | var t = e.class().mocha(); 885 | print("-----------------------------------------------"), 886 | print("PROPERTIES-------------------------------------"), 887 | print("-----------------------------------------------"), 888 | print(t.properties()), 889 | print(t.propertiesWithAncestors()), 890 | print("-----------------------------------------------"), 891 | print("INSTANCE METHODS-------------------------------"), 892 | print("-----------------------------------------------"), 893 | print(t.instanceMethods()), 894 | print(t.instanceMethodsWithAncestors()), 895 | print("-----------------------------------------------"), 896 | print("CLASS METHODS----------------------------------"), 897 | print("-----------------------------------------------"), 898 | print(t.classMethods()), 899 | print(t.classMethodsWithAncestors()), 900 | print("-----------------------------------------------"), 901 | print("PROTOCOLS--------------------------------------"), 902 | print("-----------------------------------------------"), 903 | print(t.protocols()), 904 | print(t.protocolsWithAncestors()); 905 | }), 906 | (t.createLabel = function (e, t, r) { 907 | var n = NSTextField.alloc().initWithFrame(r); 908 | return ( 909 | n.setStringValue(e), 910 | n.setFont(NSFont.boldSystemFontOfSize(t)), 911 | n.setBezeled(!1), 912 | n.setDrawsBackground(!1), 913 | n.setEditable(!1), 914 | n.setSelectable(!1), 915 | n 916 | ); 917 | }), 918 | (t.popUpButtonsforRectangleIndexer_withTitleIndexer_andImageIndexer_defaultSelected_onIndex = 919 | function (e, t, r, n) { 920 | var i = NSPopUpButton.alloc().initWithFrame(e(n)); 921 | i.addItemsWithTitles(t), 922 | null != r && 923 | ((i.imageScaling = NSImageScaleProportionallyUpOrDown), 924 | Array.fromNSArray(i.itemArray()).forEach(function (e, t, n) { 925 | e.image = r[t]; 926 | })); 927 | return i; 928 | }), 929 | (t.smallImagesFromArtboard = function (e) { 930 | if (e.class() == MSSymbolMaster) return print(e), null; 931 | void 0 == e.frame && print(e); 932 | var t = e.frame().width(), 933 | r = e.frame().height(), 934 | n = t / r; 935 | n > 1 && (n = 1 / n); 936 | if (n > 0.8 || n < 0.4) return null; 937 | var i = NSClassFromString("SketchModel.MSImmutableLayerAncestry") 938 | .alloc() 939 | .initWithMutableLayer(e), 940 | o = 48 / (t > r ? t : r), 941 | a = MSExportFormat.formatWithScale_name_fileFormat(o, "", "png"), 942 | s = MSExportRequest.exportRequestsFromLayerAncestry_exportFormats( 943 | i, 944 | [a] 945 | ).firstObject(); 946 | return MSExporter.exporterForRequest_colorSpace( 947 | s, 948 | NSColorSpace.sRGBColorSpace() 949 | ).previewImage(); 950 | }); 951 | var n = u(r(1)), 952 | i = u(r(6)), 953 | o = u(r(2)), 954 | a = u(r(7)), 955 | s = r(0); 956 | function u(e) { 957 | return e && e.__esModule ? e : { default: e }; 958 | } 959 | (n.default.tryCreating = function (e) { 960 | var t = e.for, 961 | r = e.in; 962 | return t 963 | .map(function (e) { 964 | switch (e.class()) { 965 | case MSSymbolInstance: 966 | var t = Array.fromNSArray(e.availableOverrides()) || [], 967 | n = t 968 | .filter(function (e) { 969 | return e.currentValue().class() == MSImageData; 970 | }) 971 | .map(function (t) { 972 | return new o.default({ 973 | selectedLayer: e, 974 | context: r, 975 | override: t, 976 | }); 977 | }), 978 | u = t 979 | .map(function (e) { 980 | return e.children(); 981 | }) 982 | .filter(function (e) { 983 | return null != e; 984 | }) 985 | .map(Array.fromNSArray) 986 | .reduce(function (e, t) { 987 | return e.concat(t); 988 | }, []) 989 | .filter(function (e) { 990 | return e.class() == MSAvailableOverride; 991 | }) 992 | .map(function (t) { 993 | return new i.default({ 994 | override: t, 995 | selectedLayer: e, 996 | context: r, 997 | }); 998 | }); 999 | return n.concat(u); 1000 | case MSShapeGroup: 1001 | case MSShapePathLayer: 1002 | case MSRectangleShape: 1003 | return new a.default({ selectedLayer: e, context: r }); 1004 | default: 1005 | return s.Error.unsupportedElement; 1006 | } 1007 | }) 1008 | .reduce(function (e, t, r, n) { 1009 | return e.concat(t); 1010 | }, []); 1011 | }), 1012 | (Array.fromNSArray = function (e) { 1013 | for (var t = [], r = 0; r < e.length; r++) t.push(e[r]); 1014 | return t; 1015 | }), 1016 | (Array.prototype.print = function () { 1017 | return this.map(function (e) { 1018 | return print(e), e; 1019 | }); 1020 | }); 1021 | }, 1022 | function (e, t, r) { 1023 | Object.defineProperty(t, "__esModule", { value: !0 }); 1024 | var n, 1025 | i = (function () { 1026 | function e(e, t) { 1027 | for (var r = 0; r < t.length; r++) { 1028 | var n = t[r]; 1029 | (n.enumerable = n.enumerable || !1), 1030 | (n.configurable = !0), 1031 | "value" in n && (n.writable = !0), 1032 | Object.defineProperty(e, n.key, n); 1033 | } 1034 | } 1035 | return function (t, r, n) { 1036 | return r && e(t.prototype, r), n && e(t, n), t; 1037 | }; 1038 | })(), 1039 | o = (function () { 1040 | return function e(t, r, n) { 1041 | null === t && (t = Function.prototype); 1042 | var i = Object.getOwnPropertyDescriptor(t, r); 1043 | if (void 0 === i) { 1044 | var o = Object.getPrototypeOf(t); 1045 | return null === o ? void 0 : e(o, r, n); 1046 | } 1047 | if ("value" in i) return i.value; 1048 | var a = i.get; 1049 | return void 0 !== a ? a.call(n) : void 0; 1050 | }; 1051 | })(), 1052 | a = r(2), 1053 | s = (n = a) && n.__esModule ? n : { default: n }; 1054 | r(0); 1055 | var u = (function (e) { 1056 | function t() { 1057 | var e = 1058 | arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 1059 | !(function (e, t) { 1060 | if (!(e instanceof t)) 1061 | throw new TypeError("Cannot call a class as a function"); 1062 | })(this, t); 1063 | var r = (function (e, t) { 1064 | if (!e) 1065 | throw new ReferenceError( 1066 | "this hasn't been initialised - super() hasn't been called" 1067 | ); 1068 | return !t || ("object" != typeof t && "function" != typeof t) 1069 | ? e 1070 | : t; 1071 | })(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)); 1072 | return (r.override = e.override), r; 1073 | } 1074 | return ( 1075 | (function (e, t) { 1076 | if ("function" != typeof t && null !== t) 1077 | throw new TypeError( 1078 | "Super expression must either be null or a function, not " + 1079 | typeof t 1080 | ); 1081 | (e.prototype = Object.create(t && t.prototype, { 1082 | constructor: { 1083 | value: e, 1084 | enumerable: !1, 1085 | writable: !0, 1086 | configurable: !0, 1087 | }, 1088 | })), 1089 | t && 1090 | (Object.setPrototypeOf 1091 | ? Object.setPrototypeOf(e, t) 1092 | : (e.__proto__ = t)); 1093 | })(t, s["default"]), 1094 | i(t, [ 1095 | { 1096 | key: "loadValueForKey", 1097 | value: (function () { 1098 | return function (e) { 1099 | return o( 1100 | t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), 1101 | "loadValueForKey", 1102 | this 1103 | ).call(this, this.targetLayer.objectID() + "-" + e); 1104 | }; 1105 | })(), 1106 | }, 1107 | { 1108 | key: "imprintValue_forKey", 1109 | value: (function () { 1110 | return function (e, r) { 1111 | o( 1112 | t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), 1113 | "imprintValue_forKey", 1114 | this 1115 | ).call(this, e, this.targetLayer.objectID() + "-" + r); 1116 | }; 1117 | })(), 1118 | }, 1119 | { 1120 | key: "applyImage", 1121 | value: (function () { 1122 | return function () { 1123 | var e = 1124 | this.selectedLayer.overrides() || 1125 | NSDictionary.dictionary(), 1126 | t = NSMutableDictionary.dictionaryWithDictionary(e), 1127 | r = (this.overrideLayer + "").replace("_symbolID", ""), 1128 | n = t.objectForKey(r) || NSMutableDictionary.dictionary(), 1129 | i = NSMutableDictionary.dictionaryWithDictionary(n), 1130 | o = this.targetLayer.objectID(); 1131 | i.setObject_forKey(this.transformedImage, o), 1132 | t.setObject_forKey(i, r), 1133 | (this.selectedLayer.overrides = t); 1134 | }; 1135 | })(), 1136 | }, 1137 | { 1138 | key: "description", 1139 | value: (function () { 1140 | return function () { 1141 | return ( 1142 | this.override.parent().affectedLayer().name() + 1143 | " " + 1144 | this.targetLayer.name() 1145 | ); 1146 | }; 1147 | })(), 1148 | }, 1149 | ]), 1150 | t 1151 | ); 1152 | })(); 1153 | t.default = u; 1154 | }, 1155 | function (e, t, r) { 1156 | Object.defineProperty(t, "__esModule", { value: !0 }); 1157 | var n, 1158 | i = (function () { 1159 | return function (e, t) { 1160 | if (Array.isArray(e)) return e; 1161 | if (Symbol.iterator in Object(e)) 1162 | return (function (e, t) { 1163 | var r = [], 1164 | n = !0, 1165 | i = !1, 1166 | o = void 0; 1167 | try { 1168 | for ( 1169 | var a, s = e[Symbol.iterator](); 1170 | !(n = (a = s.next()).done) && 1171 | (r.push(a.value), !t || r.length !== t); 1172 | n = !0 1173 | ); 1174 | } catch (e) { 1175 | (i = !0), (o = e); 1176 | } finally { 1177 | try { 1178 | !n && s.return && s.return(); 1179 | } finally { 1180 | if (i) throw o; 1181 | } 1182 | } 1183 | return r; 1184 | })(e, t); 1185 | throw new TypeError( 1186 | "Invalid attempt to destructure non-iterable instance" 1187 | ); 1188 | }; 1189 | })(), 1190 | o = (function () { 1191 | function e(e, t) { 1192 | for (var r = 0; r < t.length; r++) { 1193 | var n = t[r]; 1194 | (n.enumerable = n.enumerable || !1), 1195 | (n.configurable = !0), 1196 | "value" in n && (n.writable = !0), 1197 | Object.defineProperty(e, n.key, n); 1198 | } 1199 | } 1200 | return function (t, r, n) { 1201 | return r && e(t.prototype, r), n && e(t, n), t; 1202 | }; 1203 | })(), 1204 | a = r(1), 1205 | s = (n = a) && n.__esModule ? n : { default: n }, 1206 | u = r(0); 1207 | function c(e, t) { 1208 | if (!e) 1209 | throw new ReferenceError( 1210 | "this hasn't been initialised - super() hasn't been called" 1211 | ); 1212 | return !t || ("object" != typeof t && "function" != typeof t) ? e : t; 1213 | } 1214 | var l = { solid: 0, gradient: 1, pattern: 4, noise: 5 }, 1215 | f = (function (e) { 1216 | function t() { 1217 | var e = 1218 | arguments.length > 0 && void 0 !== arguments[0] 1219 | ? arguments[0] 1220 | : {}; 1221 | !(function (e, t) { 1222 | if (!(e instanceof t)) 1223 | throw new TypeError("Cannot call a class as a function"); 1224 | })(this, t); 1225 | var r = c( 1226 | this, 1227 | (t.__proto__ || Object.getPrototypeOf(t)).call(this, e) 1228 | ); 1229 | r.targetLayer = r.selectedLayer; 1230 | var n = BCSketchInfo.shared().metadata().appVersion; 1231 | return ( 1232 | n < 50 1233 | ? (r.targetPath = r.selectedLayer.bezierPath()) 1234 | : n < 52 && 1235 | (r.targetPath = r.selectedLayer.pathInFrameWithTransforms()), 1236 | r.pointsAreValid ? r : c(r, u.Error.unsupportedShapePath) 1237 | ); 1238 | } 1239 | return ( 1240 | (function (e, t) { 1241 | if ("function" != typeof t && null !== t) 1242 | throw new TypeError( 1243 | "Super expression must either be null or a function, not " + 1244 | typeof t 1245 | ); 1246 | (e.prototype = Object.create(t && t.prototype, { 1247 | constructor: { 1248 | value: e, 1249 | enumerable: !1, 1250 | writable: !0, 1251 | configurable: !0, 1252 | }, 1253 | })), 1254 | t && 1255 | (Object.setPrototypeOf 1256 | ? Object.setPrototypeOf(e, t) 1257 | : (e.__proto__ = t)); 1258 | })(t, s["default"]), 1259 | o(t, [ 1260 | { 1261 | key: "applyImage", 1262 | value: (function () { 1263 | return function () { 1264 | var e = MSStyleFill.alloc().init(); 1265 | e.setImage(this.transformedImage), 1266 | (e.fillType = l.pattern), 1267 | this.targetLayer.style().removeAllStyleFills(), 1268 | this.targetLayer.style().addStyleFill(e); 1269 | }; 1270 | })(), 1271 | }, 1272 | { 1273 | key: "estimatePixelDensity", 1274 | value: (function () { 1275 | return function () { 1276 | var e = this.maximumVerticesWidthAndHeight(), 1277 | t = i(e, 2), 1278 | r = t[0], 1279 | n = t[1], 1280 | o = r / this.artboard.rect().size.width, 1281 | a = n / this.artboard.rect().size.height; 1282 | return o > a ? o : a; 1283 | }; 1284 | })(), 1285 | }, 1286 | { 1287 | key: "description", 1288 | value: (function () { 1289 | return function () { 1290 | return this.targetLayer.name(); 1291 | }; 1292 | })(), 1293 | }, 1294 | ]), 1295 | t 1296 | ); 1297 | })(); 1298 | t.default = f; 1299 | }, 1300 | ]); 1301 | "default" === e && "function" == typeof r ? r(t) : r[e](t); 1302 | } 1303 | that.onRun = __skpm_run.bind(this, "default"); 1304 | -------------------------------------------------------------------------------- /Angle.sketchplugin/Contents/Sketch/symmetry.js~HEAD: -------------------------------------------------------------------------------- 1 | var that = this; 2 | function __skpm_run (key, context) { 3 | that.context = context; 4 | 5 | var exports = 6 | /******/ (function(modules) { // webpackBootstrap 7 | /******/ // The module cache 8 | /******/ var installedModules = {}; 9 | /******/ 10 | /******/ // The require function 11 | /******/ function __webpack_require__(moduleId) { 12 | /******/ 13 | /******/ // Check if module is in cache 14 | /******/ if(installedModules[moduleId]) { 15 | /******/ return installedModules[moduleId].exports; 16 | /******/ } 17 | /******/ // Create a new module (and put it into the cache) 18 | /******/ var module = installedModules[moduleId] = { 19 | /******/ i: moduleId, 20 | /******/ l: false, 21 | /******/ exports: {} 22 | /******/ }; 23 | /******/ 24 | /******/ // Execute the module function 25 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 26 | /******/ 27 | /******/ // Flag the module as loaded 28 | /******/ module.l = true; 29 | /******/ 30 | /******/ // Return the exports of the module 31 | /******/ return module.exports; 32 | /******/ } 33 | /******/ 34 | /******/ 35 | /******/ // expose the modules object (__webpack_modules__) 36 | /******/ __webpack_require__.m = modules; 37 | /******/ 38 | /******/ // expose the module cache 39 | /******/ __webpack_require__.c = installedModules; 40 | /******/ 41 | /******/ // define getter function for harmony exports 42 | /******/ __webpack_require__.d = function(exports, name, getter) { 43 | /******/ if(!__webpack_require__.o(exports, name)) { 44 | /******/ Object.defineProperty(exports, name, { 45 | /******/ configurable: false, 46 | /******/ enumerable: true, 47 | /******/ get: getter 48 | /******/ }); 49 | /******/ } 50 | /******/ }; 51 | /******/ 52 | /******/ // getDefaultExport function for compatibility with non-harmony modules 53 | /******/ __webpack_require__.n = function(module) { 54 | /******/ var getter = module && module.__esModule ? 55 | /******/ function getDefault() { return module['default']; } : 56 | /******/ function getModuleExports() { return module; }; 57 | /******/ __webpack_require__.d(getter, 'a', getter); 58 | /******/ return getter; 59 | /******/ }; 60 | /******/ 61 | /******/ // Object.prototype.hasOwnProperty.call 62 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 63 | /******/ 64 | /******/ // __webpack_public_path__ 65 | /******/ __webpack_require__.p = ""; 66 | /******/ 67 | /******/ // Load entry module and return exports 68 | /******/ return __webpack_require__(__webpack_require__.s = 3); 69 | /******/ }) 70 | /************************************************************************/ 71 | /******/ ({ 72 | 73 | /***/ 3: 74 | /***/ (function(module, exports) { 75 | 76 | throw new Error("Module build failed: Error: ENOENT: no such file or directory, open '/Users/tmergulhao/Source/Angle for Sketch/src/symmetry.js'"); 77 | 78 | /***/ }) 79 | 80 | /******/ }); 81 | if (key === 'default' && typeof exports === 'function') { 82 | exports(context); 83 | } else { 84 | exports[key](context); 85 | } 86 | } 87 | that['onRun'] = __skpm_run.bind(this, 'default') 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://cl.ly/2g211R2e2U3B/download/Angle-Logo.png) 2 | Angle is a free and lightweight Sketch plugin for applying perspective transforms for your mockups. You can get the **[starting template](https://angle.sh)** to help you design beautiful mockups from scratch. Check out the **[full tutorial](https://www.youtube.com/watch?v=5uffgI-J29I)**. 3 | 4 | # Getting Started 5 | 6 | The only thing that Angle needs to work is an Artboard containing your Screen. That's it! 7 | ![](https://cl.ly/0D1l3y2D453a/download/Angle-GIF.gif) 8 | 9 | ## Installation 10 | 11 | Make sure you have the latest version of Sketch (72+) running on macOS Catalina (10.15.0) or newer. 12 | 13 | - [Download the zip file](https://github.com/MengTo/Angle-Sketch-Plugin/archive/master.zip). 14 | - Double-click on Angle.sketchplugin 15 | 16 | #### Important note: 17 | - This plugin may not work for Sketch 93+. You can use [Mockup](https://github.com/ruslanlatypov/Mockup-Plugin-for-Sketch) (make sure to Detach from Symbol and once applied, rotate 3 times). This will work with Angle mockups. 18 | - use version [1.1.7](https://github.com/MengTo/Angle-Sketch-Plugin/releases/download/v1.1.7/Angle.sketchplugin.zip) for Sketch 86+ 19 | - use version [1.1.6](https://github.com/MengTo/Angle-Sketch-Plugin/releases/download/v1.1.6/Angle.sketchplugin.zip) for Sketch 72 to Sketch 85.1 20 | - use version [1.1.5](https://github.com/MengTo/Angle-Sketch-Plugin/releases/download/v1.1.5/Angle.sketchplugin.zip) for Sketch 66 to Sketch 71.2 21 | - use version [1.1.4](https://github.com/MengTo/Angle-Sketch-Plugin/releases/download/v1.1.4/Angle.sketchplugin.zip) for Sketch 65 and earlier. 22 | 23 | Apply perspective transforms on screen mockups. Auto-detect screens by resolution and works on shapes and symbols. 24 | 25 | ## Usage 26 | 27 | Select a shape layer and do **Apply Mockup** `Command + \`. 28 | ![](https://images.ctfassets.net/l7neci24wkw8/ebI08E1aZfvpMN1pChH1J/0fdfa8806ca1443aaeb786fd449adb72/Apply.png) 29 | 30 | - **Apply Mockup** `Command + \`: this will apply an Artboard to your selected shape. 31 | 32 | ## Auto-Detect 33 | 34 | When applying a mockup, Angle automatically detects all the Artboards, except the current Artboard. No expensive operations while you're designing, as this only happens when you run one of Angle's commands. 35 | ![](https://cl.ly/2W3o332N0p25/download/Angle-Detect.png) 36 | 37 | ## Single Artboard 38 | 39 | If you have a single Artboard with your screen, Angle will skip the modal and apply right away. Boom! 40 | 41 | ## Working with Symbols 42 | 43 | If you have a Symbol that contains your Mockup, make sure to have an **Image Fill**. That's how Angle knows that your Symbol contains a mockup. 44 | ![](https://cl.ly/1L2Q3u1n0T33/download/Angle-Symbol.png) 45 | 46 | ## Multiple Shapes and Nested Symbols 47 | 48 | If you have multiple shapes or a Nested Symbols, you can apply the mockups to multiple destinations. 49 | 50 | ## Pixel Density 51 | 52 | By default, it's set to **Auto**, which will detect the size of selected shape and apply a **2x** pixel density. You can set to **1x**, **2x**, **3x** or **4x**. 53 | 54 | ## Image Quality 55 | 56 | By default, it's set the **Best**. While you'll get the highest quality possible, this will increase your filesize dramatically. By setting to **Better**, **Good** or **Average**, you will make the file smaller. 57 | 58 | ## Credit 59 | 60 | This plugin was made by the team at [Design+Code](https://designcode.io), including [Tiago Mergulhao](https://github.com/tmergulhao), [Meng To](https://twitter.com/mengto) and [Kwan Yip Yap](http://twitter.com/pizza0502). 61 | -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Angle-Sketch-Plugin/c5145c524a2a463611fbb0aa6312943a861c6992/assets/.DS_Store -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Angle-Sketch-Plugin/c5145c524a2a463611fbb0aa6312943a861c6992/assets/icon.png -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MengTo/Angle-Sketch-Plugin/c5145c524a2a463611fbb0aa6312943a861c6992/assets/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angle", 3 | "version": "1.1.7", 4 | "repository": "https://github.com/MengTo/Angle-Sketch-Plugin", 5 | "description": "Important note: use version 1.1.7 for Sketch 86+, version 1.1.6 for Sketch 72-85, version 1.1.5 for Sketch 66-71 and version 1.1.4 for Sketch 65 and earlier. Apply perspective transforms on screen mockups. Auto-detect screens by resolution and works on shapes and symbols.", 6 | "engines": { 7 | "sketch": ">=3.0" 8 | }, 9 | "skpm": { 10 | "name": "Angle", 11 | "manifest": "src/manifest.json", 12 | "main": "Angle.sketchplugin", 13 | "assets": [ 14 | "assets/**/*" 15 | ] 16 | }, 17 | "scripts": { 18 | "build": "skpm-build", 19 | "watch": "skpm-build --watch", 20 | "start": "skpm-build --watch --run", 21 | "postinstall": "npm run build && skpm-link" 22 | }, 23 | "devDependencies": { 24 | "@skpm/builder": "^0.8.0" 25 | }, 26 | "dependencies": { 27 | "npm": "^6.14.16", 28 | "skpm": "^1.3.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Angle.js: -------------------------------------------------------------------------------- 1 | import { CompressionRatio } from './CompressionRatio'; 2 | 3 | const SegmentType = { linear: 0, quadratic: 1, cubic: 2 }; 4 | 5 | Array.prototype.rotated = function (n) { 6 | return this.slice(n, this.length).concat(this.slice(0, n)); 7 | }; 8 | export default class Angle { 9 | // --------------------------------- 10 | // PERSISTENT PROPERTIES 11 | // --------------------------------- 12 | 13 | get artboardID() { 14 | if (this._artboardID != undefined) { 15 | return this._artboardID; 16 | } 17 | 18 | // Javascript string cohersion 19 | this._artboardID = this.loadValueForKey('artboard-id') + ''; 20 | 21 | return this._artboardID; 22 | } 23 | set artboardID(value) { 24 | return; 25 | } 26 | 27 | get artboard() { 28 | if (this._artboard != undefined) { 29 | return this._artboard; 30 | } 31 | 32 | if (this.artboardID == undefined) { 33 | print('🛑 No artboard ID registered'); 34 | return; 35 | } 36 | 37 | let artboards = this.context.document.artboards(); 38 | 39 | for (let index = 0; index < artboards.count(); index++) { 40 | if (artboards[index].objectID() == this.artboardID) { 41 | this._artboard = artboards[index]; 42 | } 43 | } 44 | 45 | if (this._artboard == undefined) { 46 | print('🛑 Not able to retrieve artboard from id in document'); 47 | return; 48 | } 49 | 50 | return this._artboard; 51 | } 52 | 53 | set artboard(value) { 54 | this._artboard = value; 55 | this.imprintValue_forKey(value.objectID(), 'artboard-id'); 56 | } 57 | 58 | get rotation() { 59 | if (this._rotation == undefined) { 60 | this._rotation = this.loadValueForKey('rotation'); 61 | } 62 | return this._rotation; 63 | } 64 | set rotation(value) { 65 | this._rotation = value; 66 | this.imprintValue_forKey(value, 'rotation'); 67 | } 68 | 69 | get pixelDensity() { 70 | if (this._pixelDensity == undefined) { 71 | this._pixelDensity = this.loadValueForKey('pixel-density') + 0; 72 | } 73 | 74 | if (this._pixelDensity == 0) { 75 | let roundedEstimate = Math.round(2 * this.estimatePixelDensity() + 0.5); 76 | return roundedEstimate == 0 ? 1 : roundedEstimate; 77 | } 78 | 79 | return this._pixelDensity; 80 | } 81 | set pixelDensity(value) { 82 | this._pixelDensity = value; 83 | this.imprintValue_forKey(value, 'pixel-density'); 84 | } 85 | 86 | get compressionRatio() { 87 | if (this._compressionRatio == undefined) { 88 | this._compressionRatio = this.loadValueForKey('compression-ratio') + 0; 89 | } 90 | 91 | return this._compressionRatio; 92 | } 93 | set compressionRatio(value) { 94 | this._compressionRatio = value; 95 | this.imprintValue_forKey(value, 'compression-ratio'); 96 | } 97 | 98 | get reversed() { 99 | if (this._reversed == undefined) { 100 | // Javascript boolean cohersion 101 | this._reversed = this.loadValueForKey('reversed') == 1 ? true : false; 102 | } 103 | return this._reversed; 104 | } 105 | set reversed(value) { 106 | this._reversed = value; 107 | this.imprintValue_forKey(value, 'reversed'); 108 | } 109 | 110 | // --------------------------------- 111 | // PERSISTENCY METHODS 112 | // --------------------------------- 113 | 114 | imprintValue_forKey(value, key) { 115 | if (this.selectedLayer == null) { 116 | print('🛑 Imprinting value before selected layer assignment'); 117 | return; 118 | } 119 | 120 | this.context.command.setValue_forKey_onLayer( 121 | value, 122 | key, 123 | this.selectedLayer 124 | ); 125 | } 126 | 127 | loadValueForKey(key) { 128 | if (this.selectedLayer == null) { 129 | print('🛑 Loading value before selected layer assignment'); 130 | return null; 131 | } 132 | 133 | let value = this.context.command.valueForKey_onLayer( 134 | key, 135 | this.selectedLayer 136 | ); 137 | 138 | return value; 139 | } 140 | 141 | // --------------------------------- 142 | // CONSTRUCTOR 143 | // --------------------------------- 144 | 145 | constructor(options = {}) { 146 | this.context = options.context; 147 | this.selectedLayer = options.selectedLayer; 148 | } 149 | 150 | // --------------------------------- 151 | // IMAGE DATA 152 | // --------------------------------- 153 | 154 | exportRequest_lessThan52() { 155 | // let layerAncestry = MSImmutableLayerAncestry.alloc().initWithMSLayer(this.artboard) 156 | // let layerAncestry = this.artboard.ancestry(); 157 | 158 | // MSImmutableLayerAncestry has been renamed(As of V.66.1) and must be called from the class string 159 | let cls = NSClassFromString('SketchModel.MSImmutableLayerAncestry'); 160 | let layerAncestry = cls.alloc().initWithMutableLayer(context.selection[0]); 161 | 162 | let exportFormat = MSExportFormat.formatWithScale_name_fileFormat( 163 | this.pixelDensity, 164 | 'Angle', 165 | 'png' 166 | ); 167 | return MSExportRequest.exportRequestsFromLayerAncestry_exportFormats( 168 | layerAncestry, 169 | [exportFormat] 170 | ).firstObject(); 171 | } 172 | 173 | exporter() { 174 | const colorSpace = this.context.document.colorSpace().CGColorSpace(); 175 | 176 | const sketchVersion = BCSketchInfo.shared().metadata().appVersion; 177 | if (sketchVersion < 52) 178 | return MSExporter.exporterForRequest_colorSpace( 179 | this.exportRequest_lessThan52(), 180 | colorSpace 181 | ); 182 | 183 | const format = MSExportFormat.alloc().init(); 184 | format.fileFormat = 'png'; 185 | format.scale = this.pixelDensity; 186 | const request = MSExportRequest.exportRequestsFromExportableLayer_exportFormats_useIDForName( 187 | this.artboard, 188 | [format], 189 | true 190 | ).firstObject(); 191 | 192 | return MSExporter.exporterForRequest_colorSpace(request, colorSpace); 193 | } 194 | 195 | ciImage() { 196 | const bitmapRepresentation = this.exporter().bitmapImageRep(); 197 | 198 | return CIImage.alloc().initWithCGImage(bitmapRepresentation.CGImage()); 199 | // Below works too 200 | // return CIImage.alloc().initWithBitmapImageRep(bitmapRepresentation); 201 | } 202 | 203 | // --------------------------------- 204 | // PATH VALIDATION AND CORRECTION 205 | // --------------------------------- 206 | 207 | get pointsAreValid_lessThan50() { 208 | let points = this.pointsFromBezierPath; 209 | 210 | if (points === null) return false; 211 | 212 | if (points.length !== 7) return false; 213 | 214 | return true; 215 | } 216 | 217 | get pointsAreValid_lessThan52() { 218 | const contour = this.targetPath.contours().firstObject(); 219 | const points = Array.fromNSArray(contour.segments()); 220 | 221 | if (points === null) return false; 222 | 223 | if (points.length !== 4) return false; 224 | 225 | if (points.some((a) => a.segmentType() != SegmentType.linear)) return false; 226 | 227 | return true; 228 | } 229 | 230 | get pointsAreValid() { 231 | const sketchVersion = BCSketchInfo.shared().metadata().appVersion; 232 | 233 | if (sketchVersion < 50) return this.pointsAreValid_lessThan50; 234 | 235 | if (sketchVersion < 52) return this.pointsAreValid_lessThan52; 236 | 237 | let points = this.targetLayer.points(); 238 | 239 | if (points === null) return false; 240 | if (points.length !== 4) return false; 241 | if (points.some((a) => !a.isStraight())) return false; 242 | 243 | return true; 244 | } 245 | 246 | guessRotationAndReversion() { 247 | // Avoid double inference overriding user configuration 248 | 249 | let hasAlreadyGuessed = 250 | this.loadValueForKey('guessed-rotation') === 1 ? true : false; 251 | 252 | if (hasAlreadyGuessed) { 253 | print( 254 | '⚠️ Angle has already guessed rotation and symmetry for this shape' 255 | ); 256 | return; 257 | } 258 | 259 | let verticesLengths = this.verticesLengths; 260 | 261 | let artboardSize; 262 | 263 | if (this.artboard.class() == MSSymbolMaster) { 264 | // artboard.frame == undefined 265 | return; 266 | artboardSize = this.artboard.optimalBoundingBox(); 267 | } else { 268 | artboardSize = this.artboard.frame(); 269 | } 270 | 271 | let firstVerticeLength = verticesLengths[0]; 272 | let secondVerticeLength = verticesLengths[1]; 273 | 274 | let isHorizontal = firstVerticeLength > secondVerticeLength; 275 | let hasHorizontalArtboard = artboardSize.width() > artboardSize.height(); 276 | 277 | if (isHorizontal) { 278 | // Ensures that the first vertice is smaller 279 | print('🛑 HORIZONTAL'); 280 | this.rotate(); 281 | } 282 | 283 | if (hasHorizontalArtboard) { 284 | print('🛑 HAS HORIZONTAL ARTBOARD'); 285 | this.rotate(); 286 | } 287 | 288 | let points = this.pointsFromBezierPath; 289 | 290 | var minimumY = Math.min(...points.map((a) => a.y)); 291 | 292 | let mappedFirstPoint = points[this.mappedIndexFor(0)]; 293 | let mappedSecondPoint = points[this.mappedIndexFor(1)]; 294 | 295 | let isUpsideDown = !( 296 | mappedFirstPoint.y == minimumY || mappedSecondPoint.y == minimumY 297 | ); 298 | 299 | if (isUpsideDown) { 300 | print('🛑 UPSIDE DOWN'); 301 | this.rotate(); 302 | this.rotate(); 303 | } 304 | 305 | const sketchVersion = BCSketchInfo.shared().metadata().appVersion; 306 | if (sketchVersion < 50 || sketchVersion >= 52) { 307 | let shoelaceSumOfPoints = this.shorlaceSum(); 308 | if (shoelaceSumOfPoints < 0) { 309 | print('🛑 COUNTERCLOCKWISE'); 310 | this.reverseSymmetry(); 311 | } else if (shoelaceSumOfPoints > 0) { 312 | print('🛑 CLOCKWISE'); 313 | } else { 314 | print('🛑 UNDEFINED CHIRALITY'); 315 | } 316 | } else if (sketchVersion < 52) { 317 | const contour = this.targetPath.contours().firstObject(); 318 | if (contour.isClockwise() === 1) { 319 | this.reverseSymmetry(); 320 | print('🛑 CLOCKWISE'); 321 | } else { 322 | print('🛑 COUNTERCLOCKWISE'); 323 | } 324 | print('🛑 UNDEFINED CHIRALITY'); 325 | } 326 | 327 | print('🔄↔️ Angle has just guessed rotation and symmetry for this shape'); 328 | this.imprintValue_forKey(true, 'guessed-rotation'); 329 | } 330 | 331 | shorlaceSum() { 332 | const points = this.pointsFromBezierPath; 333 | let maximumY = Math.max(...points.map((a) => a.y)); 334 | 335 | return Array.from({ length: 4 }, (x, i) => i).reduce(function (p, i) { 336 | let edgeSum = 337 | (-points[i].x + points[(i + 1) % 4].x) * 338 | (2 * maximumY - points[i].y - points[(i + 1) % 4].y); 339 | return p + edgeSum; 340 | }, 0); 341 | } 342 | 343 | // --------------------------------- 344 | // PATH 345 | // --------------------------------- 346 | 347 | get pointsFromBezierPath() { 348 | const sketchVersion = BCSketchInfo.shared().metadata().appVersion; 349 | if (sketchVersion < 50) { 350 | let count = this.targetPath.elementCount(); 351 | if (count != 7) { 352 | return null; 353 | } 354 | 355 | return Array.from({ length: count }, (x, i) => i).map((i) => { 356 | var pointsPointer = MOPointer.alloc().initWithValue_(CGPointMake(0, 0)); 357 | var element = this.targetPath.elementAtIndex_associatedPoints_( 358 | i, 359 | pointsPointer 360 | ); 361 | return pointsPointer.value(); 362 | }); 363 | } else if (sketchVersion < 52) { 364 | const contour = this.targetPath.contours().firstObject(); 365 | return Array.fromNSArray(contour.segments()).map((a) => a.endPoint1()); 366 | } 367 | 368 | const size = this.targetLayer.rect().size; 369 | return Array.fromNSArray(this.targetLayer.points()) 370 | .map((a) => a.point()) 371 | .map((a) => ({ 372 | x: Number(a.x) * Number(size.width), 373 | y: Number(a.y) * Number(size.height), 374 | })); 375 | } 376 | 377 | get verticesLengths() { 378 | let points = this.pointsFromBezierPath; 379 | 380 | return Array.from({ length: 4 }, (x, i) => i).map((i) => { 381 | const j = (i + 1) % 4; 382 | const width = points[i].x - points[j].x; 383 | const height = points[i].y - points[j].y; 384 | return Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)); 385 | }); 386 | } 387 | 388 | maximumVerticesWidthAndHeight() { 389 | let verticesLengths = this.verticesLengths.rotated(this.rotation % 2); 390 | 391 | let layerWidth = Math.max(verticesLengths[0], verticesLengths[2]); 392 | let layerHeight = Math.max(verticesLengths[1], verticesLengths[3]); 393 | 394 | return [layerWidth, layerHeight]; 395 | } 396 | 397 | get normalizedCIVectors() { 398 | let points = this.pointsFromBezierPath; 399 | 400 | var maximumY = Math.max(...points.map((a) => a.y)); 401 | 402 | let pixelDensity = this.pixelDensity; 403 | 404 | return points.map((a) => { 405 | return CIVector.vectorWithX_Y( 406 | a.x * pixelDensity, 407 | (maximumY - a.y) * pixelDensity 408 | ); 409 | }); 410 | } 411 | 412 | // --------------------------------- 413 | // INTERFACE 414 | // --------------------------------- 415 | 416 | rotate() { 417 | this.rotation = (this.rotation + (this.reversed ? 1 : 3)) % 4; 418 | } 419 | 420 | reverseSymmetry() { 421 | this.rotate(); 422 | 423 | this.reversed = !this.reversed; 424 | } 425 | 426 | // --------------------------------- 427 | // DRAWING 428 | // --------------------------------- 429 | 430 | mappedIndexFor(index) { 431 | if (this.reversed) { 432 | return [0, 3, 2, 1][(index + this.rotation) % 4]; 433 | } 434 | return (index + this.rotation) % 4; 435 | } 436 | 437 | lossyCompressionOfImage_atRate(image, rate) { 438 | let representation = NSBitmapImageRep.alloc().initWithCIImage(image); 439 | let properties = NSMutableDictionary.dictionary(); 440 | 441 | properties.setObject_forKey( 442 | NSTIFFCompressionJPEG, 443 | NSImageCompressionMethod 444 | ); 445 | properties.setObject_forKey(rate, NSImageCompressionFactor); 446 | properties.setObject_forKey( 447 | NSColor.whiteColor(), 448 | NSImageFallbackBackgroundColor 449 | ); 450 | 451 | let compressed = representation.representationUsingType_properties( 452 | NSJPEGFileType, 453 | properties 454 | ); 455 | let nsImage = NSImage.alloc().initWithData(compressed); 456 | 457 | return nsImage; 458 | } 459 | 460 | pixelAccurateRepresentationOfImage(image) { 461 | let representation = NSCIImageRep.alloc().initWithCIImage(image); 462 | let nsImage = NSImage.alloc().initWithSize(representation.size()); 463 | nsImage.addRepresentation(representation); 464 | 465 | return nsImage; 466 | } 467 | 468 | get transformedImage() { 469 | let vectors = this.normalizedCIVectors; 470 | 471 | let perspectiveTransform = CIFilter.filterWithName( 472 | 'CIPerspectiveTransform' 473 | ); 474 | 475 | perspectiveTransform.setValue_forKey( 476 | vectors[this.mappedIndexFor(0)], 477 | 'inputTopLeft' 478 | ); 479 | perspectiveTransform.setValue_forKey( 480 | vectors[this.mappedIndexFor(1)], 481 | 'inputTopRight' 482 | ); 483 | perspectiveTransform.setValue_forKey( 484 | vectors[this.mappedIndexFor(2)], 485 | 'inputBottomRight' 486 | ); 487 | perspectiveTransform.setValue_forKey( 488 | vectors[this.mappedIndexFor(3)], 489 | 'inputBottomLeft' 490 | ); 491 | 492 | let image = this.ciImage(); 493 | 494 | perspectiveTransform.setValue_forKey(image, 'inputImage'); 495 | 496 | let perspectiveImage = perspectiveTransform.valueForKey('outputImage'); 497 | 498 | if (!perspectiveImage) { 499 | print('🛑 Unable to form perspective image'); 500 | return; 501 | } 502 | 503 | let ouputNSImage; 504 | 505 | let compressionRatio = CompressionRatio[this.compressionRatio].ratio; 506 | 507 | if (compressionRatio != 1.0) { 508 | ouputNSImage = this.lossyCompressionOfImage_atRate( 509 | perspectiveImage, 510 | compressionRatio 511 | ); 512 | } else { 513 | ouputNSImage = this.pixelAccurateRepresentationOfImage(perspectiveImage); 514 | } 515 | 516 | let imageData; 517 | 518 | if (BCSketchInfo.shared().metadata().appVersion < 47) { 519 | imageData = MSImageData.alloc().initWithImage_convertColorSpace( 520 | ouputNSImage, 521 | true 522 | ); 523 | return imageData; 524 | } 525 | 526 | imageData = MSImageData.alloc().initWithImage_(ouputNSImage); 527 | 528 | return imageData; 529 | } 530 | } 531 | -------------------------------------------------------------------------------- /src/CompositionAngle.js: -------------------------------------------------------------------------------- 1 | import SymbolicAngle from './SymbolicAngle' 2 | import { Error } from './Error' 3 | 4 | export default class CompositionAngle extends SymbolicAngle { 5 | 6 | constructor (options = {}) { 7 | 8 | super(options); 9 | 10 | this.override = options.override; 11 | } 12 | 13 | loadValueForKey (key) { 14 | return super.loadValueForKey(this.targetLayer.objectID() + "-" + key); 15 | } 16 | 17 | imprintValue_forKey (value, key) { 18 | super.imprintValue_forKey(value, this.targetLayer.objectID() + "-" + key); 19 | } 20 | 21 | applyImage () { 22 | 23 | const existingTopOverrides = this.selectedLayer.overrides() || NSDictionary.dictionary(); 24 | const topOverrides = NSMutableDictionary.dictionaryWithDictionary(existingTopOverrides); 25 | 26 | const overrideBranchAddress = (this.overrideLayer + "").replace("_symbolID", ""); 27 | 28 | const existingOverrideBranch = topOverrides.objectForKey(overrideBranchAddress) || NSMutableDictionary.dictionary(); 29 | let overrideBranch = NSMutableDictionary.dictionaryWithDictionary(existingOverrideBranch); 30 | 31 | const objectID = this.targetLayer.objectID(); 32 | overrideBranch.setObject_forKey(this.transformedImage, objectID); 33 | topOverrides.setObject_forKey(overrideBranch, overrideBranchAddress); 34 | 35 | this.selectedLayer.overrides = topOverrides; 36 | } 37 | 38 | description () { 39 | return this.override.parent().affectedLayer().name() + " " + this.targetLayer.name(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/CompressionRatio.js: -------------------------------------------------------------------------------- 1 | export const CompressionRatio = [ 2 | { selectionLabel: "Best", ratio: 1.0 }, 3 | { selectionLabel: "Better", ratio: 0.9 }, 4 | { selectionLabel: "Good", ratio: 0.8 }, 5 | { selectionLabel: "Average", ratio: 0.7 } 6 | ] 7 | -------------------------------------------------------------------------------- /src/Error.js: -------------------------------------------------------------------------------- 1 | export const Error = { 2 | unsupportedSymbol : { 3 | message : "This does not seem to be a supported symbol." 4 | }, 5 | unsupportedShapePath : { 6 | message : "There seems to be an issue with the shape we are trying to apply." 7 | }, 8 | emptySelection : { 9 | message : "Please, select a Shape, Angle Mockup or Angle Composition" 10 | }, 11 | unsupportedElement : { 12 | message : "Please, select a Shape, Angle Mockup or Angle Composition" 13 | }, 14 | noImageOverrideOnSymbol : { 15 | message : "There is no image override for the selected symbol" 16 | }, 17 | symbolWithBitMapLayer : { 18 | message : "Bitmat overrides are not supported" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/PixelDensity.js: -------------------------------------------------------------------------------- 1 | export const PixelDensity = [ 2 | { title: "Auto", selectionLabel: "Auto" }, 3 | { title: "1x", selectionLabel: "1x" }, 4 | { title: "2x", selectionLabel: "2x" }, 5 | { title: "3x", selectionLabel: "3x" }, 6 | { title: "4x", selectionLabel: "4x" }, 7 | ] 8 | -------------------------------------------------------------------------------- /src/ShapeAngle.js: -------------------------------------------------------------------------------- 1 | import Angle from './Angle' 2 | import { Error } from './Error' 3 | 4 | const StyleFillType = { solid: 0, gradient: 1, pattern: 4, noise: 5 }; 5 | export default class ShapeAngle extends Angle { 6 | constructor (options = {}) { 7 | super(options); 8 | 9 | this.targetLayer = this.selectedLayer; 10 | 11 | const sketchVersion = BCSketchInfo.shared().metadata().appVersion 12 | if (sketchVersion < 50) { 13 | this.targetPath = this.selectedLayer.bezierPath(); 14 | } else if (sketchVersion < 52) { 15 | this.targetPath = this.selectedLayer.pathInFrameWithTransforms(); 16 | } 17 | 18 | if (!this.pointsAreValid) { 19 | return Error.unsupportedShapePath 20 | } 21 | } 22 | 23 | applyImage () { 24 | 25 | let imageFill = MSStyleFill.alloc().init(); 26 | imageFill.setImage(this.transformedImage); 27 | imageFill.fillType = StyleFillType.pattern; 28 | 29 | this.targetLayer.style().removeAllStyleFills(); 30 | this.targetLayer.style().addStyleFill(imageFill); 31 | } 32 | 33 | estimatePixelDensity () { 34 | 35 | // Best guess of a 2x sampling of the image if the mockup is in it's original scale 36 | 37 | let [layerWidth, layerHeight] = this.maximumVerticesWidthAndHeight(); 38 | 39 | let widthRatio = layerWidth / this.artboard.rect().size.width; 40 | let heightRatio = layerHeight / this.artboard.rect().size.height; 41 | 42 | let estimate = widthRatio > heightRatio ? widthRatio : heightRatio; 43 | 44 | return estimate; 45 | } 46 | 47 | description () { 48 | return this.targetLayer.name(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/SymbolicAngle.js: -------------------------------------------------------------------------------- 1 | import Angle from './Angle' 2 | import { Error } from './Error' 3 | 4 | export default class SymbolicAngle extends Angle { 5 | 6 | constructor (options = {}) { 7 | 8 | super(options); 9 | 10 | this.targetLayer = options.override.affectedLayer(); 11 | 12 | if (this.targetLayer.class() === MSImmutableBitmapLayer) 13 | return Error.symbolWithBitMapLayer 14 | 15 | const sketchVersion = BCSketchInfo.shared().metadata().appVersion 16 | if (sketchVersion < 50) { 17 | this.targetPath = options.override.affectedLayer().bezierPath(); 18 | } else if (sketchVersion < 52) { 19 | this.targetPath = options.override.affectedLayer().pathInFrameWithTransforms(); 20 | } 21 | 22 | let parentSymbolIdentifier = options.override.overridePoint().parent() 23 | if (parentSymbolIdentifier !== null) { 24 | this.overrideLayer = parentSymbolIdentifier 25 | } 26 | 27 | if (!this.pointsAreValid) { 28 | return Error.unsupportedShapePath 29 | } 30 | } 31 | 32 | applyImage () { 33 | 34 | let objectID = this.targetLayer.objectID(); 35 | 36 | const existingOverrides = this.selectedLayer.overrides() || NSDictionary.dictionary(); 37 | const overrides = NSMutableDictionary.dictionaryWithDictionary(existingOverrides); 38 | 39 | overrides.setObject_forKey(this.transformedImage, objectID); 40 | 41 | this.selectedLayer.overrides = overrides; 42 | } 43 | 44 | estimatePixelDensity () { 45 | 46 | // Best guess of a 2x sampling of the image if the mockup is in it's original scale 47 | 48 | let [layerWidth, layerHeight] = this.maximumVerticesWidthAndHeight(); 49 | 50 | let widthRatio = this.selectedLayer.rect().size.width * layerWidth / (this.selectedLayer.naturalSize().width * this.artboard.rect().size.width); 51 | let heightRatio = this.selectedLayer.rect().size.height * layerHeight / (this.selectedLayer.naturalSize().height * this.artboard.rect().size.height); 52 | 53 | let estimate = widthRatio > heightRatio ? widthRatio : heightRatio; 54 | 55 | return estimate; 56 | } 57 | 58 | description () { 59 | return this.selectedLayer.name() + " " + this.targetLayer.name(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/apply.js: -------------------------------------------------------------------------------- 1 | import Angle from './Angle'; 2 | import * as Shared from './Shared'; 3 | 4 | import { Error } from './Error'; 5 | import { PixelDensity } from './PixelDensity'; 6 | import { CompressionRatio } from './CompressionRatio'; 7 | 8 | String.prototype.repeat = (i) => new Array(i + 1).join(this); 9 | 10 | export function getSelectionAndOptions_forAngleInstances(options) { 11 | let { artboards, otherArtboards, alertImage, angles } = options; 12 | 13 | let array = Array.from({ length: angles.length }, (x, i) => i); 14 | 15 | if (artboards.length == 1) { 16 | return { 17 | alertOption: NSAlertFirstButtonReturn, 18 | artboardSelections: array.map((a, i, as) => { 19 | indexOfSelectedItem: () => 0; 20 | }), 21 | densitySelections: array.map((a, i, as) => { 22 | indexOfSelectedItem: () => 0; 23 | }), 24 | compressionSelections: array.map((a, i, as) => { 25 | indexOfSelectedItem: () => 0; 26 | }), 27 | }; 28 | } 29 | 30 | var alert = NSAlert.alloc().init(); 31 | var alertContent = NSView.alloc().init(); 32 | 33 | alert.setMessageText('Apply Mockup'); 34 | alert.setInformativeText( 35 | 'Choose an Artboard to apply into the selected shape.' 36 | ); 37 | alert.addButtonWithTitle('Apply'); 38 | alert.addButtonWithTitle('Cancel'); 39 | alert.icon = alertImage; 40 | 41 | var movingYPosition = 0; 42 | var labelHeight = 16; 43 | 44 | var fisrtColumnWidth = 260; 45 | var secondColumnWidth = 90; 46 | var thirdColumnWidth = 90; 47 | 48 | const windowWidth = fisrtColumnWidth + secondColumnWidth + thirdColumnWidth; 49 | 50 | var rectangle; 51 | 52 | rectangle = NSMakeRect(0, 0, fisrtColumnWidth, labelHeight); 53 | var artboardLabel = Shared.createLabel('Artboard', 12, rectangle); 54 | alertContent.addSubview(artboardLabel); 55 | 56 | rectangle = NSMakeRect(fisrtColumnWidth, 0, secondColumnWidth, labelHeight); 57 | var densityLabel = Shared.createLabel('Pixel Density', 12, rectangle); 58 | alertContent.addSubview(densityLabel); 59 | 60 | rectangle = NSMakeRect( 61 | fisrtColumnWidth + secondColumnWidth, 62 | 0, 63 | thirdColumnWidth, 64 | labelHeight 65 | ); 66 | var compressionLabel = Shared.createLabel('Quality', 12, rectangle); 67 | alertContent.addSubview(compressionLabel); 68 | 69 | let spacing = array.length > 1 ? 50 : 28; 70 | 71 | if (array.length > 1) { 72 | let targetLabels = array.map(function (a, i, as) { 73 | let rectangle = NSMakeRect( 74 | 0, 75 | labelHeight + 4 + spacing * i, 76 | fisrtColumnWidth, 77 | labelHeight 78 | ); 79 | let label = Shared.createLabel(angles[i].description(), 12, rectangle); 80 | 81 | return label; 82 | }); 83 | targetLabels.forEach((a) => alertContent.addSubview(a)); 84 | } 85 | 86 | let allArtboards = artboards.concat(otherArtboards); 87 | 88 | let artboardNames = allArtboards 89 | .map(function (a) { 90 | if (a.name != undefined) { 91 | if (a.name instanceof String) { 92 | return a.name; 93 | } 94 | return a.name(); 95 | } 96 | }) 97 | .map(function (a, i, as) { 98 | let indexesWithSameName = as 99 | .map((b, j, bs) => (a == b ? j : -1)) 100 | .filter((a, i, as) => a != -1); 101 | 102 | if (indexesWithSameName.length > 1) { 103 | let indexOfIndex = indexesWithSameName.indexOf(i); 104 | return a + ' '.repeat(indexOfIndex); 105 | } 106 | 107 | return a; 108 | }); 109 | 110 | let artboardImages = allArtboards.map((a) => 111 | Shared.smallImagesFromArtboard(a) 112 | ); 113 | 114 | let artboardSelections = array.map((a, index, as) => 115 | Shared.popUpButtonsforRectangleIndexer_withTitleIndexer_andImageIndexer_defaultSelected_onIndex( 116 | (i) => 117 | NSMakeRect(0, labelHeight + 4 + spacing * i + 16, fisrtColumnWidth, 28), 118 | artboardNames, 119 | artboardImages, 120 | index 121 | ) 122 | ); 123 | artboardSelections.forEach((a) => alertContent.addSubview(a)); 124 | 125 | let pixelDensityNames = PixelDensity.map((a) => a.selectionLabel); 126 | let pixelDensitySelections = array.map((a, index, as) => 127 | Shared.popUpButtonsforRectangleIndexer_withTitleIndexer_andImageIndexer_defaultSelected_onIndex( 128 | (i) => 129 | NSMakeRect( 130 | fisrtColumnWidth, 131 | labelHeight + 4 + spacing * i + 16, 132 | secondColumnWidth, 133 | 28 134 | ), 135 | pixelDensityNames, 136 | null, 137 | index 138 | ) 139 | ); 140 | pixelDensitySelections.forEach((a) => alertContent.addSubview(a)); 141 | 142 | let compressionRatioNames = CompressionRatio.map((a) => a.selectionLabel); 143 | let compressionRatioSelections = array.map((a, index, as) => 144 | Shared.popUpButtonsforRectangleIndexer_withTitleIndexer_andImageIndexer_defaultSelected_onIndex( 145 | (i) => 146 | NSMakeRect( 147 | fisrtColumnWidth + secondColumnWidth, 148 | labelHeight + 4 + spacing * i + 16, 149 | thirdColumnWidth, 150 | 28 151 | ), 152 | compressionRatioNames, 153 | null, 154 | index 155 | ) 156 | ); 157 | compressionRatioSelections.forEach((a) => alertContent.addSubview(a)); 158 | 159 | movingYPosition = labelHeight + 4 + spacing * angles.length + 28; 160 | 161 | // Render those label, dropdown etc into the Alert view 162 | alertContent.frame = NSMakeRect(0, 0, windowWidth, movingYPosition); 163 | 164 | // Reverse order of the content elements 165 | alertContent.setFlipped(true); 166 | 167 | alert.accessoryView = alertContent; 168 | 169 | // With this will run the modal and return a reference to the selection element 170 | return { 171 | alertOption: alert.runModal(), 172 | artboardSelections: artboardSelections, 173 | densitySelections: pixelDensitySelections, 174 | compressionSelections: compressionRatioSelections, 175 | }; 176 | } 177 | 178 | function loadLocalImage(scriptPath, filePath) { 179 | const basePath = scriptPath 180 | .stringByDeletingLastPathComponent() 181 | .stringByDeletingLastPathComponent() 182 | .stringByDeletingLastPathComponent(); 183 | 184 | return NSImage.alloc().initWithContentsOfFile(basePath + '/' + filePath); 185 | } 186 | 187 | function applyAngles(options) { 188 | let { 189 | angles, 190 | artboardsOnSelectPage: artboards, 191 | context, 192 | artboardsOnOtherPages: otherArtboards, 193 | } = options; 194 | 195 | if (artboards.length === 1) { 196 | angles.forEach(function (a) { 197 | a.artboard = artboards[0]; 198 | a.pixelDensity = 0; 199 | a.selectedCompressionRatio = 0; 200 | }); 201 | } else { 202 | const angleLogo = loadLocalImage( 203 | context.scriptPath, 204 | 'Contents/Resources/logo.png' 205 | ); 206 | 207 | let response = getSelectionAndOptions_forAngleInstances({ 208 | artboards: artboards, 209 | otherArtboards: otherArtboards, 210 | angles: angles, 211 | alertImage: angleLogo, 212 | }); 213 | 214 | if (response.alertOption != NSAlertFirstButtonReturn) { 215 | return false; 216 | } 217 | 218 | angles.forEach(function (a, i, as) { 219 | let artboardSelectionIndex = response.artboardSelections[ 220 | i 221 | ].indexOfSelectedItem(); 222 | 223 | let allArtboards = artboards.concat(otherArtboards); 224 | 225 | a.artboard = allArtboards[artboardSelectionIndex]; 226 | a.pixelDensity = response.densitySelections[i].indexOfSelectedItem(); 227 | a.compressionRatio = response.compressionSelections[ 228 | i 229 | ].indexOfSelectedItem(); 230 | }); 231 | } 232 | 233 | angles.forEach((a) => { 234 | a.guessRotationAndReversion(); 235 | a.applyImage(); 236 | }); 237 | 238 | return true; 239 | } 240 | 241 | export default function (context) { 242 | let { document, selection } = context; 243 | 244 | const angleLogo = loadLocalImage( 245 | context.scriptPath, 246 | 'Contents/Resources/logo.png' 247 | ); 248 | 249 | if (selection == null) { 250 | Shared.show({ 251 | message: Error.emptySelection.message, 252 | inDocument: document, 253 | }); 254 | return; 255 | } 256 | 257 | const selectedLayers = Array.fromNSArray(selection); 258 | 259 | if (selectedLayers.length == 0) { 260 | Shared.show({ 261 | message: Error.emptySelection.message, 262 | inDocument: document, 263 | }); 264 | return; 265 | } 266 | 267 | let parentArtboard = selectedLayers[0].parentArtboard(); 268 | 269 | let artboardsOnSelectPage = Array.fromNSArray(document.artboards()) 270 | .filter((a) => a != parentArtboard) 271 | .sort(Shared.compareByRatioAndAlphabet); 272 | 273 | let artboardsOnOtherPages = Array.fromNSArray(document.pages()) 274 | .filter((a) => a != document.currentPage()) 275 | .map((a) => a.artboards()) 276 | .map((a) => Array.fromNSArray(a)) 277 | .reduce((p, a) => { 278 | return p.concat(a); 279 | }, new Array()) 280 | .filter(Shared.filterPossibleArtboards) 281 | .sort(Shared.compareByRatioAndAlphabet); 282 | 283 | if (artboardsOnSelectPage.length + artboardsOnOtherPages.length == 0) { 284 | var alert = NSAlert.alloc().init(); 285 | 286 | alert.setMessageText('Angle needs an Artboard'); 287 | alert.setInformativeText( 288 | 'To start using Angle, create a new Artboard that contains your screen.' 289 | ); 290 | alert.addButtonWithTitle('OK'); 291 | alert.icon = angleLogo; 292 | 293 | alert.runModal(); 294 | return; 295 | } 296 | 297 | let possibleAngles = Angle.tryCreating({ for: selectedLayers, in: context }); 298 | 299 | let angles = possibleAngles.filter((a) => a instanceof Angle); 300 | let errors = possibleAngles.filter((a) => !(a instanceof Angle)); 301 | 302 | if (angles.length != 0) { 303 | let appliedShapeAngles = applyAngles({ 304 | angles: angles, 305 | artboardsOnSelectPage: artboardsOnSelectPage, 306 | artboardsOnOtherPages: artboardsOnOtherPages, 307 | context: context, 308 | }); 309 | 310 | if (appliedShapeAngles) { 311 | Shared.show({ 312 | message: 'You got Angled! 📱', 313 | inDocument: document, 314 | }); 315 | } 316 | 317 | return; 318 | } 319 | 320 | if (errors.length == 0) { 321 | Shared.show({ 322 | message: Error.unsupportedElement.message, 323 | inDocument: document, 324 | }); 325 | } else { 326 | Shared.show({ 327 | message: errors[0].message, 328 | inDocument: document, 329 | }); 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "bundleVersion": 1, 3 | "version": "1.1.7", 4 | "icon": "icon.png", 5 | "identifier": "io.designcode.Angle", 6 | "description": "Important note: use version 1.1.7 for Sketch 86+, version 1.1.6 for Sketch 72-85, version 1.1.5 for Sketch 66-71 and version 1.1.4 for Sketch 65 and earlier. Apply perspective transforms on screen mockups. Auto-detect screens by resolution and works on shapes and symbols.", 7 | "homepage": "https://angle.sh", 8 | "author": "Design+Code", 9 | "authorEmail": "angle@designcode.io", 10 | "compatibleVersion": 50, 11 | "commands": [ 12 | { 13 | "name": "Rotate Mockup", 14 | "identifier": "Angle-Rotate-Mockup", 15 | "script": "./rotate.js", 16 | "shortcut": "control \\" 17 | }, 18 | { 19 | "name": "Flip Mockup", 20 | "identifier": "Angle-Rotate-Flip", 21 | "script": "./symmetry.js", 22 | "shortcut": "cmd shift \\" 23 | }, 24 | { 25 | "name": "Apply Mockup", 26 | "identifier": "Angle-Apply-Mockup", 27 | "script": "./apply.js", 28 | "shortcut": "cmd \\" 29 | }, 30 | { 31 | "name": "Reset Mockup", 32 | "identifier": "Angle-Reset-Metadata", 33 | "script": "./reset.js", 34 | "shortcut": "cmd control \\" 35 | } 36 | ], 37 | "menu": { 38 | "title": "Angle", 39 | "items": [ 40 | "Angle-Apply-Mockup", 41 | "Angle-Rotate-Flip", 42 | "Angle-Rotate-Mockup", 43 | "Angle-Reset-Metadata" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/reset.js: -------------------------------------------------------------------------------- 1 | import Angle from './Angle' 2 | import * as Shared from './Shared' 3 | 4 | export default function ({ document, selection, command }) { 5 | 6 | if (selection == undefined || selection.count() != 1) { 7 | Shared.show({ 8 | message: "Please, select 1️⃣ element to reset", 9 | inDocument: document 10 | }); 11 | return 12 | } 13 | 14 | let layer = selection.firstObject(); 15 | let possibleAngle = Angle.tryCreating({ for: [layer], in: { document, selection, command } }); 16 | 17 | if (!(possibleAngle instanceof Angle)) { 18 | 19 | Shared.show({ 20 | message: "Reset only works on shapes and symbols.", 21 | inDocument: document 22 | }); 23 | return 24 | } 25 | 26 | context.command.setValue_forKey_onLayer(null, "pixel-density", layer); 27 | context.command.setValue_forKey_onLayer(null, "rotation", layer); 28 | context.command.setValue_forKey_onLayer(null, "artboard-id", layer); 29 | context.command.setValue_forKey_onLayer(null, "compression-ratio", layer); 30 | context.command.setValue_forKey_onLayer(null, "reversed", layer); 31 | context.command.setValue_forKey_onLayer(null, "guessed-rotation", layer); 32 | 33 | Shared.show({ 34 | message: "Angle Mockup metadata reset.", 35 | inDocument: document 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/rotate.js: -------------------------------------------------------------------------------- 1 | import Angle from './Angle'; 2 | import * as Shared from './Shared'; 3 | 4 | import { Error } from './Error'; 5 | 6 | export default function (context) { 7 | let selectedLayersNSArray = context.selection[0]; 8 | 9 | if (selectedLayersNSArray == null) { 10 | Shared.show({ 11 | message: Error.emptySelection.message, 12 | inDocument: context.document, 13 | }); 14 | return; 15 | } 16 | 17 | let selectedLayers = Array.fromNSArray(selectedLayersNSArray); 18 | // console.log('SELECTED LAYERS', selectedLayers); 19 | 20 | if (selectedLayers.length == 0) { 21 | Shared.show({ 22 | message: Error.emptySelection.message, 23 | inDocument: context.document, 24 | }); 25 | return; 26 | } 27 | 28 | let possibleAngles = Angle.tryCreating({ for: selectedLayers, in: context }); 29 | 30 | let angles = possibleAngles.filter((a) => a instanceof Angle); 31 | let errors = possibleAngles.filter((a) => !(a instanceof Angle)); 32 | 33 | if (angles.length == 0) { 34 | let error = errors[0]; 35 | Shared.show({ 36 | message: error.message, 37 | inDocument: context.document, 38 | }); 39 | return; 40 | } 41 | 42 | angles.forEach((a) => { 43 | a.rotate(); 44 | a.applyImage(); 45 | }); 46 | 47 | Shared.show({ 48 | message: 'Angle rotated! 🔄', 49 | inDocument: context.document, 50 | }); 51 | 52 | return; 53 | } 54 | -------------------------------------------------------------------------------- /src/shared.js: -------------------------------------------------------------------------------- 1 | import Angle from './Angle'; 2 | import CompositionAngle from './CompositionAngle'; 3 | import SymbolicAngle from './SymbolicAngle'; 4 | import ShapeAngle from './ShapeAngle'; 5 | 6 | import { Error } from './Error'; 7 | 8 | Angle.tryCreating = function ({ for: selectedLayers, in: context }) { 9 | return selectedLayers 10 | .map((layer) => { 11 | switch (layer.class()) { 12 | case MSSymbolInstance: 13 | let overrides = Array.fromNSArray(layer.availableOverrides()) || []; 14 | 15 | let symbolAngles = overrides 16 | .filter( 17 | (override) => override.currentValue().class() == MSImageData 18 | ) 19 | .map((override) => { 20 | return new SymbolicAngle({ 21 | selectedLayer: layer, 22 | context: context, 23 | override: override, 24 | }); 25 | }); 26 | 27 | let nestedAngles = overrides 28 | .map((a) => a.children()) 29 | .filter((a) => a != null) 30 | .map(Array.fromNSArray) 31 | .reduce((p, a) => p.concat(a), []) 32 | .filter(function (a) { 33 | return a.class() == MSAvailableOverride; 34 | }) 35 | .map(function (a) { 36 | return new CompositionAngle({ 37 | override: a, 38 | selectedLayer: layer, 39 | context: context, 40 | }); 41 | }); 42 | return symbolAngles.concat(nestedAngles); 43 | case MSShapeGroup: 44 | case MSShapePathLayer: 45 | case MSRectangleShape: 46 | return new ShapeAngle({ 47 | selectedLayer: layer, 48 | context: context, 49 | }); 50 | default: 51 | return Error.unsupportedElement; 52 | } 53 | }) 54 | .reduce((p, a, i, as) => p.concat(a), []); 55 | }; 56 | 57 | Array.fromNSArray = function (nsArray) { 58 | let array = []; 59 | for (var i = 0; i < nsArray.length; i++) { 60 | array.push(nsArray[i]); 61 | } 62 | return array; 63 | }; 64 | 65 | Array.prototype.print = function () { 66 | return this.map((a) => { 67 | print(a); 68 | return a; 69 | }); 70 | }; 71 | 72 | export function show({ message, inDocument: document }) { 73 | if (document != undefined && document.showMessage != undefined) { 74 | document.showMessage(message); 75 | } 76 | 77 | print(message); 78 | } 79 | 80 | export function filterPossibleArtboards(artboardOrSymbol) { 81 | let upperMaring = 0.8; 82 | let lowerMargin = 0.4; 83 | let minimumDimention = 250; 84 | 85 | let elementClass = artboardOrSymbol.class(); 86 | 87 | switch (elementClass) { 88 | case MSArtboardGroup: 89 | let artboard = artboardOrSymbol; 90 | let frame = artboard.frame(); 91 | 92 | if ( 93 | frame.width() < minimumDimention || 94 | frame.height() < minimumDimention 95 | ) { 96 | return false; 97 | } 98 | 99 | let ratio = frame.width() / frame.height(); 100 | if (ratio > 1) { 101 | ratio = 1 / ratio; 102 | } 103 | 104 | if (ratio < lowerMargin) { 105 | return false; 106 | } 107 | break; 108 | case MSSymbolMaster: 109 | // Traversion of symbols does not work properly yet. 110 | return false; 111 | 112 | default: 113 | print(elementClass); 114 | return false; 115 | } 116 | 117 | return true; 118 | } 119 | 120 | export function compareByRatioAndAlphabet(a, b) { 121 | let upperMaring = 0.8; 122 | let lowerMargin = 0.4; 123 | 124 | let artboardSizeA = a.frame(); 125 | let artboardSizeB = b.frame(); 126 | 127 | let artboardARatio = artboardSizeA.width() / artboardSizeA.height(); 128 | if (artboardARatio > 1) { 129 | artboardARatio = 1 / artboardARatio; 130 | } 131 | 132 | let artboardARatioInsideMargin = 133 | artboardARatio > lowerMargin && artboardARatio < upperMaring; 134 | 135 | let artboardBRatio = artboardSizeB.width() / artboardSizeB.height(); 136 | if (artboardBRatio > 1) { 137 | artboardBRatio = 1 / artboardBRatio; 138 | } 139 | 140 | let artboardBRatioInsideMargin = 141 | artboardBRatio > lowerMargin && artboardBRatio < upperMaring; 142 | 143 | if (artboardARatioInsideMargin && !artboardBRatioInsideMargin) { 144 | return false; 145 | } 146 | 147 | if (artboardBRatioInsideMargin && !artboardARatioInsideMargin) { 148 | return true; 149 | } 150 | 151 | if (artboardARatio == artboardBRatio) { 152 | return a.name() > b.name(); 153 | } 154 | 155 | return artboardARatio > artboardBRatio; 156 | } 157 | 158 | export function introspect(type) { 159 | let mocha = type.class().mocha(); 160 | 161 | print('-----------------------------------------------'); 162 | print('PROPERTIES-------------------------------------'); 163 | print('-----------------------------------------------'); 164 | 165 | print(mocha.properties()); 166 | print(mocha.propertiesWithAncestors()); 167 | 168 | print('-----------------------------------------------'); 169 | print('INSTANCE METHODS-------------------------------'); 170 | print('-----------------------------------------------'); 171 | print(mocha.instanceMethods()); 172 | print(mocha.instanceMethodsWithAncestors()); 173 | 174 | print('-----------------------------------------------'); 175 | print('CLASS METHODS----------------------------------'); 176 | print('-----------------------------------------------'); 177 | print(mocha.classMethods()); 178 | print(mocha.classMethodsWithAncestors()); 179 | 180 | print('-----------------------------------------------'); 181 | print('PROTOCOLS--------------------------------------'); 182 | print('-----------------------------------------------'); 183 | print(mocha.protocols()); 184 | print(mocha.protocolsWithAncestors()); 185 | } 186 | 187 | export function createLabel(text, size, frame) { 188 | var label = NSTextField.alloc().initWithFrame(frame); 189 | 190 | label.setStringValue(text); 191 | label.setFont(NSFont.boldSystemFontOfSize(size)); 192 | label.setBezeled(false); 193 | label.setDrawsBackground(false); 194 | label.setEditable(false); 195 | label.setSelectable(false); 196 | 197 | return label; 198 | } 199 | 200 | export function popUpButtonsforRectangleIndexer_withTitleIndexer_andImageIndexer_defaultSelected_onIndex( 201 | rectangle, 202 | titles, 203 | images, 204 | index 205 | ) { 206 | let button = NSPopUpButton.alloc().initWithFrame(rectangle(index)); 207 | button.addItemsWithTitles(titles); 208 | 209 | if (images != null) { 210 | button.imageScaling = NSImageScaleProportionallyUpOrDown; 211 | 212 | Array.fromNSArray(button.itemArray()).forEach((a, i, as) => { 213 | a.image = images[i]; 214 | }); 215 | } 216 | 217 | return button; 218 | } 219 | 220 | export function smallImagesFromArtboard(artboard) { 221 | // var sketch = require('sketch/dom'); 222 | // var artboard = sketch.fromNative(msartboard); 223 | 224 | // //let image = sketch.export(artboard); 225 | 226 | // print(msartboard.unselectedPreviewImage()); 227 | 228 | // return nil 229 | 230 | if (artboard.class() == MSSymbolMaster) { 231 | print(artboard); 232 | return null; 233 | } 234 | 235 | if (artboard.frame == undefined) { 236 | print(artboard); 237 | } 238 | 239 | let artboardWidth = artboard.frame().width(); 240 | let artboardHeight = artboard.frame().height(); 241 | var artboardRatio = artboardWidth / artboardHeight; 242 | if (artboardRatio > 1) { 243 | artboardRatio = 1 / artboardRatio; 244 | } 245 | 246 | if (artboardRatio > 0.8 || artboardRatio < 0.4) { 247 | return null; 248 | } 249 | 250 | // let layerAncestry = MSImmutableLayerAncestry.alloc().initWithMSLayer(artboard); 251 | // let layerAncestry = this.artboard.ancestry(); 252 | 253 | // MSImmutableLayerAncestry has been renamed (As of V.66.1) and must be called from the class string 254 | let cls = NSClassFromString('SketchModel.MSImmutableLayerAncestry'); 255 | let layerAncestry = cls.alloc().initWithMutableLayer(artboard); 256 | 257 | let biggerDimention = 258 | artboardWidth > artboardHeight ? artboardWidth : artboardHeight; 259 | let exportScale = 48 / biggerDimention; 260 | let exportFormat = MSExportFormat.formatWithScale_name_fileFormat( 261 | exportScale, 262 | '', 263 | 'png' 264 | ); 265 | let exportRequest = MSExportRequest.exportRequestsFromLayerAncestry_exportFormats( 266 | layerAncestry, 267 | [exportFormat] 268 | ).firstObject(); 269 | let exporter = MSExporter.exporterForRequest_colorSpace( 270 | exportRequest, 271 | NSColorSpace.sRGBColorSpace() 272 | ); 273 | 274 | return exporter.previewImage(); 275 | } 276 | -------------------------------------------------------------------------------- /src/symmetry.js: -------------------------------------------------------------------------------- 1 | import Angle from './Angle'; 2 | import * as Shared from './Shared'; 3 | 4 | import { Error } from './Error'; 5 | 6 | export default function (context) { 7 | let selectedLayersNSArray = context.selection[0]; 8 | 9 | if (selectedLayersNSArray == null) { 10 | Shared.show({ 11 | message: Error.emptySelection.message, 12 | inDocument: context.document, 13 | }); 14 | return; 15 | } 16 | 17 | let selectedLayers = Array.fromNSArray(selectedLayersNSArray); 18 | 19 | if (selectedLayers.length == 0) { 20 | Shared.show({ 21 | message: Error.emptySelection.message, 22 | inDocument: context.document, 23 | }); 24 | return; 25 | } 26 | 27 | let possibleAngles = Angle.tryCreating({ for: selectedLayers, in: context }); 28 | 29 | let angles = possibleAngles.filter((a) => a instanceof Angle); 30 | let errors = possibleAngles.filter((a) => !(a instanceof Angle)); 31 | 32 | if (angles.length == 0) { 33 | Shared.show({ 34 | message: errors[0].message, 35 | inDocument: context.document, 36 | }); 37 | return; 38 | } 39 | 40 | angles.forEach((a) => { 41 | a.reverseSymmetry(); 42 | a.applyImage(); 43 | }); 44 | 45 | Shared.show({ 46 | message: 'Angle flipped! ↔️', 47 | inDocument: context.document, 48 | }); 49 | 50 | return; 51 | } 52 | -------------------------------------------------------------------------------- /workspace.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "search.exclude": { 9 | "**/Angle.sketchplugin": true, 10 | "**/Angle.xcodeproj": true 11 | } 12 | }, 13 | } 14 | --------------------------------------------------------------------------------