~xigoi/vier

6ff700db3bf13dbad1040a9fd681e1afe8df7756 — Adam Blažek 8 months ago 66407f6
Selection features
3 files changed, 91 insertions(+), 19 deletions(-)

M src/vier.nim
M website/index.html
M website/index.xd
M src/vier.nim => src/vier.nim +60 -4
@@ 81,6 81,7 @@ type
    command: string
    mode: Mode
    tool: Tool
    clipboard: seq[PixelChange]

const
  backgroundColor0 = color"#555555"


@@ 298,6 299,7 @@ proc injectColor(picture: Picture, color: Color) =
      )
  picture.add(change)
  picture.apply(change)
  picture.selection.setLen(0)

proc addColor(picture: Picture, color: Color) =
  ## Adds the given color to all selected pixels.


@@ 317,10 319,11 @@ proc addColor(picture: Picture, color: Color) =
      )
  picture.add(change)
  picture.apply(change)
  picture.selection.setLen(0)

proc clickPixel(picture: Picture, target: Vec, mode: Mode, tool: Tool, color: Color) =
  ## Reacts to clicking the image at the given point or pressing spacebar with the cursor on it.
  picture.selection = @[]
  picture.selection.setLen(0)
  picture.anchor = target
  case mode
  of mInject:


@@ 510,6 513,36 @@ proc swapColors(app: App) =
  swap(app.selectedPaletteIndex, app.secondaryColorPos.palette)
  app.selectedPalette.cursor = cursor

proc copy(app: App) =
  let selection = app.selectedPicture.selection
  let xMin = selection.mapIt(it.x).min()
  let yMin = selection.mapIt(it.y).min()
  app.clipboard =
    collect:
      for pixel in selection:
        PixelChange(pos: pixel - (xMin, yMin), newColor: app.selectedPicture[pixel])
  app.selectedPicture.selection.setLen(0)

proc paste(app: App) =
  let picture = app.selectedPicture
  let cursor = picture.cursor
  let
    change =
      ImageChange(
        pixelChanges:
          collect(
            for pixel in app.clipboard:
              if cursor + pixel.pos < picture.image.size:
                PixelChange(
                  pos: cursor + pixel.pos,
                  originalColor: picture[pixel.pos],
                  newColor: pixel.newColor,
                )
          )
      )
  picture.add(change)
  picture.apply(change)

proc ensureNonEmptyPictures(app: App) =
  ## Ensures that the app has at least one picture.
  ## Should be called whenever there is a possibility that the app has no pictures.


@@ 594,8 627,13 @@ proc processKeyboard(app: App) =
      while (var ch = getCharPressed(); ch != 0):
        app.command.add(Rune(ch))
  else:
    if isKeyPressed(Escape):
      app.selectedPicture.selection.setLen(0)
    if isKeyPressed(A):
      app.mode = mAdd
      if app.selectedPicture.selection.len == 0:
        app.mode = mAdd
      else:
        app.selectedPicture.addColor(app.color)
    if isKeyPressed(B):
      app.tool = Tool(kind: tBrush)
    if isKeyPressed(C):


@@ 619,7 657,12 @@ proc processKeyboard(app: App) =
    if isKeyPressed(F):
      app.tool = Tool(kind: tFlood)
    if isKeyPressed(I):
      app.mode = mInject
      if app.selectedPicture.selection.len == 0:
        app.mode = mInject
      else:
        app.selectedPicture.injectColor(app.color)
    if isKeyPressed(P):
      app.paste()
    if isKeyPressed(Q):
      if shift or app.pictures.all(isSaved):
        closeWindow()


@@ 645,7 688,10 @@ proc processKeyboard(app: App) =
    if isKeyPressed(X):
      app.swapColors()
    if isKeyPressed(Y):
      app.color = app.selectedPicture.colorAtCursor
      if app.selectedPicture.selection.len == 0:
        app.color = app.selectedPicture.colorAtCursor
      else:
        app.copy()
    if isKeyPressed(Semicolon):
      app.mode = mCommand
      app.command = ""


@@ 812,6 858,16 @@ proc addApi(spry: var spryvm.Interpreter, app: App) =
  nimFunc("new"):
    app.pictures.add(newPicture())
    app.selectedPictureIndex = app.pictures.high
  nimFunc("resize"):
    let widthNode = spry.evalArg()
    let heightNode = spry.evalArg()
    if widthNode of IntVal and heightNode of IntVal:
      let width = widthNode.IntVal.value.int32
      let height = heightNode.IntVal.value.int32
      app.selectedPicture.image.imageResizeCanvas(width, height, 0, 0, Blank)
      app.selectedPicture.loadTexture()
    else:
      raiseRuntimeException("Dimensions must be integers")
  nimFunc("resize-nn"):
    let widthNode = spry.evalArg()
    let heightNode = spry.evalArg()

