├── .gitignore ├── LICENSE ├── README.md ├── cloud.png ├── css ├── main.css └── print.css ├── custom_ui.js ├── generators.js ├── index.html ├── print ├── extra_data.js ├── index.html └── print.js └── sketch.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .DS_Store 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Daniel Shiffman 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Community Clouds 2 | 3 | This software is for generating clouds for name tags for the Processing 4 | Community Day. You can try out all of the current submissions on the GitHub 5 | pages site: 6 | 7 | https://codingtrain.github.io/CommunityClouds/ 8 | 9 | 10 | ## About Processing Community Day 11 | 12 | > Community Day will bring together members of the community to discuss work, 13 | > share ideas and experiences, and promote outreach to new members, 14 | > particularly those who are underrepresented in creative and technological 15 | > fields. The event is a time to celebrate, reflect, and look forward. 16 | 17 | > Organized by the Processing Foundation, the day-long event is for anyone 18 | > interested in creative computing, software literacy, accessibility, and 19 | > diversity in the fields of art, technology, and design. The event will be a 20 | > mix of talks and demos by people from all backgrounds within the Processing 21 | > community, as well as the Processing Foundation members, Fellows, and others 22 | > who use Processing, p5.js, and all the related variants in their work. 23 | 24 | Interested? Find out more at the 25 | [Processing Community Day Page][1]. 26 | 27 | ## Contributing 28 | 29 | The main theme is, of course, the community and so it would be great if you 30 | want to submit functions to generate cloud shapes! If you are new to git, you 31 | can check out [Git and GitHub for Poets][2], otherwise feel free to fork this 32 | repository and create a Pull Request with your changes. Need help? Feel free 33 | to raise an issue if there's anything we can help you with! 34 | 35 | To add a new cloud generator, simply edit the `generators.js` file and create 36 | a new function following the example! Your new generator should then show up 37 | in the list on the main page! 38 | 39 | ### What are we looking for? 40 | 41 | Anything really! Perhaps you have a design in your mind, 42 | or perhaps you can take inspiration from this cloud drawing from Processing Day organizer Taeyoon Choi: 43 | 44 | ![Cloud by Taeyoon Choi](cloud.png) 45 | 46 | [0]: https://codingtrain.github.io/CommunityClouds/ 47 | [1]: https://day.processing.org/ 48 | [2]: https://www.youtube.com/watch?v=BCQHnlnPusY&list=PLRqwX-V7Uu6ZF9C0YMKuns9sLDzK6zoiV 49 | -------------------------------------------------------------------------------- /cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/CommunityClouds/321c0cf8848e2ce6ed71d51294d02bcb8d5034e9/cloud.png -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,700,700italic); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | color: #959595; 8 | font-family: 'Open Sans', sans-serif; 9 | transition-timing-function: cubic-bezier(0.57, 0.0, 0.1, 1.0); 10 | } 11 | 12 | body, 13 | html { 14 | height: 100%; 15 | background: #fff; 16 | font-size: 16px; 17 | } 18 | 19 | 20 | /* Generic styles */ 21 | 22 | .l-float { 23 | float: left; 24 | } 25 | 26 | .r-float { 27 | float: right; 28 | } 29 | 30 | 31 | /* Page styling */ 32 | 33 | #sketch-contain { 34 | margin: 0 auto; 35 | text-align: center; 36 | font-size: 0; 37 | height: 100%; 38 | width: 100%; 39 | } 40 | 41 | .form-contain, 42 | .selection-container .opt-all, 43 | input[type="submit"] { 44 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.2); 45 | } 46 | 47 | .form-contain { 48 | opacity: 0.5; 49 | width: 400px; 50 | height: auto; 51 | margin: 0 auto; 52 | text-align: left; 53 | position: absolute; 54 | background: #fff; 55 | transition: all 300ms 200ms; 56 | } 57 | 58 | .form-contain:hover { 59 | opacity: 1; 60 | transition: all 300ms; 61 | } 62 | 63 | .form-contain#form-primary { 64 | top: 36px; 65 | left: 36px; 66 | } 67 | 68 | .form-contain#form-secondary { 69 | bottom: 36px; 70 | left: 36px; 71 | padding: 12px; 72 | } 73 | 74 | .download-svg { 75 | opacity: .3; 76 | display: block; 77 | width: auto; 78 | height: auto; 79 | margin: 0 auto; 80 | text-align: left; 81 | position:absolute; 82 | bottom: 36px; 83 | right: 36px; 84 | padding: 12px; 85 | background: #fff; 86 | -webkit-border-radius: 5px; 87 | -moz-border-radius: 5px; 88 | border-radius: 5px; 89 | -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.2); 90 | -moz-box-shadow: 0 1px 3px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.2); 91 | box-shadow: 0 1px 3px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.2); 92 | 93 | -webkit-transition: all .3s cubic-bezier(.57,.01,.1,1); 94 | -moz-transition: all .3s cubic-bezier(.57,.01,.1,1); 95 | -o-transition: all .3s cubic-bezier(.57,.01,.1,1); 96 | transition: all .3s cubic-bezier(.57,.01,.1,1); 97 | 98 | cursor: pointer; 99 | } 100 | 101 | .download-svg:hover { 102 | 103 | opacity: 1; 104 | -webkit-transition: all .3s cubic-bezier(.57,.01,.1,1); 105 | -moz-transition: all .3s cubic-bezier(.57,.01,.1,1); 106 | -o-transition: all .3s cubic-bezier(.57,.01,.1,1); 107 | transition: all .3s cubic-bezier(.57,.01,.1,1); 108 | } 109 | 110 | .field-information { 111 | position: absolute; 112 | right: 0; 113 | width: auto; 114 | height: auto; 115 | padding: 25px; 116 | padding-bottom: 0px; 117 | overflow-y: auto; 118 | } 119 | 120 | .tooltip-title { 121 | color: #b300ff; 122 | font-weight: bold; 123 | } 124 | 125 | .fc-top, 126 | .fc-lower { 127 | display: block; 128 | width: 100%; 129 | height: auto; 130 | background: #fff; 131 | padding: 0; 132 | } 133 | 134 | .fc-lower, 135 | .fc-name { 136 | background: transparent; 137 | } 138 | 139 | .attribution { 140 | display: table; 141 | width: 100%; 142 | max-width: 400px; 143 | height: auto; 144 | max-height: 31px; 145 | white-space: nowrap; 146 | font-weight: 700; 147 | font-size: 0; 148 | } 149 | 150 | #attrib-title, 151 | #attrib-author { 152 | display: table-cell; 153 | font-size: 0.9rem; 154 | } 155 | 156 | #attrib-title { 157 | color: #fff; 158 | background: #b300ff; 159 | padding: 6px 12px; 160 | width: 1%; 161 | } 162 | 163 | #attrib-author { 164 | background: #fff; 165 | font-size: 0.9rem; 166 | padding: 4px 12px; 167 | border: 2px solid #b300ff; 168 | } 169 | 170 | #attrib-author .by-sep { 171 | color: #c3c3c3; 172 | } 173 | 174 | #attrib-author #author-name { 175 | color: #b300ff; 176 | } 177 | 178 | .selection-container { 179 | z-index: 1000; 180 | position: relative; 181 | } 182 | 183 | .selection-container .select-input { 184 | width: 100%; 185 | max-width: 400px; 186 | height: 31px; 187 | font-weight: 500; 188 | font-size: 0.9rem; 189 | background: #fff; 190 | } 191 | 192 | .selection-container li { 193 | list-style-type: none; 194 | cursor: pointer; 195 | } 196 | 197 | .selection-container .opt-cur { 198 | padding: 6px 12px; 199 | position: relative; 200 | } 201 | 202 | .selection-container .opt-cur:after { 203 | content: ''; 204 | display: inline-block; 205 | height: 6px; 206 | width: 6px; 207 | border: 3px solid #b8b8b8; 208 | border-bottom-width: 0; 209 | border-left-width: 0; 210 | position: absolute; 211 | top: 50%; 212 | right: 0%; 213 | margin-top: -3px; 214 | margin-right: 15px; 215 | transform: rotate(315deg); 216 | transition: all 150ms; 217 | } 218 | 219 | .selection-container.showing .opt-cur:after { 220 | margin-top: -6px; 221 | transform: rotate(135deg); 222 | } 223 | 224 | .selection-container .opt-all { 225 | background: #fff; 226 | min-height: 100px; 227 | max-height: calc(100vh - 300px); 228 | opacity: 0; 229 | pointer-events: none; 230 | overflow-y: scroll; 231 | width: 100%; 232 | z-index: 1000; 233 | transition: opacity 200ms; 234 | } 235 | 236 | .selection-container.showing .opt-all { 237 | opacity: 1; 238 | pointer-events: auto; 239 | } 240 | 241 | .group { 242 | padding: 8px 0; 243 | } 244 | 245 | .group:not(:last-child) { 246 | border-bottom: 2px solid hsl(0, 0%, 96%); 247 | } 248 | 249 | .selection-container .material-select-option { 250 | width: 100%; 251 | padding: 6px 12px 6px 10px; 252 | border-left: 2px solid #fff; 253 | } 254 | 255 | .selection-container .material-select-option:hover { 256 | background: #f7f7f7; 257 | color: #b300ff; 258 | border-left-color: #b300ff; 259 | } 260 | 261 | 262 | /* Styling inputs/buttons */ 263 | 264 | input { 265 | border: none; 266 | outline: none; 267 | } 268 | 269 | input.jscolor { 270 | padding: 2px 6px; 271 | } 272 | 273 | input[type="submit"] { 274 | color: #fff; 275 | background: #b300ff; 276 | padding: 7px 20px; 277 | font-weight: 700; 278 | font-size: 0.9em; 279 | border-radius: 5px; 280 | margin: 25px 0 0 0; 281 | cursor: pointer; 282 | } 283 | 284 | input[type="submit"]:disabled, 285 | input[type="submit"]:active { 286 | box-shadow: none; 287 | } 288 | 289 | input[type="submit"]:disabled { 290 | background: #d8d8d8; 291 | cursor: not-allowed; 292 | } 293 | 294 | input[type="submit"]:active { 295 | background: #2064e6; 296 | } 297 | 298 | input[type="text"] { 299 | width: 100%; 300 | max-width: 400px; 301 | padding: 4px 0 6px; 302 | margin-top: 26px; 303 | font-weight: 700; 304 | font-size: 16px; 305 | color: #8c8c8c; 306 | background: transparent; 307 | border-bottom: 2px solid #d8d8d8; 308 | } 309 | 310 | input[type="text"]:invalid { 311 | box-shadow: none; 312 | } 313 | 314 | input[type="text"]:focus { 315 | border-bottom-color: #b300ff; 316 | color: #464c57; 317 | } 318 | 319 | input[type="text"].invalid { 320 | border-bottom: 3px solid #f43939; 321 | } 322 | 323 | form .text { 324 | width: 100%; 325 | height: 80px; 326 | overflow: hidden; 327 | position: relative; 328 | } 329 | 330 | .label-field { 331 | font-size: 16px; 332 | cursor: text; 333 | position: absolute; 334 | top: 30px; 335 | transition: all 300ms, color 100ms; 336 | } 337 | 338 | .label-field-filled, 339 | .label-field-focus { 340 | top: 12px; 341 | font-size: 11px; 342 | } 343 | 344 | .label-field-filled { 345 | color: #acacac; 346 | } 347 | 348 | .label-field-focus { 349 | color: #b300ff; 350 | } 351 | 352 | span .error-info { 353 | position: absolute; 354 | bottom: 0; 355 | right: 0; 356 | font-size: 13px; 357 | color: #f43939; 358 | cursor: text; 359 | width: auto; 360 | display: inline-block; 361 | height: auto; 362 | text-align: right; 363 | } 364 | -------------------------------------------------------------------------------- /css/print.css: -------------------------------------------------------------------------------- 1 | #svg-wrap { 2 | width: 100%; 3 | max-width: 133vh; 4 | } 5 | 6 | #svg-contain { 7 | position: relative; 8 | width: 100%; 9 | height: 0; 10 | padding-bottom: 75%; 11 | } 12 | 13 | #svg-contain svg { 14 | position: absolute; 15 | width: 100%; 16 | height: 100%; 17 | } 18 | 19 | 20 | #svg-contain.transparent { 21 | background-image: linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%); 22 | background-size: 20px 20px; 23 | background-position: 0 0, 0 10px, 10px -10px, -10px 0px; 24 | } 25 | 26 | #svg-contain.white { 27 | background: #FFF; 28 | } 29 | 30 | #svg-contain.red { 31 | background: #F00; 32 | } 33 | 34 | 35 | #page-template { 36 | width: 0 !important; 37 | height: 0 !important; 38 | } 39 | 40 | #pages svg { 41 | width: 240px; 42 | height: auto; 43 | margin: 10px; 44 | } 45 | 46 | @media print { 47 | #Current { 48 | display: none; 49 | } 50 | 51 | #pages svg { 52 | width: 100%; 53 | height: auto; 54 | margin: 0; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /custom_ui.js: -------------------------------------------------------------------------------- 1 | class FormFields { 2 | constructor() { 3 | this.id = null; 4 | this.nodesRef = null; 5 | } 6 | 7 | setId() { 8 | let id = "", 9 | length = 32, 10 | charSet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 11 | for(let i = 0; i < length; i++) { 12 | let index = Math.floor(Math.random() * charSet.length); 13 | id += charSet[index]; 14 | } 15 | return id; 16 | } 17 | } 18 | 19 | class MaterialText extends FormFields { 20 | constructor(toolTip, rxPattern, rxDesc, required, name, label, value) { 21 | super(); 22 | this.id = this.setId(); 23 | this.toolTip = toolTip || ""; 24 | this.rxPattern = rxPattern || ""; 25 | this.rxDesc = rxDesc || ""; 26 | this.required = required || false; 27 | this.name = name || ""; 28 | this.label = label || ""; 29 | this.value = value || ""; 30 | this.validInput = ""; 31 | this.containerNode = ""; 32 | this.labelNode = ""; 33 | this.inputNode = ""; 34 | this.nodesRef = this.generateTree(); 35 | this.validity = true; 36 | 37 | this.addTextFieldListeners(); 38 | 39 | document.getElementById("clouds-form-options") 40 | .addEventListener("keypress", e => { 41 | let key = e.charCode || e.keyCode || 0; 42 | if(key === 13) e.preventDefault(); 43 | }); 44 | } 45 | 46 | generateTree() { 47 | let c = document.createElement("div"), 48 | l = document.createElement("label"), 49 | i = document.createElement("input"); 50 | 51 | c.classList.add("text"); 52 | 53 | l.classList.add("label-field"); 54 | l.innerText = this.label; 55 | l.setAttribute("for", this.id); 56 | 57 | i.id = this.id; 58 | i.value = this.value; 59 | i.setAttribute("type", "text"); 60 | i.setAttribute("name", this.name); 61 | if(this.required) { 62 | i.setAttribute("required", ""); 63 | } 64 | 65 | c.appendChild(l); 66 | c.appendChild(i); 67 | 68 | this.containerNode = c; 69 | this.labelNode = l; 70 | this.inputNode = i; 71 | 72 | return c; 73 | } 74 | 75 | modifyTextFieldLabel(e) { 76 | if(!this.labelNode) { 77 | return; 78 | } 79 | this.labelNode.classList.toggle("label-field-focus", this.focused); 80 | this.labelNode.classList.toggle("label-field-filled", this.value); 81 | } 82 | 83 | modifyTooltip(e) { 84 | let toolTip; 85 | /**if(this.toolTip && this.inputNode === document.activeElement) { 86 | toolTip = this.label ? "" + this.label + "
" + this.toolTip : this.toolTip; 87 | tooltipContainer.innerHTML = toolTip; 88 | } else { 89 | return; 90 | }**/ 91 | } 92 | 93 | showErrorMessages() { 94 | let errorInfo = this.containerNode.querySelectorAll(".error-info")[0]; 95 | if(this.inputNode.classList.contains("invalid")) { 96 | if(!errorInfo) { 97 | errorInfo = document.createElement("span"); 98 | if(this.required) { 99 | errorInfo.innerText = "Required; " + this.rxDesc; 100 | } else { 101 | errorInfo.innerText = this.rxDesc; 102 | } 103 | errorInfo.classList.add("error-info"); 104 | this.containerNode.appendChild(errorInfo); 105 | } 106 | } else if(errorInfo) { 107 | this.containerNode.removeChild(errorInfo); 108 | } 109 | } 110 | 111 | validateInputEvent(e) { 112 | let rx = new RegExp(this.rxPattern); 113 | this.validity = rx.test(this.value); 114 | 115 | if(this.rxPattern && this.rxPattern.length > 0) { 116 | if(this.validity) { 117 | this.inputNode.className = ""; 118 | } else if(!this.value.length > 0) { 119 | this.inputNode.className = ""; 120 | if(!this.required) { 121 | this.validity = true; 122 | } 123 | } else { 124 | this.inputNode.className = ""; 125 | this.inputNode.classList.add("invalid"); 126 | } 127 | } 128 | if(this.validity || this.value === "") { 129 | this.validInput = this.value; 130 | } 131 | 132 | this.showErrorMessages(); 133 | 134 | return true; 135 | } 136 | 137 | addTextFieldListeners() { 138 | let modifyTextFieldLabel = this.modifyTextFieldLabel.bind(this), 139 | modifyTooltip = this.modifyTooltip.bind(this), 140 | validateInputEvent = this.validateInputEvent.bind(this); 141 | 142 | this.inputNode.addEventListener("focus", modifyTextFieldLabel); 143 | this.inputNode.addEventListener("blur", modifyTextFieldLabel); 144 | this.inputNode.focus(); 145 | this.inputNode.blur(); 146 | 147 | this.inputNode.addEventListener("focus", modifyTooltip); 148 | this.inputNode.addEventListener("blur", modifyTooltip); 149 | this.inputNode.addEventListener("input", validateInputEvent); 150 | this.inputNode.addEventListener("onchange", validateInputEvent); 151 | 152 | this.validateInputEvent(); 153 | } 154 | 155 | get value() { 156 | this.value = this.inputNode.value || ""; 157 | return this._value; 158 | } 159 | 160 | set value(value) { 161 | return this._value = value; 162 | } 163 | 164 | get focused() { 165 | return this.inputNode === document.activeElement; 166 | } 167 | } 168 | 169 | class MaterialSelect extends FormFields { 170 | constructor(selectableOptions, toolTip, sorted, changeCallback) { 171 | super(); 172 | this.id = this.setId(); 173 | this.toolTip = toolTip || ""; 174 | this.value = ""; 175 | this.containerNode = ""; 176 | this.ulNode = ""; 177 | this.curOpt = ""; 178 | this.curIndex = ""; 179 | this.passedOrder = []; 180 | this.optionsInOrder = []; 181 | this.curOptNode = ""; 182 | this.optAllNode = ""; 183 | this.optionsNodes = []; 184 | this.groupNodes = []; 185 | this.groups = {}; 186 | this.default = ""; 187 | this.selectableOptions = selectableOptions || {}; 188 | this.sorted = sorted || false; 189 | this.changeCallback = changeCallback || null; 190 | this.nodesRef = this.generateTree(); 191 | 192 | this.addSelectorListeners(); 193 | } 194 | 195 | generateTree() { 196 | let s = document.createElement("div"), // Selector container 197 | u = document.createElement("ul"), // Outer ul 198 | c = document.createElement("li"), // Current option 199 | a = document.createElement("li"); // All options 200 | 201 | s.id = this.id; 202 | s.classList.add("selection-container"); 203 | s.appendChild(u); 204 | 205 | u.classList.add("select-input"); 206 | u.appendChild(c); 207 | u.appendChild(a); 208 | 209 | c.id = "opt-cur"; 210 | c.classList.add("opt-cur"); 211 | 212 | a.id = "opt-all"; 213 | a.classList.add("opt-all"); 214 | 215 | this.containerNode = s; 216 | this.ulNode = u; 217 | this.curOptNode = c; 218 | this.optAllNode = a; 219 | 220 | this.handleGrouping(); 221 | 222 | this.handleSorting(); 223 | 224 | // Generate all options 225 | this.optionsInOrder.forEach(key => { 226 | let item = this.selectableOptions[key], 227 | preSortIndex = this.passedOrder.indexOf(key), 228 | index = item.index, 229 | group = item.group; 230 | 231 | let tmp = document.createElement("li"); 232 | tmp.innerText = item.value; 233 | this.passedOrder[preSortIndex] = key; 234 | tmp.dataset.optionIndex = index; 235 | tmp.id = key.split(" ").join("_"); 236 | tmp.classList.add("material-select-option"); 237 | this.optionsNodes.push(tmp); 238 | 239 | this.groupNodes[group].appendChild(tmp); 240 | }); 241 | 242 | for(let key in this.groupNodes) { 243 | a.appendChild(this.groupNodes[key]); 244 | } 245 | 246 | this.setOptionByIndex(this.selectableOptions[this.default].index); 247 | 248 | return s; 249 | } 250 | 251 | handleGrouping() { 252 | for(let key in this.selectableOptions){ 253 | let curItem = this.selectableOptions[key], 254 | curItemGroup = curItem.group, 255 | isDefault = curItem.default || false; 256 | if(isDefault){ 257 | if(this.default){ 258 | throw new Error("Only one default option may be set."); 259 | } 260 | this.default = key; 261 | } 262 | if(!curItemGroup){ 263 | curItem.group = "no-group"; 264 | curItemGroup = "no-group"; 265 | } 266 | if(!(curItemGroup in this.groups)) { 267 | this.groups[curItemGroup] = {}; 268 | } 269 | this.groups[curItemGroup][key] = curItem; 270 | } 271 | // Generate top-level group nodes 272 | for(let key in this.groups){ 273 | let g = document.createElement("div"); // Group container 274 | g.classList.add("group"); 275 | g.id = "group-" + key; 276 | this.groupNodes[key] = g; 277 | }; 278 | } 279 | 280 | handleSorting() { 281 | let opts = this.selectableOptions, 282 | k = Object.keys; 283 | this.passedOrder = k(opts); 284 | if(this.sorted) { 285 | // Sort group names before sorting elements in groups 286 | let groupSort = k(this.groups).sort((a, b) => a.localeCompare(b)), 287 | indexTmp = -1; 288 | for(let i in groupSort){ 289 | let itemSort = k(this.groups[groupSort[i]]).sort((a, b) => a.localeCompare(b)); 290 | itemSort.forEach(key => { 291 | indexTmp++; 292 | opts[key].index = indexTmp; 293 | this.optionsInOrder[indexTmp] = key; 294 | }); 295 | } 296 | } else { 297 | let indexTmp = -1; 298 | k(opts).forEach(key => { 299 | indexTmp++; 300 | opts[key].index = indexTmp; 301 | this.optionsInOrder[indexTmp] = key; 302 | }); 303 | } 304 | } 305 | 306 | clickOffReset(e) { 307 | let target = e.target; 308 | let resetCallback = this.clickOffReset.bind(this); 309 | if(!this.containerNode.contains(target)){ 310 | this.containerNode.classList.remove("showing"); 311 | window.removeEventListener("click", resetCallback); 312 | } 313 | } 314 | 315 | chooseOptions(e) { 316 | let target = e.target, 317 | isCurOpt = target === this.curOptNode, 318 | resetCallback = this.clickOffReset.bind(this); 319 | window.addEventListener("click", resetCallback); 320 | if(isCurOpt) { 321 | this.containerNode.classList.toggle("showing"); 322 | return; 323 | } 324 | else { 325 | this.containerNode.classList.remove("showing"); 326 | window.removeEventListener("click", resetCallback); 327 | } 328 | if(target.classList.contains("material-select-option")) { 329 | this.curIndex = target.dataset.optionIndex; 330 | this.setOptionByIndex(this.curIndex); 331 | } 332 | if(this.changeCallback !== null) { 333 | this.changeCallback(); 334 | } 335 | } 336 | 337 | addSelectorListeners(){ 338 | let chooseOptions = this.chooseOptions.bind(this); 339 | this.containerNode.addEventListener("click", chooseOptions); 340 | } 341 | 342 | setOptionByIndex(ind){ 343 | let opts = this.selectableOptions, 344 | curOption = this.optionsInOrder[ind]; 345 | this.curOpt = curOption; 346 | this.value = opts[curOption].value; 347 | this.curOptNode.innerText = this.value; 348 | } 349 | 350 | nextItem(){ 351 | if(this.curIndex < this.optionsInOrder.length - 1){ 352 | this.curIndex++; 353 | this.setOptionByIndex(this.curIndex); 354 | if(this.changeCallback) { 355 | this.changeCallback(); 356 | } 357 | } 358 | return; 359 | } 360 | 361 | previousItem(){ 362 | if(this.curIndex > 0){ 363 | this.curIndex--; 364 | this.setOptionByIndex(this.curIndex); 365 | if(this.changeCallback) { 366 | this.changeCallback(); 367 | } 368 | } 369 | return; 370 | } 371 | 372 | get PresortIndex(){ 373 | return this.passedOrder.indexOf(this.curOpt); 374 | } 375 | 376 | } 377 | -------------------------------------------------------------------------------- /generators.js: -------------------------------------------------------------------------------- 1 | // Edit this file to add your cloud design! 2 | 3 | // Just write a function with a unique name like: 4 | // function perlinNoiseCloud() {} 5 | // and then add a line of code that "registers" it: 6 | // register(perlinNoiseCloud, "Perlin Noise Cloud", "Daniel Shiffman"); 7 | // Here's an example below! 8 | 9 | // Example rounded rectangle cloud 10 | function rectangle() { 11 | // Draw your cloud here 12 | rect(50, 50, width - 100, height - 100, 50); 13 | // Return an internal rectangle that it is safe to draw text within. Of the 14 | // form [top_left, top_right, width, height] 15 | return [100, 100, width - 200, height - 200]; 16 | } 17 | 18 | // Register your function with register(function, style_name, author_name) 19 | register(rectangle, "Example", "example"); 20 | 21 | /* ------ Add your custom cloud generators below! ------ */ 22 | 23 | 24 | //************************************************* 25 | 26 | function PCDCloud() { 27 | translate(width / 2, height / 2); 28 | angleMode(DEGREES); 29 | strokeWeight(1); 30 | noFill(); 31 | let stepsize = 5; 32 | beginShape(); 33 | let a = 0; 34 | 35 | for (let angle = 0; angle < 360; angle += stepsize) { 36 | let offset = abs(sin(a)) * 20; 37 | let xradius = 200 + offset; 38 | let yradius = 100 + offset; 39 | let x = xradius * cos(angle); 40 | let y = yradius * sin(angle); 41 | vertex(x, y); 42 | a = a + 55; 43 | } 44 | endShape(CLOSE); 45 | } 46 | 47 | register(PCDCloud, "PCD Day", "Processing Community"); 48 | 49 | 50 | function ellipseCloud() { 51 | const circleRadius = width / 8; 52 | 53 | const cloudWidth = width - 100 - circleRadius; 54 | const cloudHeight = height - 100 - circleRadius; 55 | 56 | // Getting number of circles based on the width of the cloud and the cloud radius size. 57 | const circleAmount = cloudWidth / (circleRadius * 0.5) * 2; 58 | 59 | push(); 60 | translate(width / 2, height / 2); 61 | 62 | // Drawing outside circles (with stroke) 63 | for (let i = 0; i < circleAmount; i++) { 64 | drawOuterCirc(i); 65 | } 66 | 67 | // Drawing inner circles which hide the inner stroke of the outer circles 68 | for (let i = 0; i < circleAmount; i++) { 69 | drawInnerCirc(i); 70 | } 71 | 72 | // Filling the middle with white for name 73 | noStroke(); 74 | ellipse(0, 0, cloudWidth, cloudHeight); 75 | pop(); 76 | 77 | function drawOuterCirc(num) { 78 | const angle = TWO_PI / circleAmount * num; 79 | 80 | const circleX = cloudWidth / 2 * cos(angle); 81 | const circleY = cloudHeight / 2 * sin(angle); 82 | 83 | push(); 84 | translate(circleX, circleY); 85 | ellipse(0, 0, circleRadius, circleRadius); 86 | pop(); 87 | } 88 | function drawInnerCirc(num) { 89 | const angle = TWO_PI / circleAmount * num; 90 | 91 | // * 0.99 to still show the stroke of the outer circles 92 | const circleX = cloudWidth / 2 * cos(angle) * 0.99; 93 | const circleY = cloudHeight / 2 * sin(angle) * 0.99; 94 | 95 | push(); 96 | noStroke(); 97 | translate(circleX, circleY); 98 | ellipse(0, 0, circleRadius, circleRadius); 99 | pop(); 100 | } 101 | 102 | return [100, 100, width - 200, height - 200]; 103 | } 104 | 105 | register(ellipseCloud, "Ellipse Cloud", "Raqbit"); 106 | 107 | function marmsCloud() { 108 | noStroke(); 109 | var a = 400; 110 | var b = 200; 111 | var xc = 200; 112 | var yc = 100; 113 | 114 | var n = random(80,120); 115 | for(var i = 0; i < n; i++){ 116 | var r = 2*random(30,50); 117 | var rx = r + random(-3,5); 118 | var ry = r + random(-3,5); 119 | var x = random(-a,a); 120 | var y = random(-b,b); 121 | while(((x-xc)/a)*((x-xc)/a) + ((y-yc)/b)*((y-yc)/b) > 1){ 122 | var x = random(-a,a); 123 | var y = random(-b,b); 124 | } 125 | fill(210,210,210); 126 | rect(xc+x,yc+y,rx,ry,10,10); 127 | fill(255,255,255); 128 | rect(xc+x+15,yc+y-15,rx,ry,10,10); 129 | } 130 | return [30,30,2*a-60,2*b-60]; 131 | } 132 | 133 | register(marmsCloud, "Marms Cloud", "Henrique Martinez Rocamora"); 134 | 135 | // Unicode Cloud by Sergio Fernández 136 | function unicodeCloud() { 137 | var size = floor(min(width, height * 2)); 138 | textSize(size); 139 | 140 | var widthOfCloud = size; 141 | var heightOfCloud = widthOfCloud * 0.37; 142 | var textX = (width - widthOfCloud) / 2; 143 | var textY = height - (height - heightOfCloud) / 2; 144 | textAlign(LEFT, BASELINE); 145 | text("☁", textX, textY); 146 | 147 | return [ 148 | textX + (widthOfCloud * 0.25), 149 | textY - (heightOfCloud * 0.7), 150 | widthOfCloud * 0.6, 151 | heightOfCloud * 0.6 152 | ]; 153 | } 154 | register(unicodeCloud, "Unicode", "Sergio Fernández"); 155 | 156 | function flatBottomCloud() { 157 | let radius = width / 4; // I don't really know where I messed up but for now just don't change the radius... 158 | let cloudWidth = width - radius - 100; 159 | let cloudHeight = height - radius - 100; 160 | let circleCount = cloudWidth / (radius*0.5); 161 | 162 | push(); 163 | translate(width / 2, height / 1.75); 164 | ellipse(0,radius/4,cloudWidth,radius); 165 | 166 | for (let i = 0; i < circleCount; i++) { 167 | let angle = -PI / circleCount * i; 168 | 169 | let x = cloudWidth / 2 * cos(angle); 170 | let y = cloudHeight / 2 * sin(angle); 171 | 172 | ellipse(x, y, radius, radius); 173 | } 174 | 175 | noStroke(); 176 | ellipse(0, -radius/5, cloudWidth+radius/2, cloudHeight+radius/10); 177 | pop(); 178 | 179 | return [100, 200, width - 200, height - 400]; 180 | } 181 | 182 | register(flatBottomCloud, "Flat Bottom", "Merijn_DH"); 183 | 184 | function kazakhCloud(radius = 200, min = 8, max = 10) { 185 | // This function draws a cloud which margins are inside of a "circle" with 186 | // a given radius. The circle is not perfect, its boundaries may vary 187 | // according to the value offset. Number of "peak" points is determined via 188 | // min and max values. 189 | let points = []; 190 | let offset = radius / 5; 191 | let numPoints = Math.round(random(min, max)); 192 | for (let i = 0; i < numPoints; i++) { 193 | // generating "peak" points of a cloud away from the center 194 | let angle = (TWO_PI / numPoints) * i; 195 | let away; 196 | while (true) { 197 | away = Math.round(random(radius) + offset); 198 | if (Math.abs(away - radius) <= offset) { 199 | break; 200 | } 201 | } 202 | let x = width / 2 + away * Math.cos(angle); 203 | let y = height / 2 - away * Math.sin(angle); 204 | points.push([x, y]); 205 | } 206 | noStroke(); 207 | fill("#FFF"); 208 | ellipse(width / 2, height / 2, 2.3*radius, 2.3*radius); 209 | for (let i = 0; i < points.length; i++) { 210 | // drawing arcs with the center inbetween every pair of points 211 | let center = []; 212 | let circleRadius; 213 | if (i == points.length - 1) { 214 | center = [(points[i][0] + points[0][0]) / 2, (points[i][1] + points[0][1]) / 2]; 215 | circleRadius = dist(points[i][0], points[i][1], points[0][0], points[0][1]); 216 | } else { 217 | center = [(points[i][0] + points[i+1][0]) / 2, (points[i][1] + points[i+1][1]) / 2]; 218 | circleRadius = dist(points[i][0], points[i][1], points[i+1][0], points[i+1][1]); 219 | } 220 | // finding slope and y-intercept for a line between center and point 221 | let mCircle = (center[1] - points[i][1]) / (center[0] - points[i][0]); 222 | let bCircle = center[1] - mCircle * center[0]; 223 | let x1Origin = (0 - bCircle) / mCircle; // finding line's intercept with X-axis 224 | let x2Origin = center[0]; // another line is a projection of previous on X 225 | // finally getting the angle using found 2 lines and trigonometry 226 | let angle = Math.acos(dist(x2Origin, 0, x1Origin, 0) / dist(center[0], center[1], x1Origin, 0)); 227 | // drawing arcs with right angle offset according to the placement of the center 228 | strokeWeight(10); 229 | stroke("#000"); 230 | fill("#FFF"); 231 | if (center[0] > width / 2 && center[1] < height / 2) { 232 | arc(center[0], center[1], circleRadius, circleRadius, PI+angle, angle); 233 | } else if (center[0] < width / 2 && center[1] < height / 2) { 234 | arc(center[0], center[1], circleRadius, circleRadius, PI-angle, -angle); 235 | } else if (center[0] < width / 2 && center[1] > height / 2) { 236 | arc(center[0], center[1], circleRadius, circleRadius, angle, PI+angle); 237 | } else if (center[0] > width / 2 && center[1] > height / 2) { 238 | arc(center[0], center[1], circleRadius, circleRadius, -angle, PI-angle); 239 | } else if (center[0] == width / 2 && center[1] < height / 2) { 240 | arc(center[0], center[1], circleRadius, circleRadius, PI, 0); 241 | } else if (center[0] == width / 2 && center[1] > height / 2) { 242 | arc(center[0], center[1], circleRadius, circleRadius, 0, PI); 243 | } else if (center[0] < width / 2 && center[1] == height / 2) { 244 | arc(center[0], center[1], circleRadius, circleRadius, HALF_PI, PI + HALF_PI); 245 | } else if (center[0] > width / 2 && center[1] == height / 2) { 246 | arc(center[0], center[1], circleRadius, circleRadius, PI + HALF_PI, HALF_PI); 247 | } 248 | } 249 | let rectSide = Math.round(Math.sqrt(2) * (radius - offset)); 250 | return [width/2 - rectSide/2, height/2 - rectSide/2, radius+offset, radius+offset]; 251 | } 252 | 253 | register(kazakhCloud, "Cloud from Kazakhstan", "Ilyas triple-o-zero"); 254 | 255 | function fluffyCloud(){ 256 | var cloudradius = 0.7*(width/2); 257 | var angle = PI/2; 258 | fill(255); 259 | while(angle<(2.5*PI)){ 260 | ellipse(width/2+cos(angle)*cloudradius,height/2+sin(angle)*0.5*cloudradius,(width/2-sin(angle)*cloudradius)/3); 261 | angle += (width/2-sin(angle)*cloudradius)/(3.9*cloudradius); 262 | } 263 | noStroke(); 264 | ellipse(width/2-4,height/2-cloudradius*0.075,cloudradius*2.4,cloudradius*1.24); 265 | stroke(0); 266 | return [120, 180, width - 240, height - 400]; 267 | } 268 | 269 | register(fluffyCloud, "Fluffy", "egg303"); 270 | 271 | //Draw a nice round cloud by Brandon Blaschke 272 | function proudRoundCloud() { 273 | 274 | //Amount of circles in the cloud 275 | let circleAmou = 15; 276 | 277 | //Radius for surrounding clouds 278 | let radius = 200; 279 | 280 | strokeWeight(5); 281 | fill(255); 282 | 283 | //Position and set up drawing 284 | push(); 285 | angleMode(DEGREES); 286 | translate(width / 2, height / 2); 287 | 288 | //Make outer edge of cloud by going in a circle around the name 289 | for(let i = 0; i < circleAmou; i++) { 290 | let angle = map(i, 0, circleAmou, 0, 360); 291 | let x = 400 * cos(angle); 292 | let y = 250 * sin(angle); 293 | ellipse(x,y, radius, radius); 294 | } 295 | 296 | //Fill the inside with white circles to fill it 297 | noStroke(); 298 | for(let i = 0; i < circleAmou; i++) { 299 | let angle = map(i, 0, circleAmou, 0, 360); 300 | let x = 200 * cos(angle); 301 | let y = 200 * sin(angle); 302 | ellipse(x,y, radius, radius); 303 | } 304 | 305 | //Fill the middle section 306 | ellipse(0,0,950, 550); 307 | pop(); 308 | 309 | return [100, 100, width - 200, height - 200]; 310 | } 311 | 312 | register(proudRoundCloud, "Proud Round Cloud", "Brandon Blaschke"); 313 | 314 | // rndCloud 315 | // Generates a cloud, by drawing ellipses of random width and height on locus of a regular ellipse 316 | // Couldn't add a nice looking stroke to the cloud :( TODO: Add a beautiful stroke 317 | function rndCloud(){ 318 | // Randomess - The main variable of the algorithm. 319 | // Randomness is inversely proportional to time taken to generate 320 | // Randomness is directly proportional to beauty (depends on viewers taste) 321 | 322 | const randomness = random(20,100); 323 | const r = width/2 - randomness; 324 | let x, y; 325 | push(); 326 | translate(width / 2, height / 2); 327 | angleMode(DEGREES) 328 | noStroke(); 329 | for(let i = 0; i <=360;) 330 | { 331 | x = r*cos(i); 332 | y = 0.5* r*sin(i); 333 | w = random(1,2) * randomness; 334 | h = random(1,2) * randomness; 335 | ellipse(x,y,w,h); //Draw the random ellipses 336 | ellipse(0,0,r*2,r); //Draw main ellipse 337 | i += randomness / 10; 338 | } 339 | pop(); 340 | return [100, 100, width - 200, height - 200]; 341 | } 342 | 343 | register(rndCloud, "RNDCloud", "Haider Ali Punjabi (@haideralipunjabi)") 344 | 345 | function cartoonCloud() { 346 | //Inspired by the cloud drawing from Processing Day organizer Taeyoon Choi 347 | function getRandMiniA() { 348 | return random(0, PI/24); 349 | } 350 | let radius = height/2.4; 351 | let angles = [0]; 352 | angleMode(RADIANS); 353 | 354 | push(); 355 | translate(width/2, height/2); 356 | 357 | //Fills the array with random angles that progressivly get bigger up until 2*PI 358 | //This creates the bigger and smaller cloud blobs, making it feel more natural 359 | let totalA = 0; 360 | while (totalA < 2*PI) { 361 | let a = random(PI/20, PI/6); 362 | if (totalA + a > 2*PI) { 363 | a = 2*PI - totalA; 364 | } 365 | totalA += a; 366 | angles.push(totalA); 367 | } 368 | 369 | //We first draw the cloud using begin/endShape() so we can fill() it 370 | noStroke(); 371 | fill(255); 372 | beginShape(); 373 | vertex(2*radius*cos(angles[0]), 2*radius*sin(angles[0])); 374 | for (let i = 0; i < angles.length; i++) { 375 | let a = angles[i]; 376 | let deltaA = angles[(i+1)%angles.length] - a; 377 | if (deltaA < 0)deltaA+=2*PI; 378 | bezierVertex( 379 | 2*1.2*radius*cos(a + deltaA/3), 1.2*radius*sin(a + deltaA/3), 380 | 2*1.2*radius*cos(a + 2*deltaA/3), 1.2*radius*sin(a + 2*deltaA/3), 381 | 2*radius*cos(angles[(i+1)%angles.length]), radius*sin(angles[(i+1)%angles.length]) 382 | ); 383 | } 384 | endShape(); 385 | 386 | //Now we draw the cloud 4 times, each time with a random offset 387 | //This gives the cloud a cartoony feeling 388 | stroke(0, 200); 389 | for (let l = 0; l < 4; l++) { 390 | for (let i = 0; i < angles.length; i++) { 391 | //Draw a bezier curve with a random stroke, a touch of alpha and some RNG 392 | let a = angles[i]; 393 | let deltaA = angles[(i+1)%angles.length] - a; 394 | if (deltaA < 0){deltaA+=2*PI;} 395 | strokeWeight(random(10,14)); 396 | bezier( 397 | 2*radius*cos(a), radius*sin(a), 398 | 2*1.2*radius*cos(a + deltaA/3- getRandMiniA()), 1.2*radius*sin(a + deltaA/3- getRandMiniA()), 399 | 2*1.2*radius*cos(a + 2*deltaA/3+ getRandMiniA()), 1.2*radius*sin(a + 2*deltaA/3+ getRandMiniA()), 400 | 2*radius*cos(angles[(i+1)%angles.length]), radius*sin(angles[(i+1)%angles.length]) 401 | ); 402 | } 403 | } 404 | pop(); 405 | return [width/2 - 2*radius/1.4, height/2 - radius / 1.4, 4 * radius / 1.4, 2 * radius / 1.4]; 406 | } 407 | 408 | register(cartoonCloud, "Cartoon cloud", "@JeBoyJurriaan"); 409 | 410 | function unstableCloud() { 411 | 412 | // You can change: 413 | // sizeFactor (how big/small the cloud will be) 414 | // all colors 415 | // DEBUG (to see how it's made) 416 | 417 | // Utils 418 | const DEBUG = false 419 | const debugColor = (alpha=150) => color(random(255), random(255), random(255), alpha) 420 | const cloudColor = (alpha=255) => color(255, alpha) 421 | const shadowColor = (alpha=255) => color(20, alpha) 422 | const createCloud = (pos, d) => { 423 | return {pos: pos, d: d} 424 | } 425 | const drawCanvasBackground = () => { 426 | noStroke() 427 | fill(0,0,0,25) 428 | rect(0, 0, width, height) 429 | } 430 | const drawCloudBase = (pos, w, h) => { 431 | noStroke() 432 | DEBUG ? fill(debugColor()) : fill(cloudColor()) 433 | rect(pos.x, pos.y, w, h) 434 | } 435 | const drawPuffyCloud = (cloud) => { 436 | noStroke() 437 | DEBUG ? fill(debugColor()) : fill(cloudColor()) 438 | ellipse(cloud.pos.x, cloud.pos.y, cloud.d, cloud.d) 439 | } 440 | 441 | const drawShadowCloud = (cloud) => { 442 | noStroke() 443 | DEBUG ? fill(debugColor()) : fill(shadowColor()) 444 | ellipse(cloud.pos.x, cloud.pos.y, cloud.d * shadowThickness, cloud.d * shadowThickness) 445 | } 446 | 447 | const getArcCenter = (A, B, C) => { 448 | 449 | // Mid-points AB and BC 450 | const midAB = A.copy().add(B).div(2) 451 | const midBC = B.copy().add(C).div(2) 452 | 453 | // Slopes AB and BC 454 | const slopeAB = (B.y-A.y)/(B.x-A.x) 455 | const slopeBC = (C.y-B.y)/(C.x-B.x) 456 | 457 | // Perpendicular lines runing through mid-points AB and BC 458 | const slopePerpAB = -Math.pow(slopeAB, -1) 459 | const slopePerpBC = -Math.pow(slopeBC, -1) 460 | 461 | // determine b to get linear equation standard form y = mx + b 462 | const bAB = midAB.y - slopePerpAB * midAB.x 463 | const bBC = midBC.y - slopePerpBC * midBC.x 464 | 465 | // get intersection point 466 | const x = (bBC - bAB)/(slopePerpAB - slopePerpBC) 467 | const y = slopePerpBC * x + bBC 468 | 469 | if(DEBUG) { 470 | stroke(255) 471 | strokeWeight(5) 472 | line(midAB.x, midAB.y, x, y) 473 | line(midBC.x, midBC.y, x, y) 474 | line(B.x, B.y, C.x, C.y) 475 | line(B.x, B.y, A.x, A.y) 476 | noStroke() 477 | fill(0, 200) 478 | ellipse(A.x, A.y, 20, 20) 479 | ellipse(B.x, B.y, 20, 20) 480 | ellipse(C.x, C.y, 20, 20) 481 | ellipse(x, y, 20, 20) 482 | } 483 | 484 | return createVector(x, y) 485 | } 486 | 487 | // Variable factores 488 | const sizeFactor = 1 489 | const rndBaseHeight = 250 + random(50) 490 | const rndArcCenterHeight = -25 + random(75) 491 | const nPuffyClouds = Math.round(6+random(1)) 492 | const puffyCloudsBaseSize = 300 * sizeFactor 493 | const puffyCloudsSizeOffset = 100 * sizeFactor 494 | const shadowThickness = 1.05 495 | 496 | // Cloud base 497 | const baseWidth = 1100 * sizeFactor 498 | const baseHeight = rndBaseHeight * sizeFactor 499 | const baseCenter = createVector(width*.5, height*.6) 500 | const basePos = createVector(baseCenter.x - baseWidth *.5, baseCenter.y - baseHeight *.5) 501 | 502 | // Puffy clouds 503 | let puffyClouds = [] 504 | const puffyDiameter = baseHeight 505 | const puffyRadius = puffyDiameter*.5 506 | const puffyPos = createVector(basePos.x, basePos.y + baseHeight - puffyRadius) 507 | const puffyLeft = createCloud(puffyPos, puffyDiameter) 508 | const puffyRight = createCloud(puffyPos.copy().add(baseWidth, 0), puffyDiameter) 509 | puffyClouds.push(puffyLeft) 510 | puffyClouds.push(puffyRight) 511 | 512 | // Create arc from 3 points 513 | const A = puffyLeft.pos.copy().sub(puffyRadius*.5, 0) 514 | const B = baseCenter.copy().sub(0, baseHeight*.5).sub(0, rndArcCenterHeight) 515 | const C = puffyRight.pos.copy().add(puffyRadius*.5, 0) 516 | const arcCenter = getArcCenter(A, B, C) 517 | const arcRadius = B.dist(arcCenter) 518 | const arcDiameter = arcRadius * 2 519 | const arcOffsetAngle = atan(-(C.y - arcCenter.y) / (C.x - arcCenter.x)) 520 | const arcTotalAngle = PI - 2*arcOffsetAngle 521 | const arcAngleStep = arcTotalAngle/(nPuffyClouds-1) 522 | 523 | for(let i = 1; i < nPuffyClouds-1; i++) { 524 | const x = cos(i*(arcAngleStep)+arcOffsetAngle)*arcRadius 525 | const y = -sin(i*(arcAngleStep)+arcOffsetAngle)*arcRadius 526 | const sizeOffset = map(y, -sin(arcOffsetAngle) * arcRadius, -arcRadius, 0, puffyCloudsSizeOffset, true) 527 | const r = puffyCloudsBaseSize + random(sizeOffset) 528 | puffyClouds.push(createCloud(createVector(x, y).add(arcCenter), r)) 529 | } 530 | 531 | // Draw 532 | if(DEBUG) { 533 | drawCanvasBackground() 534 | stroke(0) 535 | noFill() 536 | arc(arcCenter.x, arcCenter.y, arcDiameter, arcDiameter, PI+arcOffsetAngle, -arcOffsetAngle) 537 | } 538 | puffyClouds.forEach(drawShadowCloud) 539 | drawCloudBase(basePos, baseWidth, baseHeight) 540 | // line(basePos.x, basePos.y + baseHeigth, basePos.x + baseWidth, basePos.y + baseHeigth) 541 | puffyClouds.forEach(drawPuffyCloud) 542 | stroke(shadowColor()) 543 | strokeWeight(shadowThickness * 5.5 * sizeFactor) 544 | line(basePos.x, basePos.y + baseHeight*1.013, basePos.x + baseWidth, basePos.y + baseHeight*1.013) 545 | 546 | return [basePos.x, basePos.y - baseHeight*.1, baseWidth, baseHeight] 547 | } 548 | 549 | register(unstableCloud, "A Cloud", "@unstablectrl") 550 | 551 | function someCloud(){ 552 | angleMode(DEGREES); 553 | translate(width/2, height/2); 554 | 555 | let fullDeg = 360, 556 | sz = 2; 557 | 558 | noStroke(); 559 | fill(255) 560 | arc(0, 0, 400 * sz, 310 * sz, 0, fullDeg) 561 | 562 | for(let i = 0;i <= fullDeg; i++){ 563 | let s = 200 * sz * sin(i); 564 | let c = 150 * sz * cos(i); 565 | arc(s, c, random(100), random(100), 0, fullDeg); 566 | } 567 | 568 | return [-350, -200, width - 200, height -200]; 569 | } 570 | 571 | register(someCloud, "Some Cloud", "Indmind"); 572 | 573 | // arbitrary cloud by Hung 574 | function arbitraryCloud(){ 575 | let cloud_x = 50; 576 | let cloud_y = 50; 577 | let cloud_width = width - cloud_x * 2; 578 | let cloud_height = height - cloud_y * 2; 579 | 580 | let cloud_center_size = { 581 | "width" : cloud_width - 300, 582 | "height" : cloud_height - 300 583 | }; 584 | 585 | let cloud_center = { 586 | x: (width - cloud_center_size.width) / 2, 587 | y: (height - cloud_center_size.height) / 2 588 | }; 589 | ellipseMode(CORNER); 590 | noStroke(); 591 | // center area for drawing text 592 | ellipse( 593 | cloud_center.x, 594 | cloud_center.y, 595 | cloud_center_size.width, 596 | cloud_center_size.height 597 | ); 598 | // generate random sub clouds 599 | 600 | // sub clouds infos 601 | let number_random_sub_clouds = 10; 602 | let smallest_sub_cloud_width = 200; 603 | let smallest_sub_cloud_height = 200; 604 | 605 | for(let i = 0; i < number_random_sub_clouds; i++){ 606 | let rand_width = random(smallest_sub_cloud_width, cloud_width - 100); 607 | let rand_height = random(smallest_sub_cloud_height, cloud_height - 100); 608 | let rand_x = random(cloud_x + 10, cloud_width - rand_width); 609 | let rand_y = random(cloud_y + 10, cloud_height - rand_height); 610 | ellipse(rand_x, rand_y, rand_width, rand_height); 611 | } 612 | return [cloud_center.x, cloud_center.y, cloud_center_size.width, cloud_center_size.height]; 613 | } 614 | register(arbitraryCloud, "Arbitrary cloud", "Hung Nguyen (fb.com/ZeroXCEH)"); 615 | 616 | function shadowCloud() { 617 | 618 | // A puffy cloud with a shadow by Arjen Klaverstijn info@arjenklaverstijn.com 619 | // https://github.com/arjhun 620 | 621 | angleMode(DEGREES); 622 | 623 | let segments = 0, 624 | radius = 600, 625 | start = random(0, 360), 626 | end = start + 360, 627 | min = 20, 628 | max = 40, 629 | puff = max * 5, 630 | cPoints = []; 631 | 632 | for (let i = start; i < end;) { 633 | 634 | segments = random(min, max); 635 | //next segment 636 | let nextV = i + segments; 637 | // lets fill up the last segment with a reasonable one 638 | if (end - nextV < max - (max - min / 2)) nextV = end; 639 | //create coordinates for bezier segments 640 | cPoints.push( 641 | [radius * sin(i), 642 | radius / 3 * cos(i), 643 | (radius + puff) * sin(i), 644 | (radius / 3 + puff) * cos(i), 645 | (radius + puff) * sin(nextV), 646 | (radius / 3 + puff) * cos(nextV), 647 | radius * sin(nextV), 648 | radius / 3 * cos(nextV) 649 | ]); 650 | i += segments 651 | //if next vertex is at the end stop drawing 652 | if (nextV == end) break; 653 | } 654 | 655 | function drawCloud(offSet) { 656 | push(); 657 | translate(width / 2+offSet, height / 2+offSet); 658 | beginShape(); 659 | for (let c = 0; c < cPoints.length; c++) { 660 | vertex(cPoints[c][0] , cPoints[c][1] ); 661 | bezierVertex(cPoints[c][2] , cPoints[c][3] , cPoints[c][4] , cPoints[c][5] , cPoints[c][6] , cPoints[c][7] ); 662 | } 663 | endShape(); 664 | pop(); 665 | } 666 | 667 | //draw the actuall clouds 668 | 669 | fill(0); // random shadow 670 | drawCloud(random(20,100)); 671 | 672 | fill(255); 673 | drawCloud(0); 674 | 675 | return [width/2-radius, height/2 -(radius/3), radius*2, (radius/3)*2]; 676 | } 677 | 678 | register(shadowCloud, "Cloud with Shadow", "Arjen Klaverstijn (@aklaverstijn)"); 679 | 680 | function CircularCloud() { 681 | // Draw big cloud parts 682 | fill(255, 255, 255, 90); 683 | for(let i = 0; i < 500; i++){ 684 | fill(255, 255, 255, 50); 685 | //the bigger i; the more centered are the circles and the less likely they are outlined 686 | let offsetVec = p5.Vector.random2D(); 687 | let scaleX = random(-600, 600) / (i * 0.001+1); 688 | let scaleY = random(-300, 300) / (i * 0.001+1); 689 | offsetVec.x *= scaleX; 690 | offsetVec.y *= scaleY; 691 | let outside = offsetVec.mag(); 692 | if(random(700) < outside && random() > 0.7){ 693 | strokeWeight(1); 694 | //fill(200, 0, 0); 695 | } 696 | 697 | else strokeWeight(0); 698 | ellipse(width/2 + offsetVec.x, height/2 + offsetVec.y, random(50, width/4)); 699 | } 700 | 701 | // Draw small cloud parts 702 | fill(255, 255, 255, 90); 703 | for(let i = 0; i < 500; i++){ 704 | fill(255, 255, 255, 50); 705 | let offsetVec = p5.Vector.random2D(); 706 | let scaleX = random(-500, 500) / (i * 0.001+1); 707 | let scaleY = random(-250, 250) / (i * 0.001+1); 708 | offsetVec.x *= scaleX; 709 | offsetVec.y *= scaleY; 710 | let outside = offsetVec.mag(); 711 | if(random(3000) < outside && random() > 0.7){ 712 | strokeWeight(1); 713 | //fill(200, 0, 0); 714 | } 715 | else strokeWeight(0); 716 | ellipse(width/2 + offsetVec.x, height/2 + offsetVec.y, random(10, width/8)); 717 | } 718 | 719 | 720 | return [100, 100, width - 200, height - 200]; 721 | } 722 | 723 | register(CircularCloud, "CircularCloud", "lokmeinmatz / Matthias"); 724 | 725 | function cumulus() { 726 | let cloud = []; 727 | const x = width / 2, 728 | y = height / 1.8, 729 | humps = round(random(3, 7)), 730 | diameter = width / humps, 731 | spacing = diameter / 2, 732 | mainHumpPos = 0.5, // 0 = left > 1 = right 733 | piRatio = HALF_PI / (humps / (1 / mainHumpPos)); 734 | 735 | for (let i in [...Array(humps)]) { 736 | // Start with smaller "puffs", larger in the middle, end with small again 737 | let sine = 1 + sin(piRatio * i), 738 | variance = random(1, 0.7 / mainHumpPos) * sine, 739 | radius = spacing * variance, 740 | newX = width/2 - x / 2 + spacing / 2 + spacing * i; // Seriously? That much effort to move half to the left then continue spacing to the right? 741 | cloud.push([newX, y, radius]); // save the cloud so we can double draw, idk how else to do it 742 | } 743 | 744 | // draw black cloud with stroke 745 | cloud.forEach(puff => { 746 | strokeWeight(10); 747 | arc(puff[0], puff[1], puff[2], puff[2], PI, TAU, PIE); 748 | }); 749 | 750 | // draw white cloud without stroke 751 | cloud.forEach(puff => { 752 | fill(255); 753 | strokeWeight(0); 754 | arc(puff[0], puff[1], puff[2], puff[2], PI, TAU, PIE); 755 | }); 756 | 757 | return [x / 2, 0, x, height]; 758 | } 759 | register(cumulus, "Cumulus", "Luke Flego"); 760 | 761 | function arcClouds() { 762 | // draws arc 763 | function drawArc(c1, c2, c3) { 764 | // first and second circle intersection points 765 | let fs_int_points = circleIntersetc(c1.x, c1.y, c1.r, 766 | c2.x, c2.y, c2.r); 767 | // second and third circle intersection points 768 | let st_int_points = circleIntersetc(c2.x, c2.y, c2.r, 769 | c3.x, c3.y, c3.r); 770 | // 771 | let p0_x = fs_int_points[0]; 772 | let p0_y = fs_int_points[1]; 773 | let p1_x = st_int_points[0]; 774 | let p1_y = st_int_points[1]; 775 | // 776 | let v = createVector(1, 0); 777 | let v1 = createVector(p0_x - c2.x, p0_y - c2.y); 778 | let v2 = createVector(p1_x - c2.x, p1_y - c2.y); 779 | let startAngle = -angle(v1, v); 780 | let endAngle = -angle(v2, v); 781 | strokeWeight(3); 782 | stroke(0); 783 | arc(c2.x, c2.y, c2.r * 2, c2.r * 2, startAngle, endAngle); 784 | } 785 | // computes angle between 2 vectors, returns value between [0, 2*PI] 786 | function angle(v1, v2) { 787 | return Math.atan2(v1.x * v2.y - v1.y * v2.x, v1.x * v2.x + v1.y * v2.y); 788 | } 789 | // computes the intersection points between 2 circles 790 | function circleIntersetc(x0, y0, r0, x1, y1, r1) { 791 | // 792 | let d = dist(x0, y0, x1, y1); 793 | // distance between P0 and P2 794 | let a = (r0 * r0 - r1 * r1 + d * d) / (2 * d); 795 | let h = sqrt(r0 * r0 - a * a); 796 | // 797 | let x2 = x0 + a * (x1 - x0) / d; 798 | let y2 = y0 + a * (y1 - y0) / d; 799 | // 800 | let intp1_x = x2 + h * (y1 - y0) / d; 801 | let intp2_x = x2 - h * (y1 - y0) / d; 802 | let intp1_y = y2 - h * (x1 - x0) / d; 803 | let intp2_y = y2 + h * (x1 - x0) / d; 804 | // 805 | return [intp1_x, intp1_y, intp2_x, intp2_y]; 806 | } 807 | noFill(); 808 | translate(width / 2, height / 2); 809 | let r1 = 400; 810 | let r2 = 200; 811 | let increment = 0.5; 812 | let firstCircle, secondCircle, thirdCircle; 813 | let circles = []; 814 | // create circles 815 | for(let angle = 0;angle < 2 * PI;angle+=increment) { 816 | let x = r1 * cos(angle); 817 | let y = r2 * sin(angle); 818 | let rr = random(100, 150); 819 | circles.push({ 820 | x : x, 821 | y : y, 822 | r : rr 823 | }); 824 | } 825 | // draw arcs 826 | for(let j = 0;j < circles.length - 2;j++) { 827 | firstCircle = circles[j]; 828 | secondCircle = circles[j + 1]; 829 | thirdCircle = circles[j + 2]; 830 | if(firstCircle && secondCircle && thirdCircle) { 831 | drawArc(firstCircle, secondCircle, thirdCircle); 832 | } 833 | } 834 | // 835 | firstCircle = circles[circles.length - 2]; 836 | secondCircle = circles[circles.length - 1]; 837 | thirdCircle = circles[0]; 838 | drawArc(firstCircle, secondCircle, thirdCircle); 839 | // 840 | firstCircle = circles[circles.length - 1]; 841 | secondCircle = circles[0]; 842 | thirdCircle = circles[1]; 843 | drawArc(firstCircle, secondCircle, thirdCircle); 844 | // 845 | return [-r1, -r2, 2 * r1, 2* r2]; 846 | } 847 | 848 | register(arcClouds, "Arc Cloud", "edwin.straub"); 849 | 850 | // created by georges Daou 29-sep-2017 851 | function randomSimpleCloud() { 852 | 853 | 854 | let minWidthSub = (5 * width) / 100; 855 | let maxWidthSub = (30 * width) / 100; 856 | 857 | let minHeightSub = (70 * height) / 100; 858 | let maxHeightSub = (85 * height) / 100; 859 | 860 | 861 | let cloudWidth = width - floor(random(minWidthSub, maxWidthSub)) - 100; 862 | let cloudHeight = height - floor(random(minHeightSub, maxHeightSub) - 100); 863 | 864 | push(); 865 | translate(width / 2, height / 2); 866 | 867 | noStroke(); 868 | fill(255); 869 | 870 | //base papa cloud 871 | ellipse(0, 0, cloudWidth, cloudHeight); 872 | 873 | angleMode(DEGREES); 874 | 875 | for (let angle = 0; angle < 360; angle += random(20, 30)) { //generating little cloudinette :) 876 | 877 | //choose right coordinates for the cloudinette not too close to the edge 878 | 879 | let x = ((cloudWidth / 2) * cos(angle)); 880 | 881 | if (abs(x) > cloudWidth / 2 - cloudWidth / 6) 882 | continue; 883 | 884 | let y = (cloudHeight / 2) * sin(angle); 885 | 886 | //pushing the cloudinette a bit to the center for more realistic look 887 | if (y >= 0) 888 | y = random(y - 20, y); 889 | else 890 | y = random(y, y + 20); 891 | 892 | // finally choose width and height in relation of the papa cloud size 893 | ellipse(x, y, random(cloudWidth / 4, cloudWidth / 4 + 15), random(cloudHeight / 2, cloudHeight / 2 + 15)); 894 | } 895 | 896 | pop(); 897 | return [100, 100, width - 200, height - 200]; 898 | } 899 | register(randomSimpleCloud, "Simple Random Cloud", "Georges Daou"); 900 | 901 | // Puffy Cloud 902 | function puffyCloud(){ 903 | const puffRadius = width/6; 904 | const mainRadius = width/2.5; 905 | let x, y; 906 | let stack = []; 907 | push(); 908 | translate(width / 2, height / 2); 909 | angleMode(DEGREES) 910 | noStroke(); 911 | fill(255, 255, 255); 912 | ellipse(0, 0, mainRadius*2, mainRadius); 913 | fill(0, 0, 0); 914 | for(let i = 0; i <=360;) { 915 | x = mainRadius * cos(i); 916 | y = 0.5 * mainRadius * sin(i); 917 | r = puffRadius * random(0.9, 1.5) * ((y+width) / width); 918 | ellipse(x, y, r, r); 919 | i += random(15, 25); 920 | stack.push({x: x, y: y, r: r}); 921 | } 922 | fill(255, 255, 255); 923 | stack.forEach(function (puff) { 924 | let x = puff.x * .97; 925 | let y = puff.y * .97; 926 | let r = puff.r * .985; 927 | ellipse(x, y, r, r); 928 | }); 929 | pop(); 930 | return [100, 100, width - 200, height - 200]; 931 | } 932 | 933 | register(puffyCloud, "Puffy Cloud", "Cary Stanley (@carystanley)") 934 | 935 | function bubblyCloud() { 936 | let RANGE = 30; 937 | let SMALLEST = 0.1; 938 | let SHADOW_A = Math.PI / 8 * 3; 939 | let SHADOW_I = 1.1; 940 | let SIDE = (height < width ? height : width) 941 | 942 | for(var i = 0; i <= RANGE; i++) { 943 | let dist = i * (1 - SMALLEST) * 2 / RANGE - 1 - SMALLEST; 944 | let diameter = SIDE * (1 - dist * dist) * 0.7; 945 | let shadow = i / RANGE * 255 946 | 947 | push(); 948 | 949 | translate(width / 2 - diameter / 2 + random(diameter), height / 2 - diameter / 2 + random(diameter)); 950 | 951 | fill(shadow / 10 + 228); 952 | noStroke(); 953 | smooth(); 954 | 955 | ellipse(0, 0, diameter - 1, diameter - 1); 956 | 957 | var start_a = SHADOW_A + 0.005 - Math.PI / 2; 958 | var stop_a = SHADOW_A - 0.005 + Math.PI / 2; 959 | 960 | fill(shadow / 10 + 200); 961 | arc(0, 0, diameter, diameter, start_a, stop_a, CHORD); 962 | 963 | let m_d = diameter * SHADOW_I; 964 | let ad = asin(diameter / m_d); 965 | 966 | start_a = SHADOW_A - ad; 967 | stop_a = SHADOW_A + ad; 968 | 969 | let r_d = sqrt(m_d * m_d - diameter * diameter) / 2 970 | let offset_x = -r_d * cos(SHADOW_A); 971 | let offset_y = -r_d * sin(SHADOW_A); 972 | 973 | fill(shadow / 10 + 228); 974 | arc(offset_x, offset_y, m_d, m_d, start_a, stop_a, CHORD); 975 | 976 | pop(); 977 | } 978 | return [(width - SIDE) / 2, (height - SIDE) / 2, SIDE, SIDE, 80]; 979 | } 980 | register(bubblyCloud, "Bubbly Cloud", "G4m3M4ni4c"); 981 | 982 | function mcCloud() { 983 | noStroke(); 984 | 985 | const w = width - width / 3; 986 | const h = w / 4; 987 | const dmin = w / 4; 988 | const dmax = w / 2; 989 | const offset = createVector((width - w) / 2, (height - h) / 2); 990 | const gray = 240; 991 | 992 | const getPosition = (i) => { 993 | if (i < w) return createVector(i, 0); 994 | if (i < w + h) return createVector(w, i - w); 995 | if (i < w * 2 + h) return createVector(i - (w + h), h); 996 | if (i < (w + h) * 2) return createVector(0, i - (w * 2 + h)); 997 | return null; 998 | }; 999 | 1000 | let i = random(dmax - dmin); 1001 | let pos; 1002 | while (pos = getPosition(i)) { 1003 | pos.add(offset); 1004 | var d = random(dmin, dmax); 1005 | for (let j = 0; j <= 20; j+=5) { 1006 | fill(gray, gray, gray, map(j, 0, 20, 100, 0)); 1007 | ellipse(pos.x, pos.y + 50 + j, d); 1008 | } 1009 | fill(255, 255, 255); 1010 | ellipse(pos.x, pos.y, d); 1011 | i += d / 2; 1012 | } 1013 | 1014 | rect(offset.x, offset.y, w, h); 1015 | 1016 | return [offset.x, offset.y, w, h]; 1017 | } 1018 | register(mcCloud, "Mc Cloud", "Rodolphe Peccatte"); 1019 | 1020 | function drawBumpyCloud() 1021 | { 1022 | push(); 1023 | strokeWeight(5); 1024 | translate(width / 2, height / 2); 1025 | angleMode(DEGREES); 1026 | let cloudWidth = width / 2; 1027 | let cloudHeight = height / 2; 1028 | let vertices = []; 1029 | 1030 | for (let i = random(5, 15); i < 360; i += random(20, 15)) 1031 | { 1032 | vertices.push( 1033 | { 1034 | x: cos(i) * (cloudWidth / 2), 1035 | y: sin(i) * (cloudHeight / 2) 1036 | }); 1037 | } 1038 | vertices.push(vertices[0]); 1039 | stroke(0); 1040 | fill(255); 1041 | for (let i = 0; i < vertices.length - 1; i++) 1042 | { 1043 | let p1 = vertices[i]; 1044 | let p2 = vertices[i + 1]; 1045 | let dx = p2.x - p1.x; 1046 | let dy = p2.y - p1.y; 1047 | let dst = sqrt(dx * dx + dy * dy); 1048 | ellipse((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, dst, dst); 1049 | } 1050 | noStroke(); 1051 | fill(255); 1052 | ellipse(0, 0, cloudWidth, cloudHeight); 1053 | pop(); 1054 | cloudWidth = cloudWidth * 4 / 5; 1055 | cloudHeight = cloudHeight * 4 / 5; 1056 | 1057 | return [(width - cloudWidth) / 2, (height - cloudHeight) / 2, cloudWidth, cloudHeight]; 1058 | } 1059 | 1060 | register(drawBumpyCloud, "Bumpy Cloud", "Fir3will"); 1061 | 1062 | function fluffyTriangle(){ 1063 | const cw = width - (width / 5); // Cloud width 1064 | const side = round(random(1)); // What side the inside triangle goes to 1065 | const bottomY = (height/3)*2; 1066 | const topY = height/3; 1067 | const topX = (width/5) + (side*(cw/3)) + (noise(100)*(cw/3)); 1068 | const bs = cw/15; 1069 | // Noise used here to get random values close to the middle of the used values 1070 | fill(255); 1071 | 1072 | // bottom 1073 | for(let x=width/5+random(bs); x currentY ? BS_RECT_TOP : BS_RECT_TOP_RIGHT); 1168 | } 1169 | } 1170 | 1171 | topRectList.push({ 1172 | x: currentX, 1173 | y: currentY, 1174 | size: size, 1175 | type: type 1176 | }); 1177 | 1178 | currentY = nextY; 1179 | currentX += size; 1180 | } 1181 | 1182 | // ++++++++++ generate last TOP ELEMENT 1183 | topRectList.push({ 1184 | x: currentX, 1185 | y: currentY, 1186 | size: BS_CLOUD_WIDTH - currentX, 1187 | type: BS_RECT_TOP 1188 | }); 1189 | 1190 | // ++++++++++ generate BOTTOM 1191 | currentX = 0; 1192 | currentY = BS_CLOUD_HEIGHT; 1193 | nextY = currentY; 1194 | type = 0; 1195 | 1196 | while(currentX < BS_CLOUD_WIDTH - BS_CLOUD_MAX_SIZE) { 1197 | size = Math.round(random() * BS_CLOUD_RANDOM_VALUE) + BS_CLOUD_MIN_SIZE; 1198 | nextY += Math.round(random() * size / 2.0 - (size / 4.0)); 1199 | 1200 | if(currentX < BS_CLOUD_WIDTH / 2.0) { 1201 | type = (nextY <= currentY ? BS_RECT_BOTTOM : BS_RECT_BOTTOM_LEFT); 1202 | } else { 1203 | if(bottomRectList.length <= 0) {type = BS_RECT_BOTTOM;} 1204 | else { 1205 | let item = bottomRectList[bottomRectList.length - 1]; 1206 | type = (item.y <= currentY ? BS_RECT_BOTTOM : BS_RECT_BOTTOM_RIGHT); 1207 | } 1208 | } 1209 | 1210 | bottomRectList.push({ 1211 | x: currentX, 1212 | y: currentY, 1213 | size: size, 1214 | type: type 1215 | }); 1216 | 1217 | currentY = nextY; 1218 | currentX += size; 1219 | } 1220 | 1221 | // ++++++++++ generate last BOTTOM ELEMENT 1222 | bottomRectList.push({ 1223 | x: currentX, 1224 | y: currentY, 1225 | size: BS_CLOUD_WIDTH - currentX, 1226 | type: BS_RECT_BOTTOM 1227 | }); 1228 | 1229 | // +++++++++++ generate LEFT 1230 | currentX = 0; 1231 | currentY = topRectList[0].y + topRectList[0].size; 1232 | 1233 | while(currentY < (bottomRectList[0].y - bottomRectList[0].size - BS_CLOUD_MAX_SIZE)) { 1234 | size = Math.round(random() * BS_CLOUD_RANDOM_VALUE) + BS_CLOUD_MIN_SIZE; 1235 | 1236 | type = BS_RECT_LEFT; 1237 | 1238 | leftRectList.push({ 1239 | x: currentX - size, 1240 | y: currentY, 1241 | size: size, 1242 | type: type 1243 | }); 1244 | 1245 | currentY += size; 1246 | } 1247 | 1248 | // +++++++++++ generate last LEFT ELEMENT 1249 | size = (bottomRectList[0].y - bottomRectList[0].size) - currentY; 1250 | leftRectList.push({ 1251 | x: currentX - size, 1252 | y: currentY, 1253 | size: size, 1254 | type: BS_RECT_LEFT 1255 | }); 1256 | 1257 | // +++++++++++ generate RIGHT 1258 | currentX = topRectList[topRectList.length - 1].x + topRectList[topRectList.length - 1].size; 1259 | currentY = topRectList[topRectList.length - 1].y + topRectList[topRectList.length - 1].size; 1260 | 1261 | while(currentY < (bottomRectList[bottomRectList.length - 1].y - BS_CLOUD_MAX_SIZE)) { 1262 | size = Math.round(random() * BS_CLOUD_RANDOM_VALUE) + BS_CLOUD_MIN_SIZE; 1263 | type = BS_RECT_RIGHT; 1264 | 1265 | rightRectList.push({ 1266 | x: currentX, 1267 | y: currentY, 1268 | size: size, 1269 | type: type 1270 | }); 1271 | 1272 | currentY += size; 1273 | } 1274 | 1275 | // +++++++++++ generate last RIGHT ELEMENT 1276 | size = Math.abs((bottomRectList[bottomRectList.length - 1].y) - currentY); 1277 | rightRectList.push({ 1278 | x: currentX, 1279 | y: currentY, 1280 | size: size, 1281 | type: BS_RECT_RIGHT 1282 | }); 1283 | 1284 | 1285 | // draw rects 1286 | beginShape(); 1287 | for(let i in topRectList) { 1288 | let item = topRectList[i]; 1289 | bs_drawRect(item.x, item.y, item.size, item.type); 1290 | } 1291 | for(let i in rightRectList) { 1292 | let item = rightRectList[i]; 1293 | bs_drawRect(item.x, item.y, item.size, item.type); 1294 | } 1295 | for(let i = bottomRectList.length - 1; i >= 0; i--) { 1296 | let item = bottomRectList[i]; 1297 | bs_drawRect(item.x, item.y, item.size, item.type); 1298 | } 1299 | for(let i = leftRectList.length - 1; i >= 0; i--) { 1300 | let item = leftRectList[i]; 1301 | bs_drawRect(item.x, item.y, item.size, item.type); 1302 | } 1303 | endShape(); 1304 | fill(255); 1305 | 1306 | function bs_drawRect(posX, posY, size, type) { 1307 | switch(type) { 1308 | case BS_RECT_LEFT: 1309 | vertex(posX + size, posY + size); 1310 | vertex(posX, posY + size); 1311 | vertex(posX, posY); 1312 | vertex(posX + size, posY); 1313 | break; 1314 | case BS_RECT_TOP_LEFT: 1315 | vertex(posX, posY + size); 1316 | vertex(posX, posY); 1317 | vertex(posX + size, posY); 1318 | break; 1319 | case BS_RECT_BOTTOM_LEFT: 1320 | vertex(posX + size, posY + size); 1321 | vertex(posX, posY + size); 1322 | vertex(posX, posY); 1323 | break; 1324 | case BS_RECT_TOP: 1325 | vertex(posX, posY + size); 1326 | vertex(posX, posY); 1327 | vertex(posX + size, posY); 1328 | vertex(posX + size, posY + size); 1329 | break; 1330 | case BS_RECT_BOTTOM: 1331 | vertex(posX + size, posY); 1332 | vertex(posX + size, posY + size); 1333 | vertex(posX, posY + size); 1334 | vertex(posX, posY); 1335 | break; 1336 | case BS_RECT_BOTTOM_RIGHT: 1337 | vertex(posX + size, posY); 1338 | vertex(posX + size, posY + size); 1339 | vertex(posX, posY + size); 1340 | break; 1341 | case BS_RECT_RIGHT: 1342 | vertex(posX, posY); 1343 | vertex(posX + size, posY); 1344 | vertex(posX + size, posY + size); 1345 | vertex(posX, posY + size); 1346 | break; 1347 | case BS_RECT_TOP_RIGHT: 1348 | vertex(posX, posY); 1349 | vertex(posX + size, posY); 1350 | vertex(posX + size, posY + size); 1351 | break; 1352 | default: break; 1353 | } 1354 | } 1355 | 1356 | return [BS_DRAW_RECT_OFFSET, BS_DRAW_RECT_OFFSET, BS_DRAW_RECT_WIDTH, BS_DRAW_RECT_HEIGHT]; 1357 | } 1358 | 1359 | register(bitStoneRectCloud, "bit-stone rect cloud", "Kuno Zoltner (github: kzoltner)"); 1360 | 1361 | function curveVertexCloud() { 1362 | let radius = width/5; 1363 | let points = floor(random(5)) + 10; 1364 | let cloud = []; 1365 | 1366 | for (let angle = 0; angle < TWO_PI-0.1; angle += TWO_PI/points) { 1367 | let x = 2 * radius * cos(angle) + random(-20, 20); 1368 | let y = radius * sin(angle) + random(-20, 20); 1369 | 1370 | cloud.push(createVector(x,y)); 1371 | } 1372 | 1373 | cloud.push(cloud[0]); 1374 | 1375 | push(); 1376 | translate(width/2, height/2); 1377 | 1378 | noFill(); 1379 | stroke(0); 1380 | strokeWeight(20); 1381 | 1382 | for (let i = 0; i < cloud.length-1; i++) { 1383 | beginShape(); 1384 | curveVertex(0, 0); 1385 | curveVertex(cloud[i].x, cloud[i].y); 1386 | curveVertex(cloud[i+1].x, cloud[i+1].y); 1387 | curveVertex(0, 0); 1388 | endShape(); 1389 | } 1390 | 1391 | pop(); 1392 | 1393 | return [width/2-radius*1.4, height/2-radius/2, width/2+radius*1.4, height/2+radius/2]; 1394 | } 1395 | 1396 | register(curveVertexCloud, "curveVertex() Cloud", "xperion"); 1397 | 1398 | function noCloud() { 1399 | var bg, p, p1, p2, p3, p4, p5, center; 1400 | function drawCloud(points) { 1401 | stroke(bg); 1402 | beginShape(); 1403 | for (var i = 0; i < points.length; i++) { 1404 | vertex(points[i].x, points[i].y); 1405 | } 1406 | endShape(CLOSE); 1407 | 1408 | stroke(0); 1409 | p = points[0]; 1410 | for (var i = 1; i < points.length; i++) { 1411 | drawCloudArc(p, points[i]); 1412 | p = points[i]; 1413 | } 1414 | 1415 | line(points[0].x, points[0].y, points[points.length-1].x, points[points.length-1].y); 1416 | } 1417 | 1418 | function drawCloudArc(begin, end) { 1419 | function f(p,d) { 1420 | return p[d] - ((center[d] - p[d])/2); 1421 | } 1422 | center = {"x": width/2, "y": height/2}; 1423 | bezier(begin.x, begin.y, f(begin, "x"), f(begin,"y")-200, f(end, "x"), f(end, "y")-200, end.x, end.y); 1424 | } 1425 | 1426 | function randomOffset(n) { 1427 | return random(n*2)-n; 1428 | } 1429 | 1430 | p1 = {"x":200, "y":height-300}; 1431 | p2 = {"x":(width/4)+randomOffset(100), "y":240+randomOffset(50)}; 1432 | p3 = {"x":width*2/4+randomOffset(100), "y":150+randomOffset(50)}; 1433 | p4 = {"x":150+(width*3/4)+randomOffset(100), "y":270+randomOffset(50)}; 1434 | p5 = {"x":width-200, "y":height-300}; 1435 | 1436 | bg = 150; 1437 | fill(bg); 1438 | drawCloud([p1,p2,p3,p4,p5].map(function(e){return {"x":e.x+100,"y":e.y}})); 1439 | bg = 255; 1440 | fill(bg); 1441 | drawCloud([p1,p2,p3,p4,p5]); 1442 | 1443 | return [100, 100, width - 200, height - 200]; 1444 | } 1445 | register(noCloud, "There is no cloud", "rnoennig"); 1446 | 1447 | /** 1448 | * To draw a square cloud composed of other squares 1449 | */ 1450 | function squareNotBlueCloud() 1451 | { 1452 | 1453 | push(); 1454 | fill(255); 1455 | stroke(200); 1456 | rectMode(CENTER); 1457 | 1458 | var maxSquare = 60; 1459 | 1460 | // Function to draw n concentric squares from a center (x, y) 1461 | function putSquare(x, y, s, n) { 1462 | 1463 | for (var i = n - 1; i >= 0; --i) { 1464 | 1465 | rect(x, y, s * (i + 1), s * (i + 1)); 1466 | } 1467 | } 1468 | 1469 | // Main body 1470 | var maxLen = floor(min(width, height) / 20); 1471 | putSquare((width / 4) + 3 * maxLen, height / 2, maxLen, 15); 1472 | putSquare((3 * width / 4) - 3 * maxLen, height / 2, maxLen, 15); 1473 | 1474 | // Sub clouds 1475 | for (var i = 0; i < maxSquare; ++i) { 1476 | 1477 | putSquare( 1478 | random((width / 4) - maxLen * 6, (3 * width / 4) + maxLen * 6), 1479 | random((height / 2) - maxLen * 8, (height / 2) + maxLen * 8), 1480 | maxLen, 1481 | floor(random(1, 6)) 1482 | ); 1483 | } 1484 | 1485 | pop(); 1486 | 1487 | return [width / 2, height / 2, maxLen * 15, maxLen * 15, "77F"]; 1488 | } 1489 | 1490 | register(squareNotBlueCloud, "Square Not Blue Cloud", "Juan Sebastian Robles"); 1491 | 1492 | function CloudyCloud() 1493 | { 1494 | //I'm really sorry about this one xD 1495 | translate(width/2,height/2); 1496 | noStroke(); 1497 | for(let i = 0; i < 30; i++) 1498 | ellipse(random(-130,130),random(-15,25),random(50,200)); 1499 | 1500 | return [-130,-50,130*2,50*2]; 1501 | } 1502 | register(CloudyCloud, "Radnomly Generated Cloud", "this.Zohir") 1503 | 1504 | function CloudyMcCloudson(){ 1505 | for(var i = 0; i<15;i++){ 1506 | noStroke(); 1507 | ellipse(random(600,1400),random(200,450),random(300,400),random(300,400)); 1508 | } 1509 | return [700, 200, 400,400]; 1510 | } 1511 | register(CloudyMcCloudson, "Cloudy McCloudson", "Marius Bauer"); 1512 | 1513 | function MaxTaylorCloud() { 1514 | let clouds = [] 1515 | // Play with these to get the desired effect 1516 | const showBorder = true 1517 | const showTransparency = false; 1518 | const puffyNess = 40 1519 | const radius = 150 1520 | 1521 | for (let i = 0; i < puffyNess; i++) { 1522 | let x = width/2 + random(-200, 200) 1523 | let y = height/2 + random(-100, 100) 1524 | clouds.push({ x, y }) 1525 | 1526 | if (!showBorder) { continue; } 1527 | 1528 | noFill() 1529 | strokeWeight(5) 1530 | stroke(0) 1531 | ellipse(x, y, radius, radius) 1532 | } 1533 | 1534 | for (let cloud of clouds) { 1535 | noStroke() 1536 | fill(255, 255, 255, (!showBorder && showTransparency) ? random(170, 255) : 255); 1537 | ellipse(cloud.x, cloud.y, radius, radius) 1538 | } 1539 | 1540 | return [width/2 - 150, height/2 - 50, 300, 100] 1541 | } 1542 | 1543 | register(MaxTaylorCloud, "Cloudy McCloudFace", "Max Taylor"); 1544 | 1545 | // Koch snowflake-shaped cloud 1546 | function kochSnowCloud() { 1547 | stroke(0); 1548 | strokeWeight(2); 1549 | fill(255); 1550 | 1551 | let flake_size = width / 5; 1552 | 1553 | let pos = createVector((width - flake_size) / 2, 3 * height / 4); 1554 | let angle = 0; 1555 | let yscale = height / width; 1556 | 1557 | function move(r) { 1558 | pos.add(createVector(r * Math.cos(angle), r * Math.sin(angle) * yscale)); 1559 | } 1560 | 1561 | beginShape(); 1562 | for (let i = 0; i < 13; i++) { 1563 | koch(flake_size); 1564 | angle -= 2 * PI / 13; 1565 | } 1566 | endShape(CLOSE); 1567 | 1568 | function koch(r) { 1569 | if (r > 5) { 1570 | koch(r / 3); 1571 | angle += PI / 3; 1572 | koch(r / 3); 1573 | angle -= 2 * PI / 3; 1574 | koch(r / 3); 1575 | angle += PI / 3 1576 | koch(r / 3); 1577 | } else { 1578 | vertex(pos.x, pos.y); 1579 | move(r); 1580 | } 1581 | } 1582 | 1583 | return [(width - 3.5 * flake_size) / 2, height / 6, flake_size * 3.5, height / 3]; 1584 | } 1585 | 1586 | register(kochSnowCloud, "Koch Snow-Cloud", "Kristian Wichmann"); 1587 | 1588 | function AbdulCloud(){ 1589 | const canvas = document.querySelector('#defaultCanvas0'); 1590 | noStroke(); 1591 | c = {h:parseInt(canvas.style.height.replace('px',''))/2, w:parseInt(canvas.style.width.replace('px',''))/2}; 1592 | const numberOfElip = random(1,500) 1593 | for(var i = 0; i num*0.95) { 1674 | var percent = map(i, num*0.95, num, 0, 1); 1675 | n = lerp(n, rZero, percent); 1676 | } 1677 | 1678 | // Polar to Cartesian 1679 | // coordinate transformation 1680 | var x = width/2 + n * cos(a); 1681 | var y = height/2 + n * sin(a); 1682 | 1683 | vertex(x, y); 1684 | 1685 | xoff += 0.1; 1686 | 1687 | } 1688 | endShape(CLOSE); 1689 | } 1690 | 1691 | register(roundCloud, "Round Cloud", "Simon Tiger"); 1692 | 1693 | function lightning_cloud(x=0, y=0){ 1694 | noStroke(); 1695 | for (let i = 0; i < 100; i++){ 1696 | const r = random(100); 1697 | ellipse(x + random(-100, 100), y + random(50, 150), r, r); 1698 | } 1699 | 1700 | 1701 | const lightning = function(x, y, strokeweight, length, depth = 1){ 1702 | if (length < 1) return; 1703 | const nextDepth = []; 1704 | const intensity = sqrt(depth) * 10; 1705 | push(); 1706 | noiseSeed(random(100)); 1707 | stroke(255, 255, 255); 1708 | strokeWeight(strokeweight); 1709 | 1710 | let xCurr, yCurr; 1711 | let xPrev = x, yPrev = y; 1712 | for (let offset = 0; offset < length; offset+=10){ 1713 | xCurr = xPrev + map(noise(offset * 0.1), 0, 1, -1, 1) * intensity; 1714 | yCurr = y + offset 1715 | line(xPrev, yPrev, xCurr , yCurr); 1716 | xPrev = xCurr; 1717 | yPrev = yCurr; 1718 | nextDepth.push([xCurr, yCurr]); 1719 | } 1720 | 1721 | pop(); 1722 | 1723 | nextDepth.forEach((e) => { 1724 | for (let i = 0; i < random(-100, 4); i++) 1725 | lightning(e[0], e[1], strokeweight * 0.5, length * 0.4, depth + 1); 1726 | }) 1727 | 1728 | } 1729 | 1730 | lightning(x , y + 100 , 5, 500); 1731 | } 1732 | 1733 | register(lightning_cloud, "Lightning Cloud", "Eitan Porat") 1734 | 1735 | function trainCloud() 1736 | { 1737 | // Draw train. 1738 | push(); 1739 | 1740 | // Position, scale, stroke weight. 1741 | translate(width - 420, height - 100); 1742 | scale(2, 2); 1743 | strokeWeight(3); 1744 | 1745 | // Front. 1746 | beginShape(); 1747 | vertex(60, 40); 1748 | vertex(110, 40); 1749 | vertex(115, 55); 1750 | vertex(110, 70); 1751 | vertex(60, 70); 1752 | endShape(CLOSE); 1753 | strokeWeight(2); 1754 | line(90, 50, 90, 60); 1755 | line(95, 50, 95, 60); 1756 | line(100, 50, 100, 60); 1757 | strokeWeight(3); 1758 | 1759 | // Back. 1760 | beginShape(); 1761 | vertex(0, 10); 1762 | vertex(70, 8); 1763 | vertex(65, 70); 1764 | vertex(10, 50); 1765 | endShape(CLOSE); 1766 | 1767 | // Top. 1768 | beginShape(QUADS); 1769 | vertex(85, 40); 1770 | vertex(80, 10); 1771 | vertex(110, 10); 1772 | vertex(105, 40); 1773 | vertex(75, 10); 1774 | vertex(70, 0); 1775 | vertex(120, 0); 1776 | vertex(115, 10); 1777 | endShape(CLOSE); 1778 | 1779 | // Bottom. 1780 | beginShape(QUADS); 1781 | vertex(60, 70); 1782 | vertex(110, 70); 1783 | vertex(110, 85); 1784 | vertex(50, 85); 1785 | vertex(95, 70); 1786 | vertex(110, 70); 1787 | vertex(125, 90); 1788 | vertex(100, 90); 1789 | endShape(); 1790 | 1791 | // Outer wheels. 1792 | ellipse(35, 70, 60); 1793 | ellipse(80, 89, 20); 1794 | 1795 | // Inner wheels. 1796 | fill(0); 1797 | ellipse(35, 70, 10); 1798 | ellipse(80, 89, 5); 1799 | noFill(); 1800 | pop(); 1801 | 1802 | // Cloud drawing function. 1803 | function drawCloud(cloudRadius, stepAngle, rotationOffset) { 1804 | noStroke(); 1805 | ellipse(0, 0, cloudRadius * 4, cloudRadius * 2); 1806 | stroke(0); 1807 | for (let angle = rotationOffset; angle < rotationOffset + 360; angle += stepAngle) 1808 | { 1809 | const x = cloudRadius * 2 * cos(angle); 1810 | const y = cloudRadius * sin(angle); 1811 | const radius = cloudRadius + random(cloudRadius * 0.2); 1812 | const rotation = atan2(-x, 2 * y) - 90; 1813 | const gapAngle = 360 - random(70, 90); 1814 | const startAngle = -gapAngle + rotation; 1815 | const endAngle = gapAngle + rotation; 1816 | 1817 | noStroke(); 1818 | ellipse(x, y, radius * 2, radius * 2); 1819 | stroke(0); 1820 | arc(x, y, radius * 2, radius * 2, startAngle, endAngle); 1821 | } 1822 | } 1823 | 1824 | // Draw big cloud. 1825 | push(); 1826 | angleMode(DEGREES); 1827 | translate(width / 2 - 80, height / 2 - 80); 1828 | scale(2, 2); 1829 | strokeWeight(3); 1830 | 1831 | const radius = 70; 1832 | drawCloud(radius, Math.floor(360 / random(6, 8)), random(360)); 1833 | pop(); 1834 | 1835 | // Draw small cloud. 1836 | push(); 1837 | translate(width - 290, height - 190); 1838 | strokeWeight(3); 1839 | drawCloud(radius / 4, Math.floor(360 / random(6, 8)), random(360)); 1840 | pop(); 1841 | 1842 | // Return safe drawing area. 1843 | return [width / 2 - 80 - radius * 4, height / 2 - 80 - radius * 2, radius * 8, radius * 4]; 1844 | } 1845 | register(trainCloud, "Train Cloud", "Nils Weber"); 1846 | 1847 | function p5Cloud() { 1848 | 1849 | // words that are used to make cloud stroke 1850 | var words = [ "alpha", "blue", "brightness", "color", "green", "hue", 1851 | "lerpColor", "lightness", "red", "saturation", "background", "clear", 1852 | "colorMode", "fill", "noFill", "noStroke", "stroke", "arc", 1853 | "ellipse", "line", "point", "quad", "rect", "triangle", 1854 | "ellipseMode", "noSmooth", "rectMode", "smooth", "strokeCap", "strokeJoin", 1855 | "strokeWeight", "bezier", "bezierPoint", "bezierTangent", "curve", "curveTightness", 1856 | "curvePoint", "curveTangent", "beginContour", "beginShape", "bezierVertex", "curveVertex", 1857 | "endContour", "endShape", "quadraticVertex", "vertex", "loadModel", "model", 1858 | "plane", "box", "sphere", "cylinder", "cone", "ellipsoid", 1859 | "torus", "HALF_PI", "PI", "QUARTER_PI", "TAU", "TWO_PI", 1860 | "preload", "setup", "draw", "remove", "noLoop", "loop", 1861 | "push", "pop", "redraw", "print", "frameCount", "focused", 1862 | "cursor", "frameRate", "noCursor", "displayWidth", "displayHeight", "windowWidth", 1863 | "windowHeight", "windowResized", "width", "height", "fullscreen", "pixelDensity", 1864 | "displayDensity", "getURL", "getURLPath", "getURLParams", "p5.Element", "createCanvas", 1865 | "resizeCanvas", "noCanvas", "createGraphics", "blendMode", "applyMatrix", "resetMatrix", 1866 | "rotate", "rotateX", "rotateY", "rotateZ", "scale", "shearX", 1867 | "shearY", "translate", "p5.TypedDict", "p5.NumberDict", "append", "arrayCopy", 1868 | "concat", "reverse", "shorten", "shuffle", "sort", "splice", 1869 | "subset", "float", "int", "str", "boolean", "byte", 1870 | "char", "unchar", "hex", "unhex", "join", "match", 1871 | "matchAll", "nf", "nfc", "nfp", "nfs", "split", 1872 | "splitTokens", "trim", "deviceOrientation", "accelerationX", "accelerationY", "accelerationZ", 1873 | "pAccelerationX", "pAccelerationY", "pAccelerationZ", "rotationX", "rotationY", "rotationZ", 1874 | "pRotationX", "pRotationY", "pRotationZ", "setMoveThreshold", "setShakeThreshold", "deviceMoved", 1875 | "deviceTurned", "deviceShaken", "keyIsPressed", "key", "keyCode", "keyPressed", 1876 | "keyReleased", "keyTyped", "keyIsDown", "mouseX", "mouseY", "pmouseX", 1877 | "pmouseY", "winMouseX", "winMouseY", "pwinMouseX", "pwinMouseY", "mouseButton", 1878 | "mouseIsPressed", "mouseMoved", "mouseDragged", "mousePressed", "mouseReleased", "mouseClicked", 1879 | "doubleClicked", "mouseWheel", "touches", "touchStarted", "touchMoved", "touchEnded", 1880 | "createImage", "saveCanvas", "saveFrames", "p5.Image", "loadImage", "image", 1881 | "tint", "noTint", "imageMode", "pixels", "blend", "copy", 1882 | "filter", "get", "loadPixels", "set", "updatePixels", "loadJSON", 1883 | "loadStrings", "loadTable", "loadXML", "httpGet", "httpPost", "httpDo", 1884 | "save", "saveJSON", "saveStrings", "saveTable", "p5.Table", "p5.TableRow", 1885 | "p5.XML", "day", "hour", "minute", "millis", "month", 1886 | "second", "year", "createVector", "p5.Vector", "abs", "ceil", 1887 | "constrain", "dist", "exp", "floor", "lerp", "log", 1888 | "mag", "map", "max", "min", "norm", "pow", 1889 | "round", "sq", "sqrt", "noise", "noiseDetail", "noiseSeed", 1890 | "randomSeed", "random", "randomGaussian", "acos", "asin", "atan", 1891 | "atan2", "cos", "sin", "tan", "degrees", "radians", 1892 | "angleMode", "textAlign", "textLeading", "textSize", "textStyle", "textWidth", 1893 | "textAscent", "textDescent", "loadFont", "text", "textFont", "p5.Font", 1894 | "camera", "perspective", "ortho", "ambientLight", "directionalLight", "pointLight", 1895 | "loadShader", "shader", "normalMaterial", "texture", "ambientMaterial", "specularMaterial", 1896 | "p5.Texture", "p5.RendererGL", "p5.Shader" 1897 | ]; 1898 | 1899 | // vertices of the cloud shape (calculated manually) 1900 | var cloudPoints = [ 1901 | createVector(557, 592), 1902 | createVector(468, 464), 1903 | createVector(497, 318), 1904 | createVector(657, 228), 1905 | createVector(713, 86), 1906 | createVector(874, 26), 1907 | createVector(949, 54), 1908 | createVector(1104, 28), 1909 | createVector(1224, 109), 1910 | createVector(1255, 226), 1911 | createVector(1404, 275), 1912 | createVector(1467, 421), 1913 | createVector(1370, 592), 1914 | createVector(557, 592), 1915 | ] 1916 | 1917 | // function to draw text under angle 1918 | function angledText(textToDraw, x, y, angle) { 1919 | push(); 1920 | translate(x,y); 1921 | rotate(-angle); 1922 | text(textToDraw, 0, 0); 1923 | pop(); 1924 | } 1925 | 1926 | // used in getStringByLength to continue from the last cut position 1927 | var remainderString = ""; 1928 | // function that returns randomly generated string containing words that 1929 | // has approximate length in pixels of argument 1930 | function getStringByLength(length) { 1931 | ret = remainderString; 1932 | 1933 | while(textWidth(ret) < length) { 1934 | word = words[int(random(words.length))]; 1935 | ret += word + " "; 1936 | } 1937 | 1938 | remainderString = ""; 1939 | while(textWidth(ret) > length) { 1940 | remainderString = ret.charAt(ret.length - 1) + remainderString; 1941 | ret = ret.substring(0, ret.length - 1); 1942 | } 1943 | 1944 | return ret; 1945 | } 1946 | 1947 | //save font and other settings 1948 | push(); 1949 | // draw the cloud shape 1950 | strokeWeight(50); 1951 | stroke(255,255,255); 1952 | strokeJoin(ROUND); 1953 | beginShape(); 1954 | for(var i = 0; i < cloudPoints.length; i++) { 1955 | vertex(cloudPoints[i].x, cloudPoints[i].y); 1956 | } 1957 | endShape(CLOSE); 1958 | 1959 | // draw the textual cloud stroke 1960 | textFont("monospace", 30); 1961 | textStyle(BOLD); 1962 | strokeWeight(0); 1963 | fill(0); 1964 | for(var i = 0; i < cloudPoints.length - 1; i++) { 1965 | var startingPoint = cloudPoints[i]; 1966 | var endingPoint = cloudPoints[i+1]; 1967 | 1968 | var distance = startingPoint.dist(endingPoint); 1969 | var angle = p5.Vector.sub(endingPoint, startingPoint).heading(); 1970 | 1971 | // use negative angle because math and canvase use different coordinate system 1972 | angledText(getStringByLength(distance), startingPoint.x, startingPoint.y, -angle); 1973 | } 1974 | // restore font and other settings 1975 | pop(); 1976 | 1977 | // write cloud title 1978 | var titleRect = [763, 106, 420, 224]; 1979 | textSize(56); 1980 | textAlign(CENTER,TOP); 1981 | text("Processing\nCommunity Day", titleRect[0], titleRect[1], titleRect[2], titleRect[3]); 1982 | 1983 | // return rectangle for text (calculated manually) 1984 | return [563, 306, 805, 224]; 1985 | } 1986 | 1987 | register(p5Cloud, "P5.js Cloud", "RedAnt333"); 1988 | 1989 | // Let there be clouds :) 1990 | function letThereBeClouds(){ 1991 | // Very classy. 1992 | class CloudNode{ 1993 | constructor(_x, _y, _index){ 1994 | this.pos = createVector(_x, _y); 1995 | this.size = noise(_index)*width/4; 1996 | } 1997 | 1998 | render(){ 1999 | stroke(0); 2000 | strokeWeight(4); 2001 | fill(255,200); 2002 | ellipse(this.pos.x, this.pos.y, this.size, this.size * 0.618); 2003 | } 2004 | 2005 | } 2006 | var cloudNodes = []; 2007 | 2008 | for (let i = 0; i < 123; i++){ 2009 | 2010 | let x = width/2 + random()*width/8; 2011 | let y = height/2 + random()*height/12; 2012 | 2013 | cloudNodes.push(new CloudNode(x, y, i)); 2014 | cloudNodes[i].render(); 2015 | } 2016 | 2017 | } 2018 | 2019 | register(letThereBeClouds, "Let There Be Clouds", "Red Hen dev"); 2020 | 2021 | 2022 | function bumpCloud(){ 2023 | 2024 | let posX = []; 2025 | let posY = []; 2026 | 2027 | let ROOT_TWO = sqrt(2); 2028 | 2029 | strokeWeight(7); 2030 | stroke(0); 2031 | noFill(); 2032 | 2033 | let x, y, r1, r2; 2034 | 2035 | x = width/2; 2036 | y = height/2; 2037 | r1 = width*3/5; //horizontal radius of ellipse 2038 | r2 = width*3/10; //vertical radius of ellipse 2039 | 2040 | beginShape(); 2041 | 2042 | for(let Oangle = 0; Oangle < TWO_PI; Oangle+=0.35){ //Oangle = outer angle 2043 | let r = random(.75,1); 2044 | for(let Iangle = 0; Iangle < TWO_PI; Iangle+=0.01){ //Iangle = inner angle 2045 | vertex(x + (r1/2 * r * cos(Oangle)) + (width/10 * r * cos(Iangle)), y + (r2/2 * r * sin(Oangle)) + (width/10 * r * sin(Iangle))); 2046 | posX.push(x + (r1/2 * r * cos(Oangle)) + (width/10 * r * cos(Iangle))); 2047 | posY.push(y + (r2/2 * r * sin(Oangle)) + (width/10 * r * sin(Iangle))); 2048 | } 2049 | } 2050 | 2051 | endShape(); 2052 | 2053 | noStroke(); 2054 | fill(255); 2055 | 2056 | beginShape(); 2057 | 2058 | for(let n = 0; n < posX.length; n++){ 2059 | vertex(posX[n],posY[n]); 2060 | } 2061 | 2062 | endShape(); 2063 | 2064 | noStroke(); 2065 | 2066 | ellipse(x, y, r1, r2); 2067 | 2068 | return([-(r1/ROOT_TWO/2)+x , -(r2/ROOT_TWO/2)+y , ROOT_TWO/2*r1 , ROOT_TWO/2*r2]); 2069 | 2070 | } 2071 | 2072 | register(bumpCloud, "bumpCloud", "Xalkor"); 2073 | 2074 | function britishCloud() 2075 | { 2076 | h = height/3; 2077 | w = width/2; 2078 | position = createVector(width/2, height/2); 2079 | numberOfFluffies = 15; 2080 | 2081 | angleMode(DEGREES); 2082 | // First draw the main inner ellipse; 2083 | push(); 2084 | fill(255); 2085 | stroke(255); 2086 | ellipseMode(CENTER); 2087 | // Draw the outer "fluffy" bits/ 2088 | for (let i = 0; i < numberOfFluffies; i++) 2089 | { 2090 | angle = 360/numberOfFluffies * i; 2091 | pX = position.x + ((w/2) * cos(angle)); 2092 | pY = position.y + ((h/2) * sin(angle)); 2093 | r = random(width/22, width/15); 2094 | if (pY > position.y) 2095 | { 2096 | push(); 2097 | stroke(200,200,200); 2098 | fill(210,210,210); 2099 | ellipse(pX-(h/15), pY+(h/15), r*2, r*2); 2100 | pop(); 2101 | } 2102 | ellipse(pX, pY, r*2, r*2); 2103 | } 2104 | 2105 | // Now draw some weather!!!!!! 2106 | type = int(random(1, 4)); 2107 | switch(type) 2108 | { 2109 | case 1: // Lightening!!! 2110 | for(let i = 1; i <= 3; i++) 2111 | { 2112 | //bX = (this.position.x - this.w/2 * 0.8) + (this.w/3*0.8)*i; 2113 | bX = position.x + ((w/2) * cos((i*45))); 2114 | bY = position.y + ((h/2) * sin((i*45))); 2115 | bScale = random(h/250, h/150); 2116 | dir = int(random(0, 2)); 2117 | if (dir === 0) 2118 | bDir = -1; 2119 | else 2120 | bDir = 1; 2121 | col = random(210, 255); 2122 | var bColour = color(160,210,col,200); 2123 | drawBolt(bX, bY, bScale, bDir, bColour); 2124 | } 2125 | break; 2126 | 2127 | case 2: // Rain!!! 2128 | push(); 2129 | for(let i = 1; i <= 10; i++) 2130 | { 2131 | rX = (position.x + (w/25) + ((w/2+(w/12)) * cos(45 + i * 9))); 2132 | rY = (position.y + ((h/2-(h/12)) * sin(50))); 2133 | col = random(100, 255); 2134 | rColour = color(10,65,col,180); 2135 | stroke(rColour); 2136 | strokeWeight(h/12); 2137 | strokeCap(ROUND); 2138 | push(); 2139 | translate(rX, rY); 2140 | rotate(20); 2141 | line(0, 0, 0, (h*0.66)+(i%2*(h/6))); 2142 | pop(); 2143 | } 2144 | pop(); 2145 | break; 2146 | 2147 | case 3: // Snow!!! 2148 | push(); 2149 | for(let i = 1; i <= 5; i++) 2150 | { 2151 | sX = (position.x + (w/25) + ((w/2+(w/12)) * cos(45 + i * 18))); 2152 | sY = (position.y + (w/12) + ((h/2) * sin(50))); 2153 | drawSnowflake(sX, sY+(w/12)); 2154 | } 2155 | pop(); 2156 | 2157 | default: 2158 | break; 2159 | } 2160 | // Now fill the middle with white. 2161 | ellipse(position.x, position.y, w, h); 2162 | pop(); 2163 | 2164 | return [position.x - w/2, position.y - h/2, w, h]; 2165 | 2166 | function drawBolt(startX, startY, scl, direction, colour) 2167 | { 2168 | push(); 2169 | fill(colour); 2170 | noStroke(); 2171 | translate(startX, startY); 2172 | scale(scl); 2173 | quad(0*direction, 0, 2174 | 35*direction, 10, 2175 | 15*direction, 110, 2176 | 0*direction, 75); 2177 | quad(0*direction, 75, 2178 | 15*direction, 110, 2179 | -15*direction, 95, 2180 | -45*direction, 60); 2181 | triangle(-45*direction, 60, 2182 | -15*direction, 95, 2183 | -20*direction, 190); 2184 | pop(); 2185 | } 2186 | 2187 | function drawSnowflake(centreX, centreY) 2188 | { 2189 | push(); 2190 | stroke(255); 2191 | strokeWeight(w/250); 2192 | strokeCap(ROUND); 2193 | translate(centreX, centreY); 2194 | for (let flake = 0; flake < 3; flake++) 2195 | { 2196 | push(); 2197 | scale(random(h/450, h/300)); 2198 | translate(0, 0 + flake*(h/4)); 2199 | rotate(random(0,59)); 2200 | for (let branch = 0; branch < 6; branch++) 2201 | { 2202 | // Draw a branch, and then rotate by 60 degrees. 2203 | line(0, 0, 0, 50); 2204 | line(0, 20, 7, 20) 2205 | line(0, 20, -7, 20); 2206 | line(0, 30, 15, 37); 2207 | line(0, 30, -15, 37); 2208 | line(0, 40, 10, 45); 2209 | line(0, 40, -10, 45); 2210 | rotate(60); 2211 | } 2212 | pop(); 2213 | } 2214 | pop(); 2215 | } 2216 | } 2217 | 2218 | register(britishCloud, "British Cloud", "Jools"); 2219 | 2220 | function EllipseNameTag(){ 2221 | 2222 | //Create ellipse background which Cloud will rest on 2223 | fill(122, 182, 250) //Color of background 2224 | stroke(255) 2225 | strokeWeight(14) 2226 | ellipse(width/2, height/2, width/2, height/2.5); 2227 | 2228 | //Create cloud shadow 2229 | const m = random(25,50) 2230 | fill(255, 50) 2231 | noStroke() 2232 | for (var i = 0; i < m; i++){ 2233 | ellipse(random(width/3,2*width/3),random((window.height/2) + window.height/10,(window.height/2) + window.height/8),random(60,100),random(30,50)); 2234 | } 2235 | 2236 | //Create Cloud 2237 | fill(255) 2238 | const n = random(150,250) 2239 | for(var i = 0; i b 2403 | if( (angle >= 45 && angle <= 135) || (angle >= 225 && angle <= 315) ) 2404 | { ellipse(x, y, a/8, a/8); } 2405 | else if( (angle >= 135 && angle <= 150) || ( angle >= 210 && angle <= 225 ) 2406 | || (angle >= 315 && angle <= 330) || ( angle >= 30 && angle <= 45 ) ) 2407 | { ellipse(x, y, a/10, a/10); } 2408 | else 2409 | { ellipse(x, y, a/13, a/13); } 2410 | } 2411 | 2412 | // draw larger ellipse with no border over smaller circles 2413 | // large ellipse will "erase" inner halves of smaller circles 2414 | stroke(255); 2415 | ellipse(0, 0, a, b); 2416 | } 2417 | 2418 | register(simpleCloud, "Simple Cloud", "GreenMoonArt"); 2419 | 2420 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CommunityClouds 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 | by 16 | 17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Background color: 28 |
29 |
30 |
31 | 32 | Download SVG 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /print/extra_data.js: -------------------------------------------------------------------------------- 1 | var needs_stroke_added = [ 2 | 2, 3 | 3, 4 | 8, 5 | 11, 6 | 12, 7 | 17, 8 | 19, 9 | 20, 10 | 27, 11 | 28, 12 | 31, 13 | 32, 14 | 33, 15 | 34, 16 | 39, 17 | 40 18 | ]; 19 | 20 | var custom_size = { 21 | 35: 800 22 | } 23 | -------------------------------------------------------------------------------- /print/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CommunityClouds 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 32 | 33 | 38 | 39 | 40 | 41 | 42 | 43 |
44 |
45 |
46 |
47 | 58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /print/print.js: -------------------------------------------------------------------------------- 1 | let canvas; 2 | let canvasSvg; 3 | let generators = []; 4 | 5 | let currentSvg; 6 | let container; 7 | let generatorSelect; 8 | let sizeAdjust; 9 | let backgroundSelect; 10 | let addToPage; 11 | let addOneOfEach; 12 | 13 | let currentPage; 14 | let pageContainer; 15 | 16 | const outputWidth = 1200; 17 | const outputHeight = 900; 18 | 19 | function setup() { 20 | container = document.getElementById('svg-contain'); 21 | sizeAdjust = document.getElementById('size-adjust'); 22 | generatorSelect = document.getElementById('generator'); 23 | backgroundSelect = document.getElementById('background'); 24 | pageContainer = document.getElementById('pages'); 25 | addToPage = document.getElementById('add-to-page'); 26 | addOneOfEach = document.getElementById('add-one-of-each'); 27 | 28 | currentPage = new_page(); 29 | 30 | let scale = 0.1 + sizeAdjust.value / 100; 31 | canvas = createCanvas(1200 * scale, 900 * scale); 32 | canvas.parent("sketch-contain"); 33 | noLoop(); 34 | 35 | sizeAdjust.onchange = function() { 36 | let scale = 0.1 + sizeAdjust.value / 100; 37 | resizeCanvas(1200 * scale, 900 * scale); 38 | } 39 | 40 | generators.forEach(function(gen, i) { 41 | let elem = document.createElement('option'); 42 | elem.value = i; 43 | elem.innerHTML = gen.name; 44 | generatorSelect.appendChild(elem); 45 | }); 46 | 47 | generatorSelect.onchange = redraw; 48 | 49 | container.className = backgroundSelect.value; 50 | backgroundSelect.onchange = function() { 51 | container.className = backgroundSelect.value; 52 | } 53 | 54 | addToPage.onclick = add_to_page; 55 | 56 | addOneOfEach.onclick = function() { 57 | generators.forEach(function(gen, i) { 58 | generatorSelect.value = i; 59 | if(custom_size[i]) { 60 | resizeCanvas(custom_size[i], custom_size[i] / 4 * 3); 61 | } else { 62 | resizeCanvas(1200, 900); 63 | } 64 | add_to_page(); 65 | }); 66 | } 67 | } 68 | 69 | function draw() { 70 | canvasSvg = new C2S(width, height); 71 | canvas.drawingContext = canvasSvg; 72 | // Establish our default cloud drawing paremeters. 73 | rectMode(CORNER); 74 | ellipseMode(CENTER); 75 | angleMode(RADIANS); 76 | strokeWeight(10); 77 | // These are cached, so need to be reset 78 | stroke("#FFF"); 79 | fill("#000"); 80 | stroke("#000"); 81 | fill("#FFF"); 82 | var idx = +generatorSelect.value; 83 | gen = generators[idx]; 84 | console.log(idx, gen); 85 | gen.fn(); 86 | 87 | var xmlns = "http://www.w3.org/2000/svg"; 88 | currentSvg = canvasSvg.getSvg(); 89 | currentSvg.setAttribute('width', outputWidth); 90 | currentSvg.setAttribute('height', outputHeight); 91 | scale_svg(currentSvg, true); 92 | currentSvg.setAttribute('viewBox', `0 0 ${outputWidth} ${outputHeight}`); 93 | var g = currentSvg.lastChild; 94 | currentSvg.removeChild(g); 95 | var container_g = document.createElementNS(xmlns, 'g'); 96 | container_g.appendChild(g); 97 | if(needs_stroke_added.indexOf(idx) > -1) { 98 | container_g.appendChild(g.cloneNode(true)); 99 | alter_stroke(20, g); 100 | } 101 | currentSvg.appendChild(container_g); 102 | 103 | while(container.lastChild) { 104 | container.removeChild(container.lastChild); 105 | } 106 | container.appendChild(currentSvg); 107 | } 108 | 109 | 110 | function scale_svg(svg, append, margin) { 111 | if(append) { 112 | document.body.appendChild(svg); 113 | } 114 | margin = 50 || margin; 115 | var clientBounds = svg.getBoundingClientRect(); 116 | var internalBounds = svg.lastChild.getBoundingClientRect(); 117 | console.log(clientBounds, internalBounds); 118 | var x = clientBounds.x - internalBounds.x + margin; 119 | var y = clientBounds.y - internalBounds.y + margin; 120 | var scaleX = clientBounds.width / (internalBounds.width + 2 * margin); 121 | var scaleY = clientBounds.height / (internalBounds.height + 2 * margin); 122 | var scale = min(scaleX, scaleY); 123 | var original = svg.lastChild.getAttribute('transform') || ''; 124 | var transform = `scale(${scale}, ${scale}) translate(${x}, ${y}) ${original}`; 125 | console.log(scaleX, scaleY, scale); 126 | console.log(transform); 127 | svg.lastChild.setAttribute('transform', transform); 128 | if(append) { 129 | document.body.removeChild(svg); 130 | } 131 | } 132 | 133 | function alter_stroke(size, elem) { 134 | if(elem.transform) { 135 | for(let i = 0; i < elem.transform.baseVal.length; i++) { 136 | let transform = elem.transform.baseVal.getItem(i); 137 | if(transform.type === SVGTransform.SVG_TRANSFORM_SCALE) { 138 | let scale_mult = min(transform.matrix.a, transform.matrix.d); 139 | size /= scale_mult; 140 | console.log(transform); 141 | } 142 | } 143 | } 144 | let color = elem.getAttribute('stroke'); 145 | let width = elem.getAttribute('stroke-width') || 0; 146 | width = +width; 147 | color = "#000"; 148 | width += size; 149 | elem.setAttribute('stroke', color); 150 | elem.setAttribute('stroke-width', width); 151 | for(child of elem.children) { 152 | alter_stroke(size, child); 153 | } 154 | } 155 | 156 | function new_page() { 157 | let template = document.getElementById('page-template').cloneNode(true); 158 | template.id = ""; 159 | let anchor = document.createElement('a'); 160 | anchor.target = "_blank"; 161 | anchor.appendChild(template); 162 | pageContainer.appendChild(anchor); 163 | let page = { 164 | a: anchor, 165 | elements: 0, 166 | svg: template, 167 | }; 168 | update_link(page); 169 | return page; 170 | } 171 | 172 | function update_link(page) { 173 | let blob = new Blob([page.svg.outerHTML], {type:"image/svg+xml;charset=utf-8"}); 174 | let svgUrl = URL.createObjectURL(blob); 175 | page.a.href = svgUrl; 176 | } 177 | 178 | function add_to_page() { 179 | let id = currentPage.elements; 180 | let offsetX = (id % 2) * outputWidth + 75; 181 | let offsetY = Math.floor(id / 2) * outputHeight + 300; 182 | let newPart = currentSvg.lastChild.cloneNode(true); 183 | currentPage.svg.appendChild(newPart); 184 | newPart.setAttribute('transform', `translate(${offsetX}, ${offsetY})`); 185 | update_link(currentPage); 186 | currentPage.elements += 1; 187 | if(currentPage.elements === 6) { 188 | currentPage = new_page(); 189 | } 190 | redraw(); 191 | } 192 | 193 | // Register a new cloud generator. 194 | function register(fn, name, creator) { 195 | generators.push({ 196 | fn: fn, 197 | name: name, 198 | creator: creator 199 | }); 200 | } 201 | -------------------------------------------------------------------------------- /sketch.js: -------------------------------------------------------------------------------- 1 | let nameInput, 2 | selectionInput, 3 | canvas, 4 | generators = [], 5 | generator, 6 | bounds, 7 | formContainer = document.querySelectorAll(".form-contain")[0], 8 | titleElement = document.getElementById("attrib-title"), 9 | authorElement = document.getElementById("author-name"), 10 | backgroundColor = "#77B5FE", 11 | download = document.getElementById("download"), 12 | canvasSvg, 13 | theSeed; 14 | 15 | // Resizes the canvas to match the CSS 16 | function resize() { 17 | let parStyle = window.getComputedStyle(canvas.elt.parentNode), 18 | cWidth = parseInt(parStyle.width), 19 | cHeight = parseInt(parStyle.height); 20 | resizeCanvas(cWidth, cHeight, true); 21 | } 22 | 23 | // When the window resizes 24 | function windowResized() { 25 | resize(); 26 | redraw(); 27 | } 28 | 29 | function keyReleased() { 30 | if (keyCode == UP_ARROW || keyCode == LEFT_ARROW) { 31 | selectionInput.previousItem(); 32 | } else if (keyCode == DOWN_ARROW || keyCode == RIGHT_ARROW) { 33 | selectionInput.nextItem(); 34 | } 35 | return false; 36 | } 37 | 38 | // On setup 39 | function setup() { 40 | // Default canvas size 41 | canvas = createCanvas(window.innerWidth, window.innerHeight); 42 | canvas.parent("sketch-contain"); 43 | // When clicked 44 | canvas.elt.addEventListener("click", redraw); 45 | noLoop(); 46 | resize(); 47 | 48 | // Create list of selectable items 49 | // Begin selectable options object 50 | let selectOptions = { 51 | "random": { 52 | value : "Random", 53 | group : "_default", // Added underscore to sort before "community" 54 | default : true /** Group names should be used solely 55 | * to differentiate visual groups. 56 | * the default property allows users to 57 | * set a default, but keep it inline 58 | * with whichever group they're using. 59 | */ 60 | } 61 | }; 62 | // Finish populating selectOptions 63 | generators.forEach((n, i) => { 64 | selectOptions[n.name.toLowerCase()] = { 65 | value: n.name, 66 | group: "community" 67 | }; 68 | }); 69 | // Generate selector 70 | selectionInput = new MaterialSelect(selectOptions, "", true, redraw); 71 | // Add to clouds form 72 | select("#clouds-form-generator").elt.appendChild(selectionInput.nodesRef); 73 | 74 | // Material Design input field 75 | nameInput = new MaterialText("", "", "", true, "user_name", "Name", ""); 76 | // Add to clouds form 77 | select("#clouds-form-options").elt.appendChild(nameInput.nodesRef); 78 | // Listen for value changes to redraw() 79 | nameInput.inputNode.addEventListener("input", redraw); 80 | 81 | } 82 | 83 | function updateBg(color) { 84 | backgroundColor = color.toHEXString(); 85 | redraw(); 86 | } 87 | 88 | function handleDrawing(){ 89 | push(); 90 | 91 | document.getElementsByTagName("body")[0].style.background = backgroundColor; 92 | background(backgroundColor); 93 | 94 | scale(.7); 95 | translate(width * .2, height * .2); 96 | 97 | // Establish our default cloud drawing paremeters. 98 | rectMode(CORNER); 99 | ellipseMode(CENTER); 100 | angleMode(RADIANS); 101 | strokeWeight(10); 102 | stroke("#000"); 103 | fill("#FFF"); 104 | // Render the chosen cloud and 105 | bounds = generator.fn(); 106 | 107 | if (bounds) { 108 | // Reset styles for the text 109 | fill(bounds.length > 4 ? bounds[4] : "#000"); 110 | strokeWeight(0); 111 | textSize(16); 112 | textAlign(CENTER, CENTER); 113 | 114 | textSize(100); 115 | // Output the name (Hopefully within the bounds) 116 | let theName = nameInput.validInput ? nameInput.validInput : "Example Name"; 117 | text(theName, bounds[0], bounds[1], bounds[2], bounds[3]); 118 | } else { 119 | console.log(generator.name + " by " + generator.creator + ", did not return bounds.") 120 | } 121 | // Describe which design 122 | titleElement.innerHTML = generator.name; 123 | authorElement.innerHTML = generator.creator; 124 | 125 | pop(); 126 | } 127 | 128 | function genRandomSeed(min, max) { 129 | return Math.random() * (max - min) + min; 130 | } 131 | 132 | function setSvgDownload(e){ 133 | let tmpContext = canvas.drawingContext; 134 | 135 | let canStyleWidth = parseInt(canvas.elt.style.width), 136 | canStyleHeight = parseInt(canvas.elt.style.height); 137 | 138 | canvasSvg = new C2S(canStyleWidth, canStyleHeight); 139 | canvas.drawingContext = canvasSvg; 140 | 141 | randomSeed(theSeed); 142 | handleDrawing(); 143 | let theSVG = canvasSvg.getSerializedSvg(true), 144 | svgBlob = new Blob([theSVG], {type:"image/svg+xml;charset=utf-8"}), 145 | svgUrl = URL.createObjectURL(svgBlob); 146 | 147 | download.setAttribute("href", svgUrl); 148 | canvas.drawingContext = tmpContext; 149 | } 150 | 151 | function draw() { 152 | 153 | download.removeEventListener("mousedown", setSvgDownload); 154 | 155 | theSeed = genRandomSeed(1, 100); 156 | 157 | if (selectionInput.curOpt !== 'random') { 158 | // Get generator chosen 159 | generator = generators[selectionInput.PresortIndex - 1]; 160 | } else { 161 | // Chose a random generator 162 | generator = random(generators); 163 | 164 | } 165 | 166 | randomSeed(theSeed); 167 | noiseSeed(theSeed); 168 | handleDrawing(); 169 | 170 | download.setAttribute("download", generator.name.split(' ').join('_') + ".svg"); 171 | 172 | download.addEventListener("mousedown", setSvgDownload); 173 | 174 | randomSeed(); 175 | noiseSeed(); 176 | 177 | } 178 | 179 | // Register a new cloud generator. 180 | function register(fn, name, creator) { 181 | generators.push({ 182 | fn: fn, 183 | name: name, 184 | creator: creator 185 | }); 186 | } 187 | --------------------------------------------------------------------------------