├── LICENSE.md ├── README.md ├── draft.lua ├── example_linker.lua └── main.lua /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | draft uses the MIT open source license. 4 | 5 | ## MIT License (MIT) 6 | 7 | Copyright 2012, Pierre-Emmanuel Lévesque 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # draft 2 | 3 | ## About 4 | 5 | draft is a simple drafting module for [LÖVE 2D](https://love2d.org/). 6 | It makes it easy to draft primitive shapes, and some more luxurious ones. 7 | 8 | ## Loading the module 9 | 10 | To load the module, use the following code. 11 | 12 | ```lua 13 | -- modify the path depending where draft resides 14 | local Draft = require('draft') 15 | local draft = Draft(modeOption) 16 | ``` 17 | 18 | Setting _mode_ is optional. The default is _fill_. 19 | 20 | The drafting functions can then be called inside the `love.draw()` function. 21 | 22 | ## Usage example 23 | 24 | ```lua 25 | local Draft = require('draft') 26 | local draft = Draft() 27 | 28 | function love.draw() 29 | draft:rectangle(300, 100, 50, 30) 30 | draft:rectangle(500, 100, 50, 30) 31 | draft:rhombus(400, 200, 65, 65) 32 | draft:bow(390, 280, 100, 2.5, 0, 10, 'line') 33 | end 34 | ``` 35 | 36 | ## Notes 37 | 38 | - The _mode_ can be set when initializing the module, but it can also be 39 | overwritten with each drafting function. If you want to overwrite it for many 40 | functions, you can use the `draft:getMode` and `draft:setMode` functions. 41 | 42 | - You can also set the _mode_ to _false_ if you only want to get the vertices, 43 | and not draw the shape. For example, using the following code would get the 44 | vertices for a rectangle without drawing it. 45 | 46 | `local vertices = draft:rectangle(100, 100, 50, 80, false)` 47 | 48 | - Look at the file _main.lua_ for examples on how the functions can be called. 49 | This file draws all the possible shapes. 50 | 51 | - The `draft:compass` function is a powerful function used to draw curves. 52 | Notably, it accepts a function for the scale parameter which permits the 53 | drawing of complex shapes. You can look at the code for `draft:star` and 54 | `draft:egg` for examples of usage. You can also look at the `draft:compass` 55 | function too see how it is called. 56 | 57 | - The linkers create line between points. Try them, they are quite powerful! 58 | 59 | ## Draft function list 60 | 61 | ### Initialization 62 | draft(mode) 63 | 64 | ### Getters and Setters 65 | draft:getMode() 66 | draft:setMode(mode) 67 | 68 | ### Shapes 69 | 70 | #### Primary Shapes (using core drawing functions) 71 | 72 | draft:line(points, mode) 73 | draft:triangleIsosceles(cx, cy, width, height, mode) 74 | draft:triangleRight(cx, cy, width, height, mode) 75 | draft:rectangle(cx, cy, width, height, mode) 76 | draft:polygon(vertices, mode) 77 | 78 | #### Secondary Shapes (using primary shape functions) 79 | 80 | draft:triangleEquilateral(cx, cy, width, mode) 81 | draft:square(cx, cy, length, mode) 82 | draft:trapezoid(cx, cy, width, height, widthTop, widthTopOffset, mode) 83 | draft:rhombus(cx, cy, width, height, mode) 84 | draft:trapezium(cx, cy, widthLeft, widthRight, height, depth, mode) 85 | draft:gem(cx, cy, widthTop, widthMiddle, height, depth, mode) 86 | draft:diamond(cx, cy, width, mode) 87 | 88 | #### Tertiary Shapes (using secondary shape functions) 89 | 90 | draft:rhombusEquilateral(cx, cy, length, mode) 91 | draft:lozenge(cx, cy, width, mode) 92 | draft:kite(cx, cy, width, height, depth, mode) 93 | draft:trapezoidIsosceles(cx, cy, width, height, widthTop, mode) 94 | draft:parallelogram(cx, cy, width, height, widthOffset, mode) 95 | 96 | #### Curved Shapes 97 | 98 | draft:compass(cx, cy, width, arcAngle, startAngle, numSegments, wrap, scale, mode) 99 | draft:circle(cx, cy, radius, numSegments, mode) 100 | draft:arc(cx, cy, radius, arcAngle, startAngle, numSegments, mode) 101 | draft:bow(cx, cy, radius, arcAngle, startAngle, numSegments, mode) 102 | draft:pie(cx, cy, radius, arcAngle, startAngle, numSegments, mode) 103 | draft:ellipse(cx, cy, width, height, numSegments, mode) 104 | draft:ellipticArc(cx, cy, width, height, arcAngle, startAngle, numSegments, mode) 105 | draft:ellipticBow(cx, cy, width, height, arcAngle, startAngle, numSegments, mode) 106 | draft:ellipticPie(cx, cy, width, height, arcAngle, startAngle, numSegments, mode) 107 | draft:semicircle(cx, cy, width, startAngle, numSegments, mode) 108 | draft:dome(cx, cy, width, startAngle, numSegments, mode) 109 | 110 | #### Complex Shapes 111 | 112 | draft:star(cx, cy, width, widthInner, numPoints, startAngle, mode) 113 | draft:egg(cx, cy, width, syBottom, syTop, numSegments, mode) 114 | 115 | ### Linkers 116 | draft:linkLadder(v1, v2, mode) 117 | draft:linkTangle(v1, v2, mode) 118 | draft:linkWeb(v, mode) 119 | draft:linkTangleWebs(v1, v2, mode) 120 | 121 | ## example_linker.lua (shows off linking) 122 | 123 | ```lua 124 | -- load draft 125 | local Draft = require('draft') 126 | local draft = Draft() 127 | 128 | function love.load() 129 | limitUpper = 100 130 | limitLower = 4 131 | numSegments = limitLower 132 | direction = 'up' 133 | step = 0.01 134 | end 135 | 136 | function love.update(dt) 137 | if numSegments > limitUpper and direction == 'up' then 138 | direction = 'down' 139 | elseif numSegments < limitLower and direction == 'down' then 140 | direction = 'up' 141 | elseif direction == 'up' then 142 | numSegments = numSegments + step 143 | else 144 | numSegments = numSegments - step 145 | end 146 | end 147 | 148 | function love.draw() 149 | local v = draft:egg(400, 300, 1500, 1, 1, numSegments, 'line') 150 | draft:linkWeb(v) 151 | end 152 | ``` 153 | -------------------------------------------------------------------------------- /draft.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Title: draft 3 | Use: drafting module for the LÖVE 2D game engine 4 | Author: Pierre-Emmanuel Lévesque 5 | Email: pierre.e.levesque@gmail.com 6 | Created: July 26th, 2012 7 | Copyright: Copyright 2012, Pierre-Emmanuel Lévesque 8 | License: MIT license - @see LICENSE.md 9 | --]] 10 | 11 | ---------------------------------------------------- 12 | -- Module setup 13 | ---------------------------------------------------- 14 | 15 | local draft = {} 16 | draft.__index = draft 17 | 18 | --[[ 19 | New (constructor) 20 | 21 | @param string drawMode (fill, line, false) [def: fill] 22 | @return table metatable 23 | --]] 24 | local function new(mode) 25 | if mode == nil then mode = 'fill' end 26 | return setmetatable({ 27 | mode = mode 28 | }, draft) 29 | end 30 | 31 | ---------------------------------------------------- 32 | -- Getters and setters 33 | ---------------------------------------------------- 34 | 35 | function draft:getMode() return self.mode end 36 | function draft:setMode(mode) self.mode = mode end 37 | 38 | ---------------------------------------------------- 39 | -- Utilities 40 | ---------------------------------------------------- 41 | 42 | --[[ 43 | Appends vertices to another set of vertices 44 | 45 | @param table vertices {x1, y1, x2, y2, ...} 46 | @param table new vertices {x1, y1, x2, y2, ...} 47 | @return table merged vertices {x1, y1, x2, y2, ...} 48 | --]] 49 | local function appendVertices(vertices, newVertices) 50 | for _,v in ipairs(newVertices) do 51 | table.insert(vertices, v) 52 | end 53 | return vertices 54 | end 55 | 56 | ---------------------------------------------------- 57 | -- Primary shapes (using core drawing functions) 58 | ---------------------------------------------------- 59 | 60 | --[[ 61 | Draws lines between points 62 | 63 | @param table points {x1, y1, x2, y2, ...} 64 | @param mixed drawMode (fill, line, false) [def: self.mode] 65 | @return table points {x1, y1, x2, y2, ...} 66 | --]] 67 | function draft:line(points, mode) 68 | if mode == nil then mode = self.mode end 69 | if mode then 70 | love.graphics.line(points) 71 | end 72 | return points 73 | end 74 | 75 | --[[ 76 | Draws an isosceles triangle 77 | 78 | @param number center x 79 | @param number center y 80 | @param number width of the base 81 | @param number height 82 | @param mixed drawMode (fill, line, false) [def: self.mode] 83 | @return table vertices {x1, y2, x2, y2, x3, y3} 84 | --]] 85 | function draft:triangleIsosceles(cx, cy, width, height, mode) 86 | if mode == nil then mode = self.mode end 87 | local widthRadius = width / 2 88 | local heightRadius = height / 2 89 | local x1 = cx 90 | local y1 = cy - heightRadius 91 | local x2 = cx + widthRadius 92 | local y2 = cy + heightRadius 93 | local x3 = cx - widthRadius 94 | local y3 = y2 95 | if mode then 96 | love.graphics.polygon(mode, x1, y1, x2, y2, x3, y3) 97 | end 98 | return {x1, y1, x2, y2, x3, y3} 99 | end 100 | 101 | --[[ 102 | Draws a right triangle 103 | 104 | @param number center x 105 | @param number center y 106 | @param number width of the base (a minus width flips the triangle) 107 | @param number height 108 | @param mixed drawMode (fill, line, false) [def: self.mode] 109 | @return table vertices {x1, y2, x2, y2, x3, y3} 110 | --]] 111 | function draft:triangleRight(cx, cy, width, height, mode) 112 | if mode == nil then mode = self.mode end 113 | local widthRadius = width / 2 114 | local heightRadius = height / 2 115 | local x1 = cx - widthRadius 116 | local y1 = cy - heightRadius 117 | local x2 = cx + widthRadius 118 | local y2 = cy + heightRadius 119 | local x3 = x1 120 | local y3 = y2 121 | if mode then 122 | love.graphics.polygon(mode, x1, y1, x2, y2, x3, y3) 123 | end 124 | return {x1, y1, x2, y2, x3, y3} 125 | end 126 | 127 | --[[ 128 | Draws a rectangle 129 | 130 | @param number center x 131 | @param number center y 132 | @param number width 133 | @param number height 134 | @param mixed drawMode (fill, line, false) [def: self.mode] 135 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 136 | --]] 137 | function draft:rectangle(cx, cy, width, height, mode) 138 | if mode == nil then mode = self.mode end 139 | local widthRadius = width / 2 140 | local heightRadius = height / 2 141 | local left = cx - widthRadius 142 | local right = cx + widthRadius 143 | local top = cy - heightRadius 144 | local bottom = cy + heightRadius 145 | local vertices = {left, top, right, top, right, bottom, left, bottom} 146 | if mode then 147 | love.graphics.rectangle(mode, vertices[1], vertices[2], width, height) 148 | end 149 | return vertices 150 | end 151 | 152 | --[[ 153 | Draws a polygon 154 | 155 | @param table vertices {x1, y1, x2, y2, ...} 156 | @param mixed drawMode (fill, line, false) [def: self.mode] 157 | @return table vertices {x1, y1, x2, y2, ...} 158 | --]] 159 | function draft:polygon(vertices, mode) 160 | if mode == nil then mode = self.mode end 161 | if mode then 162 | if (mode == 'fill' and not love.math.isConvex(vertices)) then 163 | local triangles = love.math.triangulate(vertices) 164 | for _, v in pairs(triangles) do 165 | love.graphics.polygon('fill', v) 166 | end 167 | else 168 | love.graphics.polygon(mode, vertices) 169 | end 170 | end 171 | return vertices 172 | end 173 | 174 | ---------------------------------------------------- 175 | -- Secondary shapes (using primary shape functions) 176 | ---------------------------------------------------- 177 | 178 | --[[ 179 | Draws an equilateral triangle 180 | 181 | @param number center x 182 | @param number center y 183 | @param number width of the base 184 | @param mixed drawMode (fill, line, false) [def: self.mode] 185 | @return table vertices {x1, y2, x2, y2, x3, y3} 186 | @uses self:triangleIsosceles() 187 | --]] 188 | function draft:triangleEquilateral(cx, cy, width, mode) 189 | local height = math.sqrt(math.pow(width, 2) - math.pow(width / 2, 2)) 190 | return self:triangleIsosceles(cx, cy, width, height, mode) 191 | end 192 | 193 | --[[ 194 | Draws a square 195 | 196 | @param number center x 197 | @param number center y 198 | @param number length of one side (width or height) 199 | @param mixed drawMode (fill, line, false) [def: self.mode] 200 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 201 | @uses self:rectangle() 202 | --]] 203 | function draft:square(cx, cy, length, mode) 204 | return self:rectangle(cx, cy, length, length, mode) 205 | end 206 | 207 | --[[ 208 | Draws a trapezoid 209 | 210 | @param number center x 211 | @param number center y 212 | @param number width (bottom) 213 | @param number height 214 | @param number top width 215 | @param number top width offset (to the left) 216 | @param mixed drawMode (fill, line, false) [def: self.mode] 217 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 218 | @uses self:polygon() 219 | --]] 220 | function draft:trapezoid(cx, cy, width, height, widthTop, widthTopOffset, mode) 221 | local widthRadius = width / 2 222 | local heightRadius = height / 2 223 | local widthTopRadiusOffsetted = widthTop / 2 + widthTopOffset 224 | local vertices = { 225 | cx - widthTopRadiusOffsetted, cy - heightRadius, 226 | cx + widthTopRadiusOffsetted, cy - heightRadius, 227 | cx + widthRadius, cy + heightRadius, 228 | cx - widthRadius, cy + heightRadius 229 | } 230 | return self:polygon(vertices, mode) 231 | end 232 | 233 | --[[ 234 | Draws a rhombus 235 | 236 | @param number center x 237 | @param number center y 238 | @param number width 239 | @param number height 240 | @param mixed drawMode (fill, line, false) [def: self.mode] 241 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 242 | @uses self:polygon() 243 | --]] 244 | function draft:rhombus(cx, cy, width, height, mode) 245 | local widthRadius = width / 2 246 | local heightRadius = height / 2 247 | local vertices = { 248 | cx - widthRadius, cy, 249 | cx, cy - heightRadius, 250 | cx + widthRadius, cy, 251 | cx, cy + heightRadius 252 | } 253 | return self:polygon(vertices, mode) 254 | end 255 | 256 | --[[ 257 | Draws a trapezium (no parallel sides) 258 | 259 | @param number center x 260 | @param number center y 261 | @param number width left 262 | @param number width right 263 | @param number height 264 | @param number depth 265 | @param mixed drawMode (fill, line, false) [def: self.mode] 266 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 267 | @uses self:polygon() 268 | --]] 269 | function draft:trapezium(cx, cy, widthLeft, widthRight, height, depth, mode) 270 | local vertices = { 271 | cx - widthLeft, cy, 272 | cx, cy - height, 273 | cx + widthRight, cy, 274 | cx, cy + depth 275 | } 276 | return self:polygon(vertices, mode) 277 | end 278 | 279 | --[[ 280 | Draws a gem 281 | 282 | @param number center x 283 | @param number center y 284 | @param number top width 285 | @param number middle width 286 | @param number height 287 | @param number depth 288 | @param mixed drawMode (fill, line, false) [def: self.mode] 289 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4, x5, y5} 290 | @uses self:polygon() 291 | --]] 292 | function draft:gem(cx, cy, widthTop, widthMiddle, height, depth, mode) 293 | local widthTopRadius = widthTop / 2 294 | local widthMiddleRadius = widthMiddle / 2 295 | local vertices = { 296 | cx - widthTopRadius, cy - height, 297 | cx + widthTopRadius, cy - height, 298 | cx + widthMiddleRadius, cy, 299 | cx, cy + depth, 300 | cx - widthMiddleRadius, cy 301 | } 302 | return self:polygon(vertices, mode) 303 | end 304 | 305 | --[[ 306 | Draws a diamond 307 | 308 | @param number center x 309 | @param number center y 310 | @param number width 311 | @param mixed drawMode (fill, line, false) [def: self.mode] 312 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4, x5, y5} 313 | @uses self:polygon() 314 | --]] 315 | function draft:diamond(cx, cy, width, mode) 316 | local widthRadius = width / 2 317 | local depth = math.sqrt(math.pow(width, 2) - math.pow(widthRadius, 2)) / 2 318 | local height = depth / 2 319 | local topOffset = widthRadius / 3 * 2 320 | local vertices = { 321 | cx - widthRadius, cy, 322 | cx - topOffset, cy - height, 323 | cx + topOffset, cy - height, 324 | cx + widthRadius, cy, 325 | cx, cy + depth 326 | } 327 | return self:polygon(vertices, mode) 328 | end 329 | 330 | ---------------------------------------------------- 331 | -- Tertiary shapes (using secondary shape functions) 332 | ---------------------------------------------------- 333 | 334 | --[[ 335 | Draws an equiangular rhombus (square rotated 45˚) 336 | 337 | @param number center x 338 | @param number center y 339 | @param number length of one side (width or height) 340 | @param mixed drawMode (fill, line, false) [def: self.mode] 341 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 342 | @uses self:rhombus() 343 | --]] 344 | function draft:rhombusEquilateral(cx, cy, length, mode) 345 | return self:rhombus(cx, cy, length, length, mode) 346 | end 347 | 348 | --[[ 349 | Draws a lozenge (rhombus with 45 degree angles) 350 | 351 | @param number center x 352 | @param number center y 353 | @param number width 354 | @param mixed drawMode (fill, line, false) [def: self.mode] 355 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 356 | @uses self:rhombus() 357 | 358 | --]] 359 | function draft:lozenge(cx, cy, width, mode) 360 | return self:rhombus(cx, cy, width, width * 2, mode) 361 | end 362 | 363 | --[[ 364 | Draws a kite 365 | 366 | @param number center x 367 | @param number center y 368 | @param number width 369 | @param number height 370 | @param number depth 371 | @param mixed drawMode (fill, line, false) [def: self.mode] 372 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 373 | @uses self:trapezium() 374 | 375 | --]] 376 | function draft:kite(cx, cy, width, height, depth, mode) 377 | return self:trapezium(cx, cy, width, width, height, depth, mode) 378 | end 379 | 380 | --[[ 381 | Draws an isosceles trapezoid 382 | 383 | @param number center x 384 | @param number center y 385 | @param number width (bottom) 386 | @param number height 387 | @param number top width 388 | @param mixed drawMode (fill, line, false) [def: self.mode] 389 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 390 | @uses self:trapezoid() 391 | --]] 392 | function draft:trapezoidIsosceles(cx, cy, width, height, widthTop, mode) 393 | return self:trapezoid(cx, cy, width, height, widthTop, 0, mode) 394 | end 395 | 396 | --[[ 397 | Draws a parallelogram 398 | 399 | @param number center x 400 | @param number center y 401 | @param number width 402 | @param number height 403 | @param number width offset (to the left) 404 | @param mixed drawMode (fill, line, false) [def: self.mode] 405 | @return table vertices {x1, y2, x2, y2, x3, y3, x4, y4} 406 | @uses self:trapezoid() 407 | --]] 408 | function draft:parallelogram(cx, cy, width, height, widthOffset, mode) 409 | return self:trapezoid(cx, cy, width, height, width, widthOffset, mode) 410 | end 411 | 412 | ---------------------------------------------------- 413 | -- Curved shapes 414 | ---------------------------------------------------- 415 | 416 | --[[ 417 | Draws all kinds of curved shapes 418 | 419 | Based on code from: http://slabode.exofire.net/circle_draw.shtml 420 | 421 | @param number center x 422 | @param number center y 423 | @param number width 424 | @param number arc angle 425 | @param number start angle [def: 0] 426 | @param number number of segments [def: math.floor(10 * math.sqrt(radius))] 427 | @param bool wrap the arc upon itself [def: false] 428 | @param mixed scale (table {x, y} or function (x, y, segmentNum, numSegments)) [def: nil] 429 | @param mixed drawMode (fill, line, false) [def: self.mode] 430 | @return table vertices {x1, y1, x2, y2, ...} 431 | @uses self:polygon(), self:line() 432 | --]] 433 | function draft:compass( 434 | cx, cy, width, arcAngle, startAngle, numSegments, wrap, scale, mode 435 | ) 436 | if mode == nil then mode = self.mode end 437 | local radius = width / 2 438 | startAngle = startAngle or 0 439 | numSegments = numSegments or math.floor(10 * math.sqrt(radius)) 440 | if wrap == true then wrap = 0 else wrap = -1 end 441 | local theta = arcAngle / (numSegments - wrap) 442 | local cosine = math.cos(theta) 443 | local sine = math.sin(theta) 444 | local x = radius * math.cos(startAngle) 445 | local y = radius * math.sin(startAngle) 446 | local vertices = {} 447 | for i = 1, numSegments, 1 do 448 | local vx, vy 449 | if type(scale) == 'function' then 450 | vx, vy = scale(x,y,i,numSegments) 451 | elseif type(scale) == 'table' then 452 | vx = x * scale[1] 453 | vy = y * scale[2] 454 | else 455 | vx = x 456 | vy = y 457 | end 458 | table.insert(vertices, vx + cx) 459 | table.insert(vertices, vy + cy) 460 | local t = x 461 | x = (cosine * x) - (sine * y) 462 | y = (sine * t) + (cosine * y) 463 | end 464 | if mode then 465 | if wrap == 0 then 466 | self:polygon(vertices, mode) 467 | else 468 | self:line(vertices, mode) 469 | end 470 | end 471 | return vertices 472 | end 473 | 474 | --[[ 475 | Draws a circle (regular polygon) 476 | 477 | @param number center x 478 | @param number center y 479 | @param number radius 480 | @param number number of segments [def: 10 * math.sqrt(radius)] 481 | @param mixed drawMode (fill, line, false) [def: self.mode] 482 | @return table vertices {x1, y1, x2, y2, ...} 483 | @uses self:compass() 484 | --]] 485 | function draft:circle(cx, cy, radius, numSegments, mode) 486 | return self:compass( 487 | cx, cy, radius, 2 * math.pi, 0, numSegments, true, nil, mode 488 | ) 489 | end 490 | 491 | --[[ 492 | Draws an arc 493 | 494 | @param number center x 495 | @param number center y 496 | @param number radius 497 | @param number arc angle 498 | @param number start angle [def: 0] 499 | @param number number of segments [def: 10 * math.sqrt(radius)] 500 | @param mixed drawMode (fill, line, false) [def: self.mode] 501 | @return table vertices {x1, y1, x2, y2, ...} 502 | @uses self:compass() 503 | --]] 504 | function draft:arc(cx, cy, radius, arcAngle, startAngle, numSegments, mode) 505 | return self:compass( 506 | cx, cy, radius, arcAngle, startAngle, numSegments, false, nil, mode 507 | ) 508 | end 509 | 510 | --[[ 511 | Draws a bow (closed arc) 512 | 513 | @param number center x 514 | @param number center y 515 | @param number radius 516 | @param number arc angle 517 | @param number start angle [def: 0] 518 | @param number number of segments [def: 10 * math.sqrt(radius)] 519 | @param mixed drawMode (fill, line, false) [def: self.mode] 520 | @return table vertices {x1, y1, x2, y2, ...} 521 | @uses self:compass() 522 | --]] 523 | function draft:bow(cx, cy, radius, arcAngle, startAngle, numSegments, mode) 524 | return self:compass( 525 | cx, cy, radius, arcAngle, startAngle, numSegments, true, nil, mode 526 | ) 527 | end 528 | 529 | --[[ 530 | Draws a pie 531 | 532 | @param number center x 533 | @param number center y 534 | @param number radius 535 | @param number arc angle 536 | @param number start angle [def: 0] 537 | @param number number of segments [def: 10 * math.sqrt(radius)] 538 | @param mixed drawMode (fill, line, false) [def: self.mode] 539 | @return table vertices {x1, y1, x2, y2, ...} 540 | @uses self:compass(), appendVertices(), self:polygon() 541 | --]] 542 | function draft:pie(cx, cy, radius, arcAngle, startAngle, numSegments, mode) 543 | local vertices = self:compass( 544 | cx, cy, radius, arcAngle, startAngle, numSegments, false, nil, false 545 | ) 546 | vertices = appendVertices(vertices, {cx, cy}) 547 | return self:polygon(vertices, mode) 548 | end 549 | 550 | --[[ 551 | Draws an ellipse 552 | 553 | @param number center x 554 | @param number center y 555 | @param number width 556 | @param number height 557 | @param number number of segments [def: 10 * math.sqrt(radius)] 558 | @param mixed drawMode (fill, line, false) [def: self.mode] 559 | @return table vertices {x1, y1, x2, y2, ...} 560 | @uses self:compass() 561 | --]] 562 | function draft:ellipse(cx, cy, width, height, numSegments, mode) 563 | return self:compass( 564 | cx, cy, width, 2 * math.pi, 0, numSegments, true, {1, height / width}, mode 565 | ) 566 | end 567 | 568 | --[[ 569 | Draws an elliptic arc 570 | 571 | @param number center x 572 | @param number center y 573 | @param number width 574 | @param number height 575 | @param number arc angle 576 | @param number start angle [def: 0] 577 | @param number number of segments [def: 10 * math.sqrt(radius)] 578 | @param mixed drawMode (fill, line, false) [def: self.mode] 579 | @return table vertices {x1, y1, x2, y2, ...} 580 | @uses self:compass() 581 | --]] 582 | function draft:ellipticArc(cx, cy, width, height, arcAngle, startAngle, numSegments, mode) 583 | return self:compass( 584 | cx, cy, width, arcAngle, startAngle, numSegments, false, {1, height / width}, mode 585 | ) 586 | end 587 | 588 | --[[ 589 | Draws an elliptic bow (closed elliptic arc) 590 | 591 | @param number center x 592 | @param number center y 593 | @param number width 594 | @param number height 595 | @param number arc angle 596 | @param number start angle [def: 0] 597 | @param number number of segments [def: 10 * math.sqrt(radius)] 598 | @param mixed drawMode (fill, line, false) [def: self.mode] 599 | @return table vertices {x1, y1, x2, y2, ...} 600 | @uses self:compass() 601 | --]] 602 | function draft:ellipticBow(cx, cy, width, height, arcAngle, startAngle, numSegments, mode) 603 | return self:compass( 604 | cx, cy, width, arcAngle, startAngle, numSegments, true, {1, height / width}, mode 605 | ) 606 | end 607 | 608 | --[[ 609 | Draws an elliptic pie 610 | 611 | @param number center x 612 | @param number center y 613 | @param number width 614 | @param number height 615 | @param number arc angle 616 | @param number start angle [def: 0] 617 | @param number number of segments [def: 10 * math.sqrt(radius)] 618 | @param mixed drawMode (fill, line, false) [def: self.mode] 619 | @return table vertices {x1, y1, x2, y2, ...} 620 | @uses self:compass(), appendVertices(), self:polygon() 621 | --]] 622 | function draft:ellipticPie(cx, cy, width, height, arcAngle, startAngle, numSegments, mode) 623 | local vertices = self:compass( 624 | cx, cy, width, arcAngle, startAngle, numSegments, false, {1, height / width}, false 625 | ) 626 | vertices = appendVertices(vertices, {cx, cy}) 627 | return self:polygon(vertices, mode) 628 | end 629 | 630 | --[[ 631 | Draws a semicircle 632 | 633 | @param number center x 634 | @param number center y 635 | @param number width 636 | @param number start angle 637 | @param number number of segments [def: 10 * math.sqrt(radius)] 638 | @param mixed drawMode (fill, line, false) [def: self.mode] 639 | @return table vertices {x1, y1, x2, y2, ...} 640 | @uses self:compass() 641 | --]] 642 | function draft:semicircle(cx, cy, width, startAngle, numSegments, mode) 643 | return self:compass( 644 | cx, cy, width / 2, math.rad(180), startAngle, numSegments, false, nil, mode 645 | ) 646 | end 647 | 648 | --[[ 649 | Draws a dome (closed semicircle) 650 | 651 | @param number center x 652 | @param number center y 653 | @param number width 654 | @param number start angle 655 | @param number number of segments [def: 10 * math.sqrt(radius)] 656 | @param mixed drawMode (fill, line, false) [def: self.mode] 657 | @return table vertices {x1, y1, x2, y2, ...} 658 | @uses self:compass() 659 | --]] 660 | function draft:dome(cx, cy, width, startAngle, numSegments, mode) 661 | return self:compass( 662 | cx, cy, width / 2, math.rad(180), startAngle, numSegments, true, nil, mode 663 | ) 664 | end 665 | 666 | ---------------------------------------------------- 667 | -- Complex shapes 668 | ---------------------------------------------------- 669 | 670 | --[[ 671 | Draws a star 672 | 673 | @param number center x 674 | @param number center y 675 | @param number width 676 | @param number inner width 677 | @param number number of points [def: 5] 678 | @param number start angle [def: 0] 679 | @param mixed drawMode (fill, line, false) [def: self.mode] 680 | @return table vertices {x1, y1, x2, y2, ...} 681 | @uses self:compass() 682 | --]] 683 | function draft:star(cx, cy, width, widthInner, numPoints, startAngle, mode) 684 | widthInner = widthInner or width / 2 685 | numPoints = numPoints or 5 686 | local scale = function(cx, cy, segmentNum) 687 | if segmentNum % 2 == 0 then 688 | cx = cx * (widthInner / width) 689 | cy = cy * (widthInner / width) 690 | end 691 | return cx, cy 692 | end 693 | return self:compass( 694 | cx, cy, width, 2 * math.pi, startAngle, numPoints * 2, true, scale, mode 695 | ) 696 | end 697 | 698 | --[[ 699 | Draws an egg 700 | 701 | @param number center x 702 | @param number center y 703 | @param number width 704 | @param number scale y bottom [def: 0.8] 705 | @param number scale y top [def: 2] 706 | @param number number of segments [def: math.floor(10 * math.sqrt(radius))] 707 | @param mixed drawMode (fill, line, false) [def: self.mode] 708 | @return table vertices {x1, y1, x2, y2, ...} 709 | @uses self:compass() 710 | --]] 711 | function draft:egg(cx, cy, width, syBottom, syTop, numSegments, mode) 712 | syBottom = syBottom or 0.8 713 | syTop = syTop or 2 714 | local scale = function(cx, cy, segmentNum, numSegments) 715 | if segmentNum <= numSegments / 2 then 716 | cy = cy * syBottom 717 | else 718 | cy = cy * syTop 719 | end 720 | return cx, cy 721 | end 722 | return self:compass(cx, cy, width, 2 * math.pi, 0, numSegments, true, scale, mode) 723 | end 724 | 725 | ---------------------------------------------------- 726 | -- Linkers (draw lines between points) 727 | ---------------------------------------------------- 728 | 729 | --[[ 730 | Draws lines between two sets of vertices in ladder style 731 | 732 | Ladder style means point A->A, B->B, C->C, etc... 733 | 734 | Note: The sets of vertices must be the same size. 735 | 736 | @param table vertices 1 737 | @param table vertices 2 738 | @param mixed drawMode (fill, line, false) [def: self.mode] 739 | @return table lines {{x1, y1, x2, y2}, {x1, x2, y1, y2}, …} 740 | @uses self:line() 741 | --]] 742 | function draft:linkLadder(v1, v2, mode) 743 | local lines = {} 744 | for i=1, #v1, 2 do 745 | table.insert(lines, self:line({v1[i], v1[i+1], v2[i], v2[i+1]}, mode)) 746 | end 747 | return lines 748 | end 749 | 750 | --[[ 751 | Draws lines between two sets of vertices in tangle style 752 | 753 | Tangle style means each point of one set of vertices 754 | to all the points of the other set of vertices. 755 | 756 | @param table vertices 1 757 | @param table vertices 2 758 | @param mixed drawMode (fill, line, false) [def: self.mode] 759 | @return table lines {{x1, y1, x2, y2}, {x1, x2, y1, y2}, …} 760 | @uses self:line() 761 | --]] 762 | function draft:linkTangle(v1, v2, mode) 763 | local lines = {} 764 | for i=1, #v1, 2 do 765 | for j=1, #v2, 2 do 766 | table.insert(lines, self:line({v1[i], v1[i+1], v2[j], v2[j+1]}, mode)) 767 | end 768 | end 769 | return lines 770 | end 771 | 772 | --[[ 773 | Draws lines between vertices in web style 774 | 775 | Web style means each point to all other points. 776 | 777 | @param table vertices 778 | @param mixed drawMode (fill, line, false) [def: self.mode] 779 | @return table lines {{x1, y1, x2, y2}, {x1, x2, y1, y2}, …} 780 | @uses self:line() 781 | --]] 782 | function draft:linkWeb(v, mode) 783 | local lines = {} 784 | local limit = #v - 2 785 | for i=1, #v-4, 2 do 786 | if i == 3 then limit = limit + 2 end 787 | for j=i+4, limit, 2 do 788 | table.insert(lines, self:line({v[i], v[i+1], v[j], v[j+1]}, mode)) 789 | end 790 | end 791 | return lines 792 | end 793 | 794 | --[[ 795 | Draws a linkTangle and linkWeb between two sets of vertices 796 | 797 | @param table vertices 1 798 | @param table vertices 2 799 | @param mixed drawMode (fill, line, false) [def: self.mode] 800 | @return table lines {{x1, y1, x2, y2}, {x1, x2, y1, y2}, …} 801 | @uses self:linkTangle(), self:linkWeb() 802 | --]] 803 | function draft:linkTangleWebs(v1, v2, mode) 804 | local lines = {} 805 | table.insert(lines, self:linkTangle(v1, v2, mode)) 806 | table.insert(lines, self:linkWeb(v1, mode)) 807 | table.insert(lines, self:linkWeb(v2, mode)) 808 | return lines 809 | end 810 | 811 | ---------------------------------------------------- 812 | -- Module 813 | ---------------------------------------------------- 814 | 815 | return setmetatable({new = new}, 816 | {__call = function(_, ...) return new(...) end}) 817 | -------------------------------------------------------------------------------- /example_linker.lua: -------------------------------------------------------------------------------- 1 | -- load draft 2 | local Draft = require('draft') 3 | local draft = Draft() 4 | 5 | function love.load() 6 | limitUpper = 100 7 | limitLower = 4 8 | numSegments = limitLower 9 | direction = 'up' 10 | step = 0.01 11 | end 12 | 13 | function love.update(dt) 14 | if numSegments > limitUpper and direction == 'up' then 15 | direction = 'down' 16 | elseif numSegments < limitLower and direction == 'down' then 17 | direction = 'up' 18 | elseif direction == 'up' then 19 | numSegments = numSegments + step 20 | else 21 | numSegments = numSegments - step 22 | end 23 | end 24 | 25 | function love.draw() 26 | local v = draft:egg(400, 300, 1500, 1, 1, numSegments, 'line') 27 | draft:linkWeb(v) 28 | end 29 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Unittest 3 | 4 | This program acts as a unittest by testing all 5 | of draft's shapes and linkers. 6 | --]] 7 | 8 | -- load draft 9 | local Draft = require('draft') 10 | local draft = Draft() 11 | 12 | function love.draw() 13 | 14 | -- getters and setters 15 | love.graphics.setColor{255, 255, 255} 16 | local y = 100 17 | draft:setMode('fill') 18 | love.graphics.print('The current mode is ' .. draft:getMode() .. '.', 10, y) 19 | draft:setMode('line') 20 | love.graphics.print('The new mode is ' .. draft:getMode() .. '.', 10, y + 20) 21 | 22 | -- primary shapes 23 | love.graphics.setColor{255, 0, 0} 24 | draft:line({10, 10, 45, 45, 75, 20}) 25 | draft:triangleIsosceles(90, 50, 60, 70) 26 | draft:triangleIsosceles(90, 50, 40, 35, 'fill') 27 | draft:triangleIsosceles(200, 200, 200, 200, false) 28 | draft:triangleRight(150, 30, 50, 40) 29 | draft:triangleRight(150, 30, 30, 25, 'fill') 30 | draft:triangleRight(200, 200, 200, 200, false) 31 | draft:rectangle(210, 25, 50, 30) 32 | draft:rectangle(210, 25, 40, 20, 'fill') 33 | draft:rectangle(200, 200, 200, 200, false) 34 | draft:polygon({250, 10, 250, 90, 290, 75, 275, 30}) 35 | draft:polygon({260, 30, 260, 70, 280, 65, 270, 40}, 'fill') 36 | draft:polygon({10, 30, 200, 500, 600, 300, 270, 40}, false) 37 | 38 | -- secondary shapes 39 | love.graphics.setColor{0, 255, 0} 40 | draft:triangleEquilateral(340, 50, 80) 41 | draft:triangleEquilateral(340, 50, 50, 'fill') 42 | draft:triangleEquilateral(200, 200, 200, false) 43 | draft:square(420, 35, 50) 44 | draft:square(420, 35, 30, 'fill') 45 | draft:square(200, 200, 200, false) 46 | draft:trapezoid(500, 40, 60, 30, 30, 20) 47 | draft:trapezoid(500, 40, 50, 20, 20, 20, 'fill') 48 | draft:trapezoid(200, 200, 200, 100, 60, 40, false) 49 | draft:rhombus(570, 50, 40, 60) 50 | draft:rhombus(570, 50, 35, 50, 'fill') 51 | draft:rhombus(200, 200, 200, 150, false) 52 | draft:trapezium(625, 50, 30, 20, 40, 20) 53 | draft:trapezium(625, 50, 25, 15, 35, 15, 'fill') 54 | draft:trapezium(200, 200, 100, 70, 80, 20, false) 55 | draft:gem(675, 40, 20, 30, 20, 35) 56 | draft:gem(675, 40, 15, 25, 15, 30, 'fill') 57 | draft:gem(200, 200, 120, 60, 100, 200, false) 58 | draft:diamond(730, 30, 50) 59 | draft:diamond(730, 30, 40, 'fill') 60 | draft:diamond(200, 200, 200, false) 61 | 62 | -- tertiary shapes 63 | love.graphics.setColor{0, 0, 255} 64 | draft:rhombusEquilateral(50, 200, 80) 65 | draft:rhombusEquilateral(50, 200, 60, 'fill') 66 | draft:rhombusEquilateral(200, 200, 200, false) 67 | draft:lozenge(125, 200, 50) 68 | draft:lozenge(125, 200,30, 'fill') 69 | draft:lozenge(200, 200, 200, false) 70 | draft:kite(200, 200, 40, 80, 30) 71 | draft:kite(200, 200, 30, 75, 25, 'fill') 72 | draft:kite(200, 200, 200, 200, 100, false) 73 | draft:trapezoidIsosceles(295, 165, 80, 100, 40) 74 | draft:trapezoidIsosceles(295, 165, 70, 90, 30, 'fill') 75 | draft:trapezoidIsosceles(200, 200, 200, 200, 150, false) 76 | draft:parallelogram(375, 145, 40, 50, 20) 77 | draft:parallelogram(375, 145, 35, 35, 15, 'fill') 78 | draft:parallelogram(200, 200, 100, 125, 75, false) 79 | 80 | -- curved shapes 81 | love.graphics.setColor{127, 127, 127} 82 | draft:compass(450, 85, 60, math.rad(135), 0, 10, false, nil) 83 | draft:circle(40, 280, 50) 84 | draft:circle(40, 280, 40, nil, 'fill') 85 | draft:circle(200, 200, 200, nil, false) 86 | draft:arc(435, 130, 60, 2) 87 | draft:arc(200, 200, 100, 3, 0, 10, false) 88 | draft:bow(90, 265, 80, 2) 89 | draft:bow(90, 265, 70, 2, 0, nil, 'fill') 90 | draft:bow(200, 200, 200, 2, 0, nil, false) 91 | draft:pie(155, 265, 100, 2) 92 | draft:pie(155, 265, 80, 2, 0, nil, 'fill') 93 | draft:pie(300, 300, 300, 2, 0, nil, false) 94 | draft:ellipse(530, 125, 100, 60) 95 | draft:ellipse(530, 125, 90, 50, 10, 'fill') 96 | draft:ellipse(200, 200, 200, 200, nil, false) 97 | draft:ellipticArc(645, 115, 90, 50, 3) 98 | draft:ellipticArc(200, 200, 90, 50, 3, 0, nil, false) 99 | draft:ellipticBow(735, 70, 90, 80, 2.4) 100 | draft:ellipticBow(735, 70, 90, 70, 2.4, 0, nil, 'fill') 101 | draft:ellipticBow(250, 250, 250, 70, 2.4, 0, nil, false) 102 | draft:ellipticPie(275, 275, 125, 100, 2.9, 1) 103 | draft:ellipticPie(275, 275, 67, 45, 2.9, 1, nil, 'fill') 104 | draft:ellipticPie(300, 300, 300, 45, 2.9, 1, nil, false) 105 | draft:semicircle(325, 255, 80, 1) 106 | draft:semicircle(400, 400, 400, 1, nil, false) 107 | draft:dome(380, 250, 150, 2.3) 108 | draft:dome(380, 250, 125, 2.3, 5, 'fill') 109 | draft:dome(400, 400, 400, 2.3, 5, false) 110 | 111 | -- complex shapes 112 | love.graphics.setColor{127, 0, 127} 113 | draft:star(455, 220, 80, 25, 4, 2) 114 | draft:star(455, 220, 55, 10, 4, 2, 'fill') 115 | draft:star(500, 500, 80, 14, 6, nil, false) 116 | draft:egg(540, 220, 50) 117 | draft:egg(540, 220, 30, nil, nil, nil, 'fill') 118 | draft:egg(500, 500, 30, nil, nil, nil, false) 119 | 120 | -- linkers 121 | love.graphics.setColor{255, 127, 0} 122 | local v1 123 | local v2 124 | 125 | v1 = draft:line({ 126 | 580, 160, 127 | 580, 180, 128 | 580, 200, 129 | 580, 220, 130 | 580, 240, 131 | 580, 260, 132 | 580, 280 133 | }) 134 | v2 = draft:line({ 135 | 580, 280, 136 | 600, 280, 137 | 620, 280, 138 | 640, 280, 139 | 660, 280, 140 | 680, 280, 141 | 700, 280 142 | }) 143 | draft:linkLadder(v1, v2) 144 | 145 | v1 = draft:square(640, 200, 60) 146 | v2 = draft:square(680, 190, 60) 147 | draft:linkTangle(v1, v2) 148 | 149 | v1 = draft:diamond(750, 200, 50) 150 | draft:linkWeb(v1) 151 | 152 | v1 = draft:square(350, 340, 40) 153 | v2 = draft:line({400, 330, 420, 310, 440, 270, 420, 240}) 154 | draft:linkTangleWebs(v1, v2) 155 | 156 | end 157 | --------------------------------------------------------------------------------