git.haldean.org e / master blit.nim
master

Tree @master (Download .tar.gz)

blit.nim @masterraw · history · blame

import unicode

type
  Style* = uint8
  Char* = (Rune, Style)

  Pt* = object
    x*, y*: int
  Size* = object
    w*, h*: int
  Rect* = object
    lo*, hi*: Pt

  RectBuf[T] = ref object
    c: seq[T]
    stride: int
  Blit* = RectBuf[Char]

const NO_CHAR*: Char = (Rune(' '), Style(0xFF))

## POINT MATH

proc `+`*(p0: Pt, p1: Pt): Pt =
  Pt(x: p0.x + p1.x, y: p0.y + p1.y)

proc `-`*(p0: Pt, p1: Pt): Pt =
  Pt(x: p0.x - p1.x, y: p0.y - p1.y)

proc `+=`*(p0: var Pt, p1: Pt) =
  p0.x += p1.x
  p0.y += p1.y

## RECTS

proc width*(r: Rect): int =
  if r.hi.x < r.lo.x:
    return 0
  return r.hi.x - r.lo.x + 1

proc height*(r: Rect): int =
  if r.hi.y < r.lo.y:
    return 0
  return r.hi.y - r.lo.y + 1

proc size*(r: Rect): Size =
  Size(w: r.width(), h: r.height())

proc isempty*(r: Rect): bool = r.width() <= 0 or r.height() <= 0

proc width*[T](b: RectBuf[T]): int = b.stride

proc height*[T](b: RectBuf[T]): int =
  int(b.c.len() / int(b.stride))

proc size*[T](b: RectBuf[T]): Size =
  Size(w: b.width(), h: b.height())

proc contains*(r: Rect, p: Pt): bool =
  r.lo.x <= p.x and p.x <= r.hi.x and
  r.lo.y <= p.y and p.y <= r.hi.y

proc contains*[T](b: RectBuf[T], p: Pt): bool =
  0 <= p.x and p.x < b.width() and
  0 <= p.y and p.y < b.height()

proc `+=`*(r0: var Rect, r1: Rect) =
  if r1.isempty():
    return
  if r0.isempty():
    r0.lo = r1.lo
    r0.hi = r1.hi
  else:
    r0.lo = Pt(x: min(r0.lo.x, r1.lo.x), y: min(r0.lo.y, r1.lo.y))
    r0.hi = Pt(x: max(r0.hi.x, r1.hi.x), y: max(r0.hi.y, r1.hi.y))

## BLIT INDEXING

proc index(b: RectBuf, p: Pt): int =
  p.y * b.stride + p.x

proc fill*[T](b: RectBuf[T], c: T): void =
  for i in 0..(b.c.len() - 1):
    b.c[i] = c

proc set*[T](b: RectBuf[T], p: Pt, c: T): void =
  b.c[b.index(p)] = c

proc get*[T](b: RectBuf[T], p: Pt): T =
  b.c[b.index(p)]

proc resize*[T](b: var RectBuf[T], size: Size) =
  let s = size.w * size.h
  b.c = newSeq[T](s)
  b.stride = size.w

## CONSTRUCTORS

proc newPt*(x, y: int): Pt =
  Pt(x: x, y: y)

proc emptyRect*(): Rect =
  Rect(lo: newPt(0, 0), hi: newPt(-1, -1))

proc rectFromSize*(s: Size, lo: Pt = Pt(x: 0, y: 0)): Rect =
  Rect(lo: lo, hi: Pt(x: lo.x + s.w - 1, y: lo.y + s.h - 1))

proc newRectBuf[T](size: Size): RectBuf[T] =
  var b = RectBuf[T]()
  b.resize(size)
  return b

proc newBlit*(size: Size): Blit =
  var b = newRectBuf[Char](size)
  b.fill(NO_CHAR)
  return b

## ITERATORS

iterator pointsInside*(rects: seq[Rect]): Pt =
  var domain = emptyRect()
  for r in rects:
    domain += r
  for y in domain.lo.y..domain.hi.y:
    for x in domain.lo.x..domain.hi.x:
      let p = newPt(x, y)
      for r in rects:
        if r.contains(p):
          yield p
          break