M website/index.html => website/index.html +13 -8
@@ 31,17 31,16 @@ nimble <span class="token function">install</span>
<section><h2 class="xd-section-heading">Keyboard mappings</h2><p>The key mappings are inspired by Vim. They are currently not customizable.</p>
<table><tr><th>Key</th><th>Alone</th><th>Shift</th><th>Control</th></tr>
<tr><td><kbd>⎵</kbd></td><td>hold to draw</td><td>hold to draw with secondary color</td></tr>
<tr><td><kbd>A</kbd></td><td>Mode ← Add / add to selection</td></tr>
<tr><td><kbd>A</kbd></td><td>Mode ← Add</td></tr>
<tr><td><kbd>B</kbd></td><td>Tool ← Brush</td></tr>
<tr><td><kbd>C</kbd></td><td>Mode ← Color</td></tr>
<tr><td><kbd>D</kbd></td><td>delete pixel / delete selection</td></tr>
<tr><td><kbd>D</kbd></td><td>delete pixel</td></tr>
<tr><td><kbd>F</kbd></td><td>Tool ← Flood</td></tr>
<tr><td><kbd>H</kbd></td><td>move left</td><td></td><td>previous picture/palette</td></tr>
<tr><td><kbd>I</kbd></td><td>Mode ← Inject / fill selection</td></tr>
<tr><td><kbd>I</kbd></td><td>Mode ← Inject</td></tr>
<tr><td><kbd>J</kbd></td><td>move down</td><td></td></tr>
<tr><td><kbd>K</kbd></td><td>move up</td><td></td></tr>
<tr><td><kbd>L</kbd></td><td>move right</td><td></td><td>next picture/palette</td></tr>
<tr><td><kbd>M</kbd></td><td>Tool ← Mirror / cut selection</td></tr>
<tr><td><kbd>P</kbd></td><td>paste</td></tr>
<tr><td><kbd>Q</kbd></td><td>quit</td><td>force quit</td></tr>
<tr><td><kbd>R</kbd></td><td>Tool ← Rectangle</td><td></td><td>redo</td></tr>


@@ 50,14 49,20 @@ nimble <span class="token function">install</span>
<tr><td><kbd>V</kbd></td><td>Mode ← Select</td></tr>
<tr><td><kbd>W</kbd></td><td>write picture</td><td>write all pictures</td></tr>
<tr><td><kbd>X</kbd></td><td>swap colors</td></tr>
<tr><td><kbd>Y</kbd></td><td>pick color / copy selection</td><td>pick secondary color</td></tr>
<tr><td><kbd>Y</kbd></td><td>pick color</td><td>pick secondary color</td></tr>
<tr><td><kbd>-</kbd></td><td>zoom out</td><td>zoom out all pictures</td></tr>
<tr><td><kbd>=</kbd></td><td>zoom in</td><td>zoom in all pictures</td></tr>
<tr><td><kbd>;</kbd></td><td>Mode ← Command</td></tr></table>
Note that the mouse can also be used to draw and select colors. The right mouse button draws with the secondary color.</section>
<section><h2 class="xd-section-heading">Commands</h2><dl><dt><code>e image.png</code></dt><dd>open a file for editing (the filename is a symbol)</dd><dt><code>edit &quot;image.png&quot;</code></dt><dd>open a file for editing (the filename is a string)</dd><dt><code>flip-x</code></dt><dd>flip the current picture horizontally</dd><dt><code>flip-y</code></dt><dd>flip the current picture vertically</dd><dt><code>new</code></dt><dd>create a new picture with a transparent background and the default size</dd><dt><code>resize-nn 16 8</code></dt><dd>resize the current picture using the nearest-neighbor algorithm</dd><dt><code>w image.png</code></dt><dd>save the current picture under the specified filename (the filename is a symbol)</dd><dt><code>write &quot;image.png&quot;</code></dt><dd>save the current picture under the specified filename (the filename is a string)</dd></dl>
Note that the mouse can also be used to draw and select colors. The right mouse button draws with the secondary color.
<section><h3 class="xd-section-heading">Selection keyboard mappings</h3><p>Some keys work differently when used with an active selection.</p>
<table><tr><th>Key</th><th>Alone</th><th>Shift</th><th>Control</th></tr>
<tr><td><kbd>Esc</kbd></td><td>cancel selection</td></tr>
<tr><td><kbd>A</kbd></td><td>add selected color on selection</td></tr>
<tr><td><kbd>I</kbd></td><td>fill selection with selected color</td></tr>
<tr><td><kbd>Y</kbd></td><td>copy selection</td></tr></table></section></section>
<section><h2 class="xd-section-heading">Commands</h2><dl><dt><code>e image.png</code></dt><dd>open a file for editing (the filename is a symbol)</dd><dt><code>edit &quot;image.png&quot;</code></dt><dd>open a file for editing (the filename is a string)</dd><dt><code>flip-x</code></dt><dd>flip the current picture horizontally</dd><dt><code>flip-y</code></dt><dd>flip the current picture vertically</dd><dt><code>new</code></dt><dd>create a new picture with a transparent background and the default size</dd><dt><code>resize 16 8</code></dt><dd>resize the current picture, anchoring at the northwest corner</dd><dt><code>resize-nn 16 8</code></dt><dd>resize the current picture using the nearest-neighbor algorithm</dd><dt><code>w image.png</code></dt><dd>save the current picture under the specified filename (the filename is a symbol)</dd><dt><code>write &quot;image.png&quot;</code></dt><dd>save the current picture under the specified filename (the filename is a string)</dd></dl>
<p>Note that currently, edits done via commands are not added to undo history and break it.</p></section>
<section><h2 class="xd-section-heading">Planned features</h2><p>Roughly in order of priority.</p>
<ul><li>copy/paste functionality for the select mode</li><li>ellipse tool</li><li>eyedropper tool</li><li>upscaling (various algorithms)</li><li>better editing support and tab completion for the command line</li><li>mirrored drawing</li><li>color picker, palette editing</li><li>programmable configuration</li><li>gradients</li><li>animated sprite editing</li></ul></section>
<ul><li>ellipse tool</li><li>upscaling (various algorithms)</li><li>better editing support and tab completion for the command line</li><li>mirrored drawing</li><li>color picker, palette editing</li><li>programmable configuration</li><li>gradients</li><li>animated sprite editing</li></ul></section>
<section><h2 class="xd-section-heading">Name</h2><p>I partially created Vier as a semestral project for a Computer Graphics course at my university. The name of the lecturer can be loosely translated as “fear”, so I decided to name the project after a German word which is pronounced the same way. The word means “four”, which hints at the editor treating images as a square grid (with squares having four sides, obviously). I also came up with the backronym “Vim-Inspired Editor of Rasters”.</p></section>
</body></html>
\ No newline at end of file

