git.s-ol.nu alive / wip alv-lib / love.moon
wip

Tree @wip (Download .tar.gz)

love.moon @wipraw · history · blame

import Constant, Op, PureOp, Input, Error, T, Array, any from require 'alv.base'

unpack or= table.unpack

vec2 = Array 2, T.num
vec3 = Array 3, T.num
vec4 = Array 4, T.num

class DrawId
  new: =>

draw = Constant.meta
  meta:
    name: 'draw'
    summary: "draw one or more love/shape shapes."
    examples: { '(love/draw shape1 …)', '(love/draw shapes)' }

  value: class extends Op
    new: (...) =>
      super ...
      @state or= DrawId!

    pattern = any['love/shape']*0
    setup: (inputs, scope) =>
      if #inputs == 1
        only = inputs[1]
        type = only\type!
        if type.__class == Array and type.type == T['love/shape']
          super Input.hot only
          return

      shapes = pattern\match inputs
      inputs = [Input.hot shape for shape in *shapes]
      inputs.num = Input.cold Constant.num #shapes
      super inputs

    tick: =>
      shapes = @unwrap_all!
      num = shapes.num or #shapes
      for i=1, num do shapes[i] or= ->
      COPILOT.drawlist[@state] = shapes

    destroy: =>
      COPILOT.drawlist[@state] = nil

no_shape = Constant.meta
  meta:
    name: 'no-shape'
    summary: "invisible null shape."

  value: T['love/shape']\mk_const ->

circle = Constant.meta
  meta:
    name: 'circle'
    summary: "create a circle shape."
    examples: { '(love/circle mode radius [segments])' }

  value: class extends PureOp
    pattern: any.str + any.num + -any.num
    type: T['love/shape']

    tick: =>
      { mode, radius, segments } = @unwrap_all!

      @out\set ->
        love.graphics.circle mode, 0, 0, radius, segments

ellipse = Constant.meta
  meta:
    name: 'ellipse'
    summary: "create a ellipse shape."
    examples: { '(love/ellipse mode size [segments])', '(love/ellipse mode rx ry [segments])' }

  value: class extends PureOp
    pattern: any.str + (any(vec2) / (any.num + any.num)) + -any.num
    type: T['love/shape']

    tick: =>
      { mode, size, segments } = @unwrap_all!
      { rx, ry } = size

      @out\set ->
        love.graphics.ellipse mode, 0, 0, rx, ry, segments

rectangle = Constant.meta
  meta:
    name: 'rectangle'
    summary: "create a rectangle shape."
    examples: { '(love/rectangle mode size)', '(love/rectangle mode w h)' }

  value: class extends PureOp
    pattern: any.str + (any(vec2) / (any.num + any.num))
    type: T['love/shape']

    tick: =>
      { mode, size } = @unwrap_all!
      { w, h } = size
      x, y = -w/2, -h/2

      @out\set ->
        love.graphics.rectangle mode, x, y, w, h

text = Constant.meta
  meta:
    name: 'text'
    summary: "create a text shape."
    examples: { '(love/text str [align] [font])' }
    description: "
Create a shape that draws the text `str` with font `font` (or the default font).
`align` should be on of the following strings:
- `center` (the default)
- `left`
- `right`"


  value: class extends PureOp
    pattern: any.str + -any.str + -any['love/font']
    type: T['love/shape']

    tick: =>
      { text, align, font } = @unwrap_all!

      wm = switch align or 'center'
        when 'left' then 0
        when 'center' then -0.5
        when 'right' then -1
        else
          error Error 'argument', "unknown text alignment '#{align}'"

      @out\set ->
        font or= love.graphics.getFont!
        width = font\getWidth text
        height = font\getHeight!
        love.graphics.print text, font, width*wm, -height/2

color = Constant.meta
  meta:
    name: 'color'
    summary: "set color of a shape."
    examples: { '(love/color color shape)', '(love/color r g b [a] shape)' }

  value: class extends PureOp
    pattern: (any(vec3) / any(vec4) / any.num\rep(3, 4)) + any['love/shape']
    type: T['love/shape']

    tick: =>
      { color, shape } = @unwrap_all!
      { r, g, b, a } = color

      @out\set ->
        love.graphics.setColor r, g, b, a
        shape!
        love.graphics.setColor 1, 1, 1

line_width = Constant.meta
  meta:
    name: 'line-width'
    summary: "set line-width of a shape."
    examples: { '(love/line-width width shape)' }

  value: class extends PureOp
    pattern: any.num + any['love/shape']
    type: T['love/shape']

    tick: =>
      { width, shape } = @unwrap_all!

      @out\set ->
        love.graphics.setLineWidth width
        shape!
        love.graphics.setLineWidth 1

