├── LICENSE ├── README.md └── Radial-Lines.sketchplugin └── Contents ├── Resources └── icon.png └── Sketch ├── Radial-Lines.coscript └── manifest.json /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Herr Helms 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Radial Lines 2 | This is a Plugin for [Sketch App](http://www.sketchapp.com) that generates lines originating from a certain coordinate of an artboard. 3 | 4 | ![Screenshot](https://www.dropbox.com/s/rf8yk0s3cis5rp6/sketchplugin_radial_lines.jpg?raw=1) 5 | 6 | ## Installation 7 | Install this plugin via [Sketch Toolbox](http://sketchtoolbox.com) or download the package manually into to your Sketch3 Plugin folder. The path should resemble the following 8 | ```.../com.bohemiancoding.sketch3/Plugins/radial-lines-master``` **Tipp:** The .sketchplugin file should reside in a directory! 9 | 10 | Select any artboard you have in your document. If there aren't any, go ahead and create a new one. (Pressing the `A` key is the fastes way to do so.) Afterwards either goto the plugins menu and select `Radial Lines -> Distribute Lines` or use keyboard shortcut `Cmd+Alt+b`. 11 | A modal will appear where you can define the setup for the lines you wish to create. 12 | 13 | ### Customization 14 | Within the modal you can choose the origin and quantity of lines, their color, and their stroke width. Optionally the lines will be distributed evenly with the same distance to one another. Or dependent on the artboard's border. (Take a look at the red example in the screenshot above.) 15 | 16 | ### That's all folks 17 | This is my third Sketch Plugin, please feel free to [contact me](http://bit.ly/1MSRFbd) via twitter for comments, suggestions and critique. Take a look at the [first one](http://github.com/herrhelms/social-artboards-sketch) and the [second one](http://github.com/herrhelms/even-guides-sketch). 18 | If you like this plugin consider giving this repository a star! If you're owning Bitcoin feel free to send some satoshis: `1BoFajaVNQ5Z8a3E7ZQvt8sWyTCGrxRtS9`. 19 | Oh and of course you can fork and contribute to the code and extend this package. I will happily merge any tested PRs. 20 | -------------------------------------------------------------------------------- /Radial-Lines.sketchplugin/Contents/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herrhelms/radial-lines-sketch/8c0c9f14b6a74d2a8978f3e29afa07747636ff09/Radial-Lines.sketchplugin/Contents/Resources/icon.png -------------------------------------------------------------------------------- /Radial-Lines.sketchplugin/Contents/Sketch/Radial-Lines.coscript: -------------------------------------------------------------------------------- 1 | var pluginName = 'Radial Lines'; 2 | 3 | var getArtboard = function(selection) { 4 | currentArtboard = selection[0]; 5 | var current_class = [currentArtboard class]; 6 | while(currentArtboard && ([currentArtboard class] !== MSArtboardGroup)) { 7 | currentArtboard.setIsSelected(0); 8 | currentArtboard = [currentArtboard parentGroup]; 9 | } 10 | currentArtboard.setIsSelected(1); 11 | return currentArtboard; 12 | } 13 | 14 | function createSettingsPanel(artboard, doc){ 15 | var formatter = [[NSNumberFormatter alloc] init]; 16 | [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; 17 | var accessoryView = NSView.alloc().initWithFrame(NSMakeRect(0,20,278,150)) 18 | 19 | var checkBox = NSButton.alloc().initWithFrame(NSMakeRect(0,31,18,18)) 20 | [checkBox setButtonType:NSSwitchButton] 21 | [checkBox setStringValue: '']; 22 | [checkBox setBezelStyle:0] 23 | 24 | var checkBoxLabel = NSTextField.alloc().initWithFrame(NSMakeRect(20,27,270,20)) 25 | checkBoxLabel.setStringValue("Align ending points to artboard's border"); 26 | [checkBoxLabel setDrawsBackground:false]; 27 | [checkBoxLabel setEditable:false]; 28 | [checkBoxLabel setEditable:false]; 29 | [checkBoxLabel setSelectable:false]; 30 | [checkBoxLabel setBezeled:false]; 31 | [checkBoxLabel setFont:[NSFont systemFontOfSize:10]]] 32 | 33 | var checkBoxByline = NSTextField.alloc().initWithFrame(NSMakeRect(0,0,278,30)) 34 | checkBoxByline.setStringValue("If uncheckd an invisible circle will be used as path to align the ending points to. Distributing all lines evenly."); 35 | [checkBoxByline setDrawsBackground:false]; 36 | [checkBoxByline setEditable:false]; 37 | [checkBoxByline setEditable:false]; 38 | [checkBoxByline setSelectable:false]; 39 | [checkBoxByline setBezeled:false]; 40 | [checkBoxByline setFont:[NSFont systemFontOfSize:10]]] 41 | 42 | var inputAmountLabel = NSTextField.alloc().initWithFrame(NSMakeRect(0,132,220,20)) 43 | inputAmountLabel.setStringValue('Amount of Lines') 44 | [inputAmountLabel setDrawsBackground:false]; 45 | [inputAmountLabel setEditable:false]; 46 | [inputAmountLabel setSelectable:false]; 47 | [inputAmountLabel setBezeled:false]; 48 | [inputAmountLabel setFont:[NSFont systemFontOfSize:10]]] 49 | 50 | var inputAmount = NSTextField.alloc().initWithFrame(NSMakeRect(0,110,140,25)) 51 | inputAmount.setStringValue('0') 52 | inputAmount.setFormatter(formatter) 53 | 54 | var inputXLabel = NSTextField.alloc().initWithFrame(NSMakeRect(150,132,70,20)) 55 | inputXLabel.setStringValue('X (%)') 56 | [inputXLabel setDrawsBackground:false]; 57 | [inputXLabel setEditable:false]; 58 | [inputXLabel setSelectable:false]; 59 | [inputXLabel setBezeled:false]; 60 | [inputXLabel setFont:[NSFont systemFontOfSize:10]]] 61 | 62 | var inputX = NSTextField.alloc().initWithFrame(NSMakeRect(150,110,55,25)) 63 | inputX.setStringValue('0') 64 | inputX.setFormatter(formatter) 65 | 66 | var inputYLabel = NSTextField.alloc().initWithFrame(NSMakeRect(215,132,70,20)) 67 | inputYLabel.setStringValue('Y (%)') 68 | [inputYLabel setDrawsBackground:false]; 69 | [inputYLabel setEditable:false]; 70 | [inputYLabel setSelectable:false]; 71 | [inputYLabel setBezeled:false]; 72 | [inputYLabel setFont:[NSFont systemFontOfSize:10]]] 73 | 74 | var inputY = NSTextField.alloc().initWithFrame(NSMakeRect(215,110,55,25)) 75 | inputY.setStringValue('0') 76 | inputY.setFormatter(formatter) 77 | 78 | var widthLabel = NSTextField.alloc().initWithFrame(NSMakeRect(0,82,220,20)) 79 | widthLabel.setStringValue('Width') 80 | [widthLabel setDrawsBackground:false]; 81 | [widthLabel setEditable:false]; 82 | [widthLabel setSelectable:false]; 83 | [widthLabel setBezeled:false]; 84 | [widthLabel setFont:[NSFont systemFontOfSize:10]]] 85 | 86 | var widthInput = NSTextField.alloc().initWithFrame(NSMakeRect(0,60,140,25)) 87 | widthInput.setStringValue('0.5') 88 | widthInput.setFormatter(formatter) 89 | var colorLabel = NSTextField.alloc().initWithFrame(NSMakeRect(150,82,220,20)) 90 | colorLabel.setStringValue('Color (#HEXHEX)') 91 | [colorLabel setDrawsBackground:false]; 92 | [colorLabel setEditable:false]; 93 | [colorLabel setSelectable:false]; 94 | [colorLabel setBezeled:false]; 95 | [colorLabel setFont:[NSFont systemFontOfSize:10]]] 96 | 97 | var colorInput = NSTextField.alloc().initWithFrame(NSMakeRect(150,60,140,25)) 98 | colorInput.setStringValue('#AAAAAA') 99 | 100 | accessoryView.addSubview(inputAmountLabel) 101 | accessoryView.addSubview(inputAmount) 102 | accessoryView.addSubview(inputXLabel) 103 | accessoryView.addSubview(inputX) 104 | accessoryView.addSubview(inputYLabel) 105 | accessoryView.addSubview(inputY) 106 | accessoryView.addSubview(widthLabel) 107 | accessoryView.addSubview(widthInput) 108 | accessoryView.addSubview(colorLabel) 109 | accessoryView.addSubview(colorInput) 110 | accessoryView.addSubview(checkBox) 111 | accessoryView.addSubview(checkBoxLabel) 112 | accessoryView.addSubview(checkBoxByline) 113 | 114 | var alert = NSAlert.alloc().init() 115 | alert.informativeText = "Keep in mind that a high amount of lines will have implications on the performance of Sketch."; 116 | alert.setMessageText(pluginName); 117 | alert.addButtonWithTitle('Ok') 118 | alert.addButtonWithTitle('Cancel') 119 | alert.setAccessoryView(accessoryView) 120 | var responseCode = alert.runModal() 121 | var amount = inputAmount.intValue(); 122 | var x = inputX.intValue(); 123 | var y = inputY.intValue(); 124 | var random = 0; 125 | if ([checkBox state]==NSOnState) { 126 | random = 1 127 | } 128 | var width = artboard.frame().width(); 129 | var height = artboard.frame().height(); 130 | var strokewidth = widthInput.floatValue(); 131 | var strokecolor = colorInput.stringValue(); 132 | 133 | var error = false; 134 | if (!error) { 135 | return [responseCode, width, height, amount, x, y, random, strokewidth, strokecolor] 136 | } else { 137 | [doc showMessage: error] 138 | } 139 | } 140 | 141 | var addLines = function(points,artboard, group, w, h, posX, posY, doc) { 142 | var linegroup = [MSLayerGroup new]; 143 | artboard.addLayers([linegroup]); 144 | linegroup.setName('artboard-lines'); 145 | var frame = [linegroup frame] 146 | [frame setWidth:w] 147 | [frame setHeight:h] 148 | 149 | for(var i=0;i<=points.length-1;i++) { 150 | var start = points[i][0]; 151 | var end = points[i][0]; 152 | var line = NSBezierPath.bezierPath(); 153 | line.moveToPoint(NSMakePoint(start, end)); 154 | line.lineToPoint(NSMakePoint(posX,posY)); 155 | var lineshape = MSShapeGroup.shapeWithBezierPath(line); 156 | lineshape.setName('Line - ' + i); 157 | var shapeborder = lineshape.style().addStylePartOfType(1); 158 | shapeborder.color = MSImmutableColor.colorWithSVGString("#AAAAAA").newMutableCounterpart(); 159 | shapeborder.thickness = 0.5; 160 | linegroup.addLayers([lineshape]); 161 | } 162 | } 163 | 164 | var prepareCage = function(artboard, group, doc, w, h, amount, start) { 165 | var borderpath = NSBezierPath.bezierPath(); 166 | borderpath.moveToPoint(NSMakePoint(0,0)); 167 | borderpath.lineToPoint(NSMakePoint(w, 0)); 168 | borderpath.lineToPoint(NSMakePoint(w, h)); 169 | borderpath.lineToPoint(NSMakePoint(0, h)); 170 | borderpath.lineToPoint(NSMakePoint(0, 0)); 171 | return borderpath; 172 | } 173 | 174 | var prepareArtboard = function(artboard, doc) { 175 | var data = createSettingsPanel(artboard, doc); 176 | var w = data[1]; 177 | var h = data[2]; 178 | var amount = data[3]; 179 | var x = data[4]; 180 | var y = data[5]; 181 | var random = data[6]; 182 | var strokewidth = data[7]; 183 | var strokecolor = data[8]; 184 | var posX = (w/100)*x; 185 | var posY = (h/100)*y; 186 | 187 | var orientation = ['north', 'east', 'south', 'west']; 188 | var directionNorthSouth = null; 189 | var directionWestEast = null; 190 | 191 | var group = [MSArtboardGroup new] 192 | artboard.addLayers([group]); 193 | 194 | group.setName('artboard-mask'); 195 | var frame = [group frame] 196 | [frame setWidth:w] 197 | [frame setHeight:h] 198 | 199 | if (y < 50) { 200 | directionNorthSouth = orientation[0] 201 | } else if (y > 50) { 202 | directionNorthSouth = orientation[2] 203 | } 204 | 205 | if (x < 50) { 206 | directionWestEast = orientation[3] 207 | } else if (x > 50) { 208 | directionWestEast = orientation[1] 209 | } 210 | 211 | if (!directionNorthSouth && !directionWestEast) { 212 | // 50 50 213 | var borderpath = prepareCage(artboard, group, doc, w, h, amount, null) 214 | } else { 215 | var borderpath = prepareCage(artboard, group, doc, w, h, amount, directionNorthSouth + '-' + directionWestEast) 216 | } 217 | 218 | if (random == 1) { 219 | var circleshape = MSShapeGroup.shapeWithBezierPath(borderpath); 220 | } else { 221 | var rect = NSMakeRect(posX-(w*2), posY-(h*2), (w*4), (h*4)); 222 | var circle = [NSBezierPath bezierPath]; 223 | [circle appendBezierPathWithOvalInRect:rect] 224 | var circleshape = MSShapeGroup.shapeWithBezierPath(circle); 225 | } 226 | 227 | function createLine(x,y,parent) { 228 | var line = NSBezierPath.bezierPath(); 229 | line.moveToPoint(NSMakePoint(x, y)); 230 | line.lineToPoint(NSMakePoint(posX, posY)); 231 | var lineshape = MSShapeGroup.shapeWithBezierPath(line); 232 | lineshape.setName('Line - ' + i); 233 | var shapeborder = lineshape.style().addStylePartOfType(1); 234 | shapeborder.color = MSImmutableColor.colorWithSVGString(strokecolor).newMutableCounterpart(); 235 | shapeborder.thickness = strokewidth; 236 | parent.addLayers([lineshape]); 237 | } 238 | 239 | var count=amount; 240 | var path=circleshape.bezierPathWithTransforms(); 241 | var step=path.length()/count; 242 | for(var i=1;i<=count;i++) { 243 | var point=path.pointOnPathAtLength(step*i); 244 | createLine(point.x,point.y,group, posX, posY); 245 | } 246 | group.addLayers([circleshape]); 247 | } 248 | 249 | var distributeLines = function(context) { 250 | var doc = context.document; 251 | var selection = context.selection; 252 | var allArtboards = [[doc currentPage] artboards]; 253 | 254 | if ([allArtboards count] === 0) { 255 | [doc showMessage: pluginName + ': Bummer! This plugin needs at least one artboard. Add one first! (A).']; 256 | } else { 257 | if (!(selection && [selection count]) || [selection count] == 0) { 258 | var lastIndex = [allArtboards count] - 1; 259 | nextArtboard = allArtboards[lastIndex]; 260 | nextArtboard.setIsSelected(1); 261 | prepareArtboard(nextArtboard, doc); 262 | } else { 263 | var currentArtboard = getArtboard(selection); 264 | prepareArtboard(currentArtboard, doc); 265 | } 266 | } 267 | } 268 | 269 | // distributeLines(context); 270 | -------------------------------------------------------------------------------- /Radial-Lines.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Radial Lines", 3 | "description": "Generate and distribute lines originating from a certain coordinate of an artboard", 4 | "author": "herrhelms", 5 | "homepage": "https://github.com/herrhelms/radial-lines-sketch", 6 | "version": 1.2, 7 | "identifier": "com.github.herrhelms.radial-lines-sketch", 8 | "updateURL": "https://github.com/herrhelms/radial-lines-sketch", 9 | "compatibleVersion": 3, 10 | "bundleVersion": 1, 11 | "commands": [ 12 | { 13 | "name": "Distribute Lines", 14 | "identifier": "distribute-lines", 15 | "shortcut": "cmd alt b", 16 | "script": "Radial-Lines.coscript", 17 | "handler": "distributeLines" 18 | } 19 | ], 20 | "menu": { 21 | "items": [ 22 | "distribute-lines", 23 | ] 24 | } 25 | } 26 | --------------------------------------------------------------------------------