M website/index.xd => website/index.xd +18 -7
@@ 60,18 60,18 @@
  [table
    [header-row Key; Alone; Shift; Control]
    [row [<kbd> ⎵]; hold to draw; hold to draw with secondary color]
    [row [<kbd> A]; Mode ← Add / add to selection]
    [row [<kbd> A]; Mode ← Add]
    [row [<kbd> B]; Tool ← Brush]
    [row [<kbd> C]; Mode ← Color]
    [row [<kbd> D]; delete pixel / delete selection]
    [row [<kbd> D]; delete pixel]
    [# row [<kbd> E]; Tool ← Ellipse]
    [row [<kbd> F]; Tool ← Flood]
    [row [<kbd> H]; move left; [# move to left edge]; previous picture/palette]
    [row [<kbd> I]; Mode ← Inject / fill selection]
    [row [<kbd> I]; Mode ← Inject]
    [row [<kbd> J]; move down; [# move to bottom edge]]
    [row [<kbd> K]; move up; [# move to top edge]]
    [row [<kbd> L]; move right; [# move to right edge]; next picture/palette]
    [row [<kbd> M]; Tool ← Mirror / cut selection]
    [# row [<kbd> M]; Tool ← Mirror]
    [row [<kbd> P]; paste]
    [row [<kbd> Q]; quit; force quit]
    [row [<kbd> R]; Tool ← Rectangle; ; redo]


@@ 80,12 80,24 @@
    [row [<kbd> V]; Mode ← Select]
    [row [<kbd> W]; write picture; write all pictures]
    [row [<kbd> X]; swap colors]
    [row [<kbd> Y]; pick color / copy selection; pick secondary color]
    [row [<kbd> Y]; pick color; pick secondary color]
    [row [<kbd> -]; zoom out; zoom out all pictures]
    [row [<kbd> =]; zoom in; zoom in all pictures]
    [row [<kbd> [;]]; Mode ← Command]
  ]
  Note that the mouse can also be used to draw and select colors. The right mouse button draws with the secondary color.
  [section Selection keyboard mappings;
    [p Some keys work differently when used with an active selection.]
    [table
      [header-row Key; Alone; Shift; Control]
      [row [<kbd> Esc]; cancel selection]
      [row [<kbd> A]; add selected color on selection]
      [# row [<kbd> D]; delete selection]
      [row [<kbd> I]; fill selection with selected color]
      [# row [<kbd> M]; cut selection]
      [row [<kbd> Y]; copy selection]
    ]
  ]
]
[section Commands;
  [description-list


@@ 94,6 106,7 @@
    ; [code flip-x]; flip the current picture horizontally
    ; [code flip-y]; flip the current picture vertically
    ; [code new]; create a new picture with a transparent background and the default size
    ; [code resize 16 8]; resize the current picture, anchoring at the northwest corner
    ; [code resize-nn 16 8]; resize the current picture using the nearest-neighbor algorithm
    ; [code w image.png]; save the current picture under the specified filename (the filename is a symbol)
    ; [code write "image.png"]; save the current picture under the specified filename (the filename is a string)


@@ 103,9 116,7 @@
[section Planned features;
  [p Roughly in order of priority.]
  [list
    ; copy/paste functionality for the select mode
    ; ellipse tool
    ; eyedropper tool
    ; upscaling (various algorithms)
    ; better editing support and tab completion for the command line
    ; mirrored drawing