translate = Constant.meta
  meta:
    name: 'translate'
    summary: "translate a shape."
    examples: { '(love/translate [delta] shape)', '(love/translate x y shape)' }

  value: class extends PureOp
    pattern: (any(vec2) / (any.num + any.num)) + any['love/shape']
    type: T['love/shape']

    tick: =>
      { pos, shape } = @unwrap_all!
      { x, y } = pos

      @out\set ->
        love.graphics.push!
        love.graphics.translate x, y
        shape!
        love.graphics.pop!

rotate = Constant.meta
  meta:
    name: 'rotate'
    summary: "rotate a shape."
    examples: { '(love/rotate angle shape)' }

  value: class extends PureOp
    pattern: any.num + any['love/shape']
    type: T['love/shape']

    tick: =>
      { angle, shape } = @unwrap_all!

      @out\set ->
        love.graphics.push!
        love.graphics.rotate angle
        shape!
        love.graphics.pop!

scale = Constant.meta
  meta:
    name: 'scale'
    summary: "scale a shape."
    examples: { '(love/scale scale shape)', '(love/scale sx [sy] shape)' }

  value: class extends PureOp
    pattern: (any(vec2) / (any.num + -any.num)) + any['love/shape']
    type: T['love/shape']

    tick: =>
      { pos, shape } = @unwrap_all!
      { sx, sy } = pos

      @out\set ->
        love.graphics.push!
        love.graphics.scale sx, sy
        shape!
        love.graphics.pop!

shear = Constant.meta
  meta:
    name: 'shear'
    summary: "shear a shape."
    examples: { '(love/shear x y shape)' }

  value: class extends PureOp
    pattern: (any(vec2) / (any.num + any.num)) + any['love/shape']
    type: T['love/shape']

    tick: =>
      { pos, shape } = @unwrap_all!
      { sx, sy } = pos

      @out\set ->
        love.graphics.push!
        love.graphics.shear sx, sy
        shape!
        love.graphics.pop!

mouse_pos = Constant.meta
  meta:
    name: 'mouse-pos'
    summary: "outputs current mouse position."
    examples: { '(love/mouse-pos)' }

  value: class extends Op
    new: (...) =>
      super ...
      @out or= vec2\mk_evt!
      @state = {}

    setup: =>
      super io: Input.hot T.bang\mk_evt!

    poll: =>
      x, y = love.mouse.getPosition!
      if x != @state.x or y != @state.y
        @state.x, @state.y = x, y
        if not @inputs.io\dirty!
          @inputs.io.result\set true
          true

    tick: =>
      @out\set { @state.x, @state.y }

mouse_pos = Constant.meta
  meta:
    name: 'mouse-pos'
    summary: "vec2 ~-stream of the current mouse position."

  value: COPILOT.mouse_pos

mouse_delta = Constant.meta
  meta:
    name: 'mouse-delta'
    summary: "vec2 !-stream of mouse movements."

  value: COPILOT.mouse_delta

wheel_delta = Constant.meta
  meta:
    name: 'wheel-delta'
    summary: "vec2 !-stream of mouse wheel movements."

  value: COPILOT.wheel_delta

key_presses = Constant.meta
  meta:
    name: 'key-presses'
    summary: "str !-stream of key presses."

  value: COPILOT.key_presses

key_releases = Constant.meta
  meta:
    name: 'key-releases'
    summary: "str !-stream of key releases."

  value: COPILOT.key_releases

Constant.meta
  meta:
    name: 'love'
    summary: "LÖVE graphics."
    description: "
This module implements basic graphics using the [love2d game engine][love].

#### running

In order to use this module, the copilot has to be started in a specific way:

    $ love bin/alv-love <session.alv>

#### usage

The [love/draw][] ops can be used to draw one or more `love/shape`s in a fixed
stacking order. `love/shape`s can be created using [love/rectangle][] etc, and
positioned and styled using the modifier ops like [love/translate][],
[love/color][] and so on. All modifier ops take the shape as the last input and
output a modified shape, and can be used comfortably with the thread-last
macro [->>][]:

    (import* love math)
    (draw (->>
      (rectangle 'fill' 100 100)
      (color 1 0 0)
      (rotate (/ pi 4))
      (translate 150 150)))


[love]: https://love2d.org"

  value:
    :draw

    'no-shape': no_shape
    :circle, :ellipse, :line, :rectangle, :text

    :translate, :rotate, :scale, :shear
    :color, 'line-width': line_width

    'mouse-pos': mouse_pos
    'mouse-delta': mouse_delta
    'wheel-data': wheel_delta
    'key-presses': key_presses
    'key-releases': key_releases