├── .gitignore ├── graphics.nimble ├── LICENSE.txt └── graphics.nim /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | -------------------------------------------------------------------------------- /graphics.nimble: -------------------------------------------------------------------------------- 1 | [Package] 2 | name = "graphics" 3 | version = "1.0.0" 4 | author = "Andreas Rumpf, Dominik Picheta" 5 | description = "Graphics module for Nim." 6 | license = "MIT" 7 | 8 | [Deps] 9 | Requires: "nim >= 0.10.0, sdl1 >= 1.0.0" 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /graphics.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Nim's Runtime Library 4 | # (c) Copyright 2012 Andreas Rumpf, Dominik Picheta 5 | # 6 | # See the file "LICENSE.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | 10 | ## This module implements graphical output for Nim; the current 11 | ## implementation uses SDL but the interface is meant to support multiple 12 | ## backends some day. There is no need to init SDL as this module does that 13 | ## implicitly. 14 | 15 | import colors, math 16 | from sdl import PSurface # Bug 17 | from sdl_ttf import openFont, closeFont 18 | 19 | type 20 | Rect* = tuple[x, y, width, height: int] 21 | Point* = tuple[x, y: int] 22 | 23 | PSurface* = ref Surface ## a surface to draw onto 24 | Surface* {.pure, final.} = object 25 | w*, h*: Natural 26 | s*: sdl.PSurface 27 | 28 | EGraphics* = object of IOError 29 | 30 | Font {.pure, final.} = object 31 | f: sdl_ttf.PFont 32 | color: sdl.Color 33 | PFont* = ref Font ## represents a font 34 | {.deprecated: [TSurface: Surface, TFont: Font, TRect: Rect, TPoint: Point].} 35 | 36 | template bytemask(x): untyped = byte(x and 0xff) 37 | 38 | proc toSdlColor*(c: Color): sdl.Color = 39 | ## Convert colors.Color to sdl.Color 40 | var x = c.extractRGB 41 | result.r = bytemask(x.r) 42 | result.g = bytemask(x.g) 43 | result.b = bytemask(x.b) 44 | 45 | proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 = 46 | ## Creates a color using ``sdl.MapRGBA``. 47 | var x = c.extractRGB 48 | return sdl.mapRGBA(sur.s.format, bytemask x.r, bytemask x.g, 49 | bytemask x.b, bytemask alpha) 50 | 51 | proc toSdlRect*(r: Rect): sdl.Rect = 52 | ## Convert ``graphics.Rect`` to ``sdl.Rect``. 53 | result.x = int16(r.x) 54 | result.y = int16(r.y) 55 | result.w = uint16(r.width) 56 | result.h = uint16(r.height) 57 | 58 | proc raiseEGraphics = 59 | raise newException(EGraphics, $sdl.getError()) 60 | 61 | proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s) 62 | 63 | proc newSurface*(width, height: int): PSurface = 64 | ## creates a new surface. 65 | new(result, surfaceFinalizer) 66 | result.w = width 67 | result.h = height 68 | result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height, 69 | 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0) 70 | if result.s == nil: 71 | raiseEGraphics() 72 | 73 | assert(not sdl.mustLock(result.s)) 74 | 75 | proc fontFinalizer(f: PFont) = closeFont(f.f) 76 | 77 | proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont = 78 | ## Creates a new font object. Raises ``EIO`` if the font cannot be loaded. 79 | new(result, fontFinalizer) 80 | result.f = openFont(name, size.cint) 81 | if result.f == nil: 82 | raise newException(IOError, "Could not open font file: " & name) 83 | result.color = toSdlColor(color) 84 | 85 | var 86 | defaultFont*: PFont ## default font that is used; this needs to initialized 87 | ## by the client! 88 | 89 | proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) = 90 | ## initializes the `defaultFont` var. 91 | defaultFont = newFont(name, size, color) 92 | 93 | proc newScreenSurface*(width, height: int): PSurface = 94 | ## Creates a new screen surface 95 | new(result, surfaceFinalizer) 96 | result.w = width 97 | result.h = height 98 | result.s = sdl.setVideoMode(width, height, 0, 0) 99 | if result.s == nil: 100 | raiseEGraphics() 101 | 102 | proc writeToBMP*(sur: PSurface, filename: string) = 103 | ## Saves the contents of the surface `sur` to the file `filename` as a 104 | ## BMP file. 105 | if sdl.saveBMP(sur.s, filename) != 0: 106 | raise newException(IOError, "cannot write: " & filename) 107 | 108 | type 109 | Pixels = ptr array[0..1000_000-1, int32] 110 | 111 | template setPix(video, pitch, x, y, col: untyped) = 112 | video[y * pitch + x] = int32(col) 113 | 114 | template getPix(video, pitch, x, y: untyped): untyped = 115 | colors.Color(video[y * pitch + x]) 116 | 117 | const 118 | ColSize = 4 119 | 120 | proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} = 121 | assert x <% sur.w 122 | assert y <% sur.h 123 | result = getPix(cast[Pixels](sur.s.pixels), sur.s.pitch.int div ColSize, 124 | x, y) 125 | 126 | proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} = 127 | assert x <% sur.w 128 | assert y <% sur.h 129 | var pixs = cast[Pixels](sur.s.pixels) 130 | #pixs[y * (sur.s.pitch div colSize) + x] = int(col) 131 | setPix(pixs, sur.s.pitch.int div ColSize, x, y, col) 132 | 133 | proc `[]`*(sur: PSurface, p: Point): Color = 134 | ## get pixel at position `p`. No range checking is done! 135 | result = getPixel(sur, p.x, p.y) 136 | 137 | proc `[]`*(sur: PSurface, x, y: int): Color = 138 | ## get pixel at position ``(x, y)``. No range checking is done! 139 | result = getPixel(sur, x, y) 140 | 141 | proc `[]=`*(sur: PSurface, p: Point, col: Color) = 142 | ## set the pixel at position `p`. No range checking is done! 143 | setPixel(sur, p.x, p.y, col) 144 | 145 | proc `[]=`*(sur: PSurface, x, y: int, col: Color) = 146 | ## set the pixel at position ``(x, y)``. No range checking is done! 147 | setPixel(sur, x, y, col) 148 | 149 | proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface, 150 | srcRect: Rect) = 151 | ## Copies ``srcSurf`` into ``destSurf`` 152 | var destTRect, srcTRect: sdl.Rect 153 | 154 | destTRect.x = int16(destRect.x) 155 | destTRect.y = int16(destRect.y) 156 | destTRect.w = uint16(destRect.width) 157 | destTRect.h = uint16(destRect.height) 158 | 159 | srcTRect.x = int16(srcRect.x) 160 | srcTRect.y = int16(srcRect.y) 161 | srcTRect.w = uint16(srcRect.width) 162 | srcTRect.h = uint16(srcRect.height) 163 | 164 | if sdl.blitSurface(srcSurf.s, addr(srcTRect), destSurf.s, addr(destTRect)) != 0: 165 | raiseEGraphics() 166 | 167 | proc textBounds*(text: string, font = defaultFont): tuple[width, height: int] = 168 | var w, h: cint 169 | if sdl_ttf.sizeUTF8(font.f, text, w, h) < 0: raiseEGraphics() 170 | result.width = int(w) 171 | result.height = int(h) 172 | 173 | proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) = 174 | ## Draws text with a transparent background, at location ``p`` with the given 175 | ## font. 176 | var textSur: PSurface # This surface will have the text drawn on it 177 | new(textSur, surfaceFinalizer) 178 | 179 | # Render the text 180 | textSur.s = sdl_ttf.renderUTF8Blended(font.f, text, font.color) 181 | # Merge the text surface with sur 182 | sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h)) 183 | 184 | proc drawText*(sur: PSurface, p: Point, text: string, 185 | bg: Color, font = defaultFont) = 186 | ## Draws text, at location ``p`` with font ``font``. ``bg`` 187 | ## is the background color. 188 | var textSur: PSurface # This surface will have the text drawn on it 189 | new(textSur, surfaceFinalizer) 190 | textSur.s = sdl_ttf.renderUTF8Shaded(font.f, text, font.color, toSdlColor(bg)) 191 | # Merge the text surface with sur 192 | sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h)) 193 | 194 | proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) = 195 | ## draws a circle with center `p` and radius `r` with the given color 196 | ## onto the surface `sur`. 197 | var video = cast[Pixels](sur.s.pixels) 198 | var pitch = sur.s.pitch.int div ColSize 199 | var a = 1 - r 200 | var py = r 201 | var px = 0 202 | var x = p.x 203 | var y = p.y 204 | while px <= py + 1: 205 | if x+px <% sur.w: 206 | if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color) 207 | if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color) 208 | 209 | if x-px <% sur.w: 210 | if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color) 211 | if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color) 212 | 213 | if x+py <% sur.w: 214 | if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color) 215 | if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color) 216 | 217 | if x-py <% sur.w: 218 | if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color) 219 | if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color) 220 | 221 | if a < 0: 222 | a = a + (2 * px + 3) 223 | else: 224 | a = a + (2 * (px - py) + 5) 225 | py = py - 1 226 | px = px + 1 227 | 228 | proc `>-<`(val: int, s: PSurface): int {.inline.} = 229 | return if val < 0: 0 elif val >= s.w: s.w-1 else: val 230 | 231 | proc `>|<`(val: int, s: PSurface): int {.inline.} = 232 | return if val < 0: 0 elif val >= s.h: s.h-1 else: val 233 | 234 | proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) = 235 | ## draws a line between the two points `p1` and `p2` with the given color 236 | ## onto the surface `sur`. 237 | var stepx, stepy: int = 0 238 | var x0 = p1.x >-< sur 239 | var x1 = p2.x >-< sur 240 | var y0 = p1.y >|< sur 241 | var y1 = p2.y >|< sur 242 | var dy = y1 - y0 243 | var dx = x1 - x0 244 | if dy < 0: 245 | dy = -dy 246 | stepy = -1 247 | else: 248 | stepy = 1 249 | if dx < 0: 250 | dx = -dx 251 | stepx = -1 252 | else: 253 | stepx = 1 254 | dy = dy * 2 255 | dx = dx * 2 256 | var video = cast[Pixels](sur.s.pixels) 257 | var pitch = sur.s.pitch.int div ColSize 258 | setPix(video, pitch, x0, y0, color) 259 | if dx > dy: 260 | var fraction = dy - (dx div 2) 261 | while x0 != x1: 262 | if fraction >= 0: 263 | y0 = y0 + stepy 264 | fraction = fraction - dx 265 | x0 = x0 + stepx 266 | fraction = fraction + dy 267 | setPix(video, pitch, x0, y0, color) 268 | else: 269 | var fraction = dx - (dy div 2) 270 | while y0 != y1: 271 | if fraction >= 0: 272 | x0 = x0 + stepx 273 | fraction = fraction - dy 274 | y0 = y0 + stepy 275 | fraction = fraction + dx 276 | setPix(video, pitch, x0, y0, color) 277 | 278 | proc drawHorLine*(sur: PSurface, x, y, w: Natural, color: Color) = 279 | ## draws a horizontal line from (x,y) to (x+w-1, y). 280 | var video = cast[Pixels](sur.s.pixels) 281 | var pitch = sur.s.pitch.int div ColSize 282 | 283 | if y >= 0 and y <= sur.s.h: 284 | for i in 0 .. min(sur.s.w-x, w)-1: 285 | setPix(video, pitch, x + i, y, color) 286 | 287 | proc drawVerLine*(sur: PSurface, x, y, h: Natural, color: Color) = 288 | ## draws a vertical line from (x,y) to (x, y+h-1). 289 | var video = cast[Pixels](sur.s.pixels) 290 | var pitch = sur.s.pitch.int div ColSize 291 | 292 | if x >= 0 and x <= sur.s.w: 293 | for i in 0 .. min(sur.s.h-y, h)-1: 294 | setPix(video, pitch, x, y + i, color) 295 | 296 | proc fillCircle*(s: PSurface, p: Point, r: Natural, color: Color) = 297 | ## draws a circle with center `p` and radius `r` with the given color 298 | ## onto the surface `sur` and fills it. 299 | var a = 1 - r 300 | var py: int = r 301 | var px = 0 302 | var x = p.x 303 | var y = p.y 304 | while px <= py: 305 | # Fill up the middle half of the circle 306 | drawVerLine(s, x + px, y, py + 1, color) 307 | drawVerLine(s, x + px, y - py, py, color) 308 | if px != 0: 309 | drawVerLine(s, x - px, y, py + 1, color) 310 | drawVerLine(s, x - px, y - py, py, color) 311 | if a < 0: 312 | a = a + (2 * px + 3) 313 | else: 314 | a = a + (2 * (px - py) + 5) 315 | py = py - 1 316 | # Fill up the left/right half of the circle 317 | if py >= px: 318 | drawVerLine(s, x + py + 1, y, px + 1, color) 319 | drawVerLine(s, x + py + 1, y - px, px, color) 320 | drawVerLine(s, x - py - 1, y, px + 1, color) 321 | drawVerLine(s, x - py - 1, y - px, px, color) 322 | px = px + 1 323 | 324 | proc drawRect*(sur: PSurface, r: Rect, color: Color) = 325 | ## draws a rectangle. 326 | var video = cast[Pixels](sur.s.pixels) 327 | var pitch = sur.s.pitch.int div ColSize 328 | if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h): 329 | var minW = min(sur.s.w - r.x, r.width) 330 | var minH = min(sur.s.h - r.y, r.height) 331 | 332 | # Draw Top 333 | for i in 0 .. minW - 1: 334 | setPix(video, pitch, r.x + i, r.y, color) 335 | setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom 336 | 337 | # Draw left side 338 | for i in 0 .. minH - 1: 339 | setPix(video, pitch, r.x, r.y + i, color) 340 | setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side 341 | 342 | proc fillRect*(sur: PSurface, r: Rect, col: Color) = 343 | ## Fills a rectangle using sdl's ``FillRect`` function. 344 | var rect = toSdlRect(r) 345 | if sdl.fillRect(sur.s, addr(rect), sur.createSdlColor(col)) == -1: 346 | raiseEGraphics() 347 | 348 | proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) = 349 | var video = cast[Pixels](sur.s.pixels) 350 | var pitch = sur.s.pitch.int div ColSize 351 | if cx+x <= sur.s.w-1: 352 | if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col) 353 | if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col) 354 | if cx-x <= sur.s.w-1: 355 | if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col) 356 | if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col) 357 | 358 | proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural, 359 | col: Color) = 360 | ## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the 361 | ## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height 362 | ## of the ellipse. 363 | var 364 | x, y: Natural 365 | xChange, yChange: int 366 | ellipseError: int 367 | twoASquare, twoBSquare: Natural 368 | stoppingX, stoppingY: Natural 369 | 370 | twoASquare = 2 * xRadius * xRadius 371 | twoBSquare = 2 * yRadius * yRadius 372 | x = xRadius 373 | y = 0 374 | xChange = yRadius * yRadius * (1 - 2 * xRadius) 375 | yChange = xRadius * xRadius 376 | ellipseError = 0 377 | stoppingX = twoBSquare * xRadius 378 | stoppingY = 0 379 | 380 | while stoppingX >= stoppingY: # 1st set of points, y` > - 1 381 | sur.plot4EllipsePoints(cx, cy, x, y, col) 382 | inc(y) 383 | inc(stoppingY, twoASquare) 384 | inc(ellipseError, yChange) 385 | inc(yChange, twoASquare) 386 | if (2 * ellipseError + xChange) > 0 : 387 | dec(x) 388 | dec(stoppingX, twoBSquare) 389 | inc(ellipseError, xChange) 390 | inc(xChange, twoBSquare) 391 | 392 | # 1st point set is done; start the 2nd set of points 393 | x = 0 394 | y = yRadius 395 | xChange = yRadius * yRadius 396 | yChange = xRadius * xRadius * (1 - 2 * yRadius) 397 | ellipseError = 0 398 | stoppingX = 0 399 | stoppingY = twoASquare * yRadius 400 | while stoppingX <= stoppingY: 401 | sur.plot4EllipsePoints(cx, cy, x, y, col) 402 | inc(x) 403 | inc(stoppingX, twoBSquare) 404 | inc(ellipseError, xChange) 405 | inc(xChange,twoBSquare) 406 | if (2 * ellipseError + yChange) > 0: 407 | dec(y) 408 | dec(stoppingY, twoASquare) 409 | inc(ellipseError, yChange) 410 | inc(yChange,twoASquare) 411 | 412 | 413 | proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) = 414 | if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h): 415 | var video = cast[Pixels](sur.s.pixels) 416 | var pitch = sur.s.pitch.int div ColSize 417 | 418 | var pixColor = getPix(video, pitch, x, y) 419 | 420 | setPix(video, pitch, x, y, 421 | pixColor.intensity(1.0 - c) + color.intensity(c)) 422 | 423 | 424 | template ipart(x: untyped): untyped = floor(x) 425 | template cround(x: untyped): untyped = ipart(x + 0.5) 426 | template fpart(x: untyped): untyped = x - ipart(x) 427 | template rfpart(x: untyped): untyped = 1.0 - fpart(x) 428 | 429 | proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) = 430 | ## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's 431 | ## line algorithm 432 | var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(), 433 | p1.y.toFloat(), p2.y.toFloat()) 434 | var dx = x2 - x1 435 | var dy = y2 - y1 436 | 437 | var ax = dx 438 | if ax < 0'f64: 439 | ax = 0'f64 - ax 440 | var ay = dy 441 | if ay < 0'f64: 442 | ay = 0'f64 - ay 443 | 444 | if ax < ay: 445 | swap(x1, y1) 446 | swap(x2, y2) 447 | swap(dx, dy) 448 | 449 | template doPlot(x, y: int, c: float, color: Color) = 450 | if ax < ay: 451 | sur.plotAA(y, x, c, color) 452 | else: 453 | sur.plotAA(x, y, c, color) 454 | 455 | if x2 < x1: 456 | swap(x1, x2) 457 | swap(y1, y2) 458 | 459 | var gradient = dy / dx 460 | # handle first endpoint 461 | var xend = cround(x1) 462 | var yend = y1 + gradient * (xend - x1) 463 | var xgap = rfpart(x1 + 0.5) 464 | var xpxl1 = int(xend) # this will be used in the main loop 465 | var ypxl1 = int(ipart(yend)) 466 | doPlot(xpxl1, ypxl1, rfpart(yend)*xgap, color) 467 | doPlot(xpxl1, ypxl1 + 1, fpart(yend)*xgap, color) 468 | var intery = yend + gradient # first y-intersection for the main loop 469 | 470 | # handle second endpoint 471 | xend = cround(x2) 472 | yend = y2 + gradient * (xend - x2) 473 | xgap = fpart(x2 + 0.5) 474 | var xpxl2 = int(xend) # this will be used in the main loop 475 | var ypxl2 = int(ipart(yend)) 476 | doPlot(xpxl2, ypxl2, rfpart(yend) * xgap, color) 477 | doPlot(xpxl2, ypxl2 + 1, fpart(yend) * xgap, color) 478 | 479 | # main loop 480 | var x = xpxl1 + 1 481 | while x <= xpxl2-1: 482 | doPlot(x, int(ipart(intery)), rfpart(intery), color) 483 | doPlot(x, int(ipart(intery)) + 1, fpart(intery), color) 484 | intery = intery + gradient 485 | inc(x) 486 | 487 | proc fillSurface*(sur: PSurface, color: Color) = 488 | ## Fills the entire surface with ``color``. 489 | if sdl.fillRect(sur.s, nil, sur.createSdlColor(color)) == -1: 490 | raiseEGraphics() 491 | 492 | template withEvents*(surf: PSurface, event: untyped, actions: untyped) = 493 | ## Simple template which creates an event loop. ``Event`` is the name of the 494 | ## variable containing the Event object. 495 | while true: 496 | var event: sdl.Event 497 | if sdl.waitEvent(addr(event)) == 1: 498 | actions 499 | 500 | if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics() 501 | if sdl_ttf.init() < 0: raiseEGraphics() 502 | 503 | when not defined(testing) and isMainModule: 504 | var surf = newScreenSurface(800, 600) 505 | 506 | surf.fillSurface(colWhite) 507 | 508 | # Draw the shapes 509 | surf.drawLineAA((150, 170), (400, 471), colTan) 510 | surf.drawLine((100, 170), (400, 471), colRed) 511 | 512 | surf.drawEllipse(200, 300, 200, 30, colSeaGreen) 513 | surf.drawHorLine(1, 300, 400, colViolet) 514 | # Check if the ellipse is the size it's suppose to be. 515 | surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is 516 | 517 | surf.drawEllipse(400, 300, 300, 300, colOrange) 518 | surf.drawEllipse(5, 5, 5, 5, colGreen) 519 | 520 | surf.drawHorLine(5, 5, 900, colRed) 521 | surf.drawVerLine(5, 60, 800, colRed) 522 | surf.drawCircle((600, 500), 60, colRed) 523 | 524 | surf.fillRect((50, 50, 100, 100), colFuchsia) 525 | surf.fillRect((150, 50, 100, 100), colGreen) 526 | surf.drawRect((50, 150, 100, 100), colGreen) 527 | surf.drawRect((150, 150, 100, 100), colAqua) 528 | surf.drawRect((250, 150, 100, 100), colBlue) 529 | surf.drawHorLine(250, 150, 100, colRed) 530 | 531 | surf.drawLineAA((592, 160), (592, 280), colPurple) 532 | 533 | #surf.drawText((300, 300), "TEST", colMidnightBlue) 534 | #var textSize = textBounds("TEST") 535 | #surf.drawText((300, 300 + textSize.height), $textSize.width & ", " & 536 | # $textSize.height, colDarkGreen) 537 | 538 | var mouseStartX = -1 539 | var mouseStartY = -1 540 | withEvents(surf, event): 541 | var eventp = addr(event) 542 | case event.kind: 543 | of sdl.QUITEV: 544 | break 545 | of sdl.KEYDOWN: 546 | var evk = sdl.evKeyboard(eventp) 547 | if evk.keysym.sym == sdl.K_LEFT: 548 | surf.drawHorLine(395, 300, 50, colBlack) 549 | echo("Drawing") 550 | elif evk.keysym.sym == sdl.K_ESCAPE: 551 | break 552 | else: 553 | echo(evk.keysym.sym) 554 | of sdl.MOUSEBUTTONDOWN: 555 | var mbd = sdl.evMouseButton(eventp) 556 | if mouseStartX == -1 or mouseStartY == -1: 557 | mouseStartX = int(mbd.x) 558 | mouseStartY = int(mbd.y) 559 | else: 560 | surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple) 561 | mouseStartX = -1 562 | mouseStartY = -1 563 | 564 | of sdl.MOUSEMOTION: 565 | var mm = sdl.evMouseMotion(eventp) 566 | if mouseStartX != -1 and mouseStartY != -1: 567 | surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple) 568 | #echo(mm.x, " ", mm.y, " ", mm.yrel) 569 | 570 | else: 571 | discard "echo(event.kind)" 572 | 573 | sdl.updateRect(surf.s, 0, 0, 800, 600) 574 | 575 | surf.writeToBMP("test.bmp") 576 | sdl.quit() 577 | --------------------------------------------------------------------------------