Add various code comments
parent
f453615478
commit
ba12fef1c6
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component is a modal settings dialog for changing the background color.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component provides a visualization of the current brush size and shape and controls for changing them.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { OverflowMenu, OverflowMenuItem } from "carbon-components-svelte";
|
import { OverflowMenu, OverflowMenuItem } from "carbon-components-svelte";
|
||||||
import { FilledCircle, FilledSquare, type PixelPosition } from "../types/shapes"
|
import { FilledCircle, FilledSquare, type PixelPosition } from "../types/shapes"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component is a modal that provides settings for changing the checkerboard background.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component shows a given color swatch/index and provides controls for adding and replacing swatches within the palette.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { Button } from "carbon-components-svelte";
|
import { Button } from "carbon-components-svelte";
|
||||||
import { AddLarge, ColorSwitch } from "carbon-icons-svelte";
|
import { AddLarge, ColorSwitch } from "carbon-icons-svelte";
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component provides HSV controls for adjusting RGBA input values.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { HSV2RGB, RGB2HSV } from "../types/colors"
|
import { HSV2RGB, RGB2HSV } from "../types/colors"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component provides a modal for deleting a swatch from the palette.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { Checkbox, Column, Dropdown, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
import { Checkbox, Column, Dropdown, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||||
import type { LoadedFile } from "../types/file"
|
import type { LoadedFile } from "../types/file"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component provides a draggable floating panel that contains arbitrary content.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import {
|
import {
|
||||||
ModalHeader,
|
ModalHeader,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component provides a modal for adjusting the grid settings.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component is a modal that provides settings for changing the theme.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { Column, Dropdown, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
import { Column, Dropdown, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component is a full 2D pixel editor.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component provides importing an indexed or RGBA PNG file with options for how to interpret columns and rows as groups and animations.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { GetFilePath, OpenFileBytes } from '../../wailsjs/go/main/App.js'
|
import { GetFilePath, OpenFileBytes } from '../../wailsjs/go/main/App.js'
|
||||||
import { data } from '../../wailsjs/go/models.js'
|
import { data } from '../../wailsjs/go/models.js'
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component provides a modal for creating a new file.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { data } from '../../wailsjs/go/models.js'
|
import { data } from '../../wailsjs/go/models.js'
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component shows swatches of a given palette and provides controls for selecting, moving, and deleting swatches.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import type { Color } from '../types/palette'
|
import type { Color } from '../types/palette'
|
||||||
import { ReplaceSwatchUndoable, type LoadedFile, AddSwatchUndoable, MoveSwatchUndoable } from '../types/file'
|
import { ReplaceSwatchUndoable, type LoadedFile, AddSwatchUndoable, MoveSwatchUndoable } from '../types/file'
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
This component provides a sprite stack view of a file.
|
||||||
|
-->
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { Grid, Row, Column, Checkbox, Slider } from "carbon-components-svelte"
|
import { Grid, Row, Column, Checkbox, Slider } from "carbon-components-svelte"
|
||||||
import type { LoadedFile } from "src/types/file"
|
import type { LoadedFile } from "src/types/file"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import type { PixelPosition } from "./shapes"
|
import type { PixelPosition } from "./shapes"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Canvas}
|
||||||
|
*
|
||||||
|
* Canvas provides a way to store and manipulate 2D pixel data.
|
||||||
|
*/
|
||||||
export class Canvas {
|
export class Canvas {
|
||||||
width: number
|
width: number
|
||||||
height: number
|
height: number
|
||||||
|
|
@ -36,21 +41,27 @@ export class Canvas {
|
||||||
return canvas
|
return canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear sets all pixels to 0.
|
||||||
clear() {
|
clear() {
|
||||||
for (let i = 0; i < this.pixels.length; i++) {
|
for (let i = 0; i < this.pixels.length; i++) {
|
||||||
this.pixels[i] = 0
|
this.pixels[i] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// refreshCanvas redraws the canvas with the current pixel data.
|
||||||
refreshCanvas() {
|
refreshCanvas() {
|
||||||
this.canvas.width = this.width
|
this.canvas.width = this.width
|
||||||
this.canvas.height = this.height
|
this.canvas.height = this.height
|
||||||
let ctx = this.canvas.getContext('2d')
|
let ctx = this.canvas.getContext('2d')
|
||||||
ctx.putImageData(this.imageData, 0, 0)
|
ctx.putImageData(this.imageData, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setPalette sets the palette to the provided value.
|
||||||
setPalette(palette: Uint32Array) {
|
setPalette(palette: Uint32Array) {
|
||||||
this.palette = palette
|
this.palette = palette
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getPaletteAsRGBA returns the RGBA values of the palette color at the provided index.
|
||||||
getPaletteAsRGBA(index: number): { r: number, g: number, b: number, a: number } {
|
getPaletteAsRGBA(index: number): { r: number, g: number, b: number, a: number } {
|
||||||
if (index < 0 || index >= this.palette.length) {
|
if (index < 0 || index >= this.palette.length) {
|
||||||
return { r: 0, g: 0, b: 0, a: 0 }
|
return { r: 0, g: 0, b: 0, a: 0 }
|
||||||
|
|
@ -63,12 +74,16 @@ export class Canvas {
|
||||||
a: (color >> 24) & 0xFF
|
a: (color >> 24) & 0xFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setPaletteFromUint8Array sets the palette to the provided value.
|
||||||
setPaletteFromUint8Array(palette: Uint8Array) {
|
setPaletteFromUint8Array(palette: Uint8Array) {
|
||||||
this.palette = new Uint32Array(palette.length / 4)
|
this.palette = new Uint32Array(palette.length / 4)
|
||||||
for (let i = 0; i < palette.length; i += 4) {
|
for (let i = 0; i < palette.length; i += 4) {
|
||||||
this.palette[i / 4] = new Uint32Array(palette.buffer.slice(i, i + 4))[0]
|
this.palette[i / 4] = new Uint32Array(palette.buffer.slice(i, i + 4))[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setPixelsFromUint8Array sets the pixel data to the provided value.
|
||||||
setPixelsFromUint8Array(pixels: Uint8Array) {
|
setPixelsFromUint8Array(pixels: Uint8Array) {
|
||||||
this.pixels = pixels
|
this.pixels = pixels
|
||||||
this.imageData = new ImageData(this.width, this.height)
|
this.imageData = new ImageData(this.width, this.height)
|
||||||
|
|
@ -80,12 +95,16 @@ export class Canvas {
|
||||||
this.imageData.data[i * 4 + 3] = (color >> 24) & 0xFF
|
this.imageData.data[i * 4 + 3] = (color >> 24) & 0xFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getPixel gets the index at the provided pixel position.
|
||||||
getPixel(x: number, y: number): number {
|
getPixel(x: number, y: number): number {
|
||||||
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return this.pixels[y * this.width + x]
|
return this.pixels[y * this.width + x]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setPixel sets the index at the provided pixel position.
|
||||||
setPixel(x: number, y: number, index: number) {
|
setPixel(x: number, y: number, index: number) {
|
||||||
this.pixels[y * this.width + x] = index
|
this.pixels[y * this.width + x] = index
|
||||||
let color = this.palette[index]
|
let color = this.palette[index]
|
||||||
|
|
@ -98,6 +117,8 @@ export class Canvas {
|
||||||
this.imageData.data[(y * this.width + x) * 4 + 2] = b
|
this.imageData.data[(y * this.width + x) * 4 + 2] = b
|
||||||
this.imageData.data[(y * this.width + x) * 4 + 3] = a
|
this.imageData.data[(y * this.width + x) * 4 + 3] = a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setPixelRGBA sets the given pixel position to the provided RGBA values. If the RGBA values do not exist in the palette, they are automatically added.
|
||||||
setPixelRGBA(x: number, y: number, r: number, g: number, b: number, a: number) {
|
setPixelRGBA(x: number, y: number, r: number, g: number, b: number, a: number) {
|
||||||
this.pixels[y * this.width + x] = this.addPaletteColor(r, g, b, a)
|
this.pixels[y * this.width + x] = this.addPaletteColor(r, g, b, a)
|
||||||
this.imageData.data[(y * this.width + x) * 4 + 0] = r
|
this.imageData.data[(y * this.width + x) * 4 + 0] = r
|
||||||
|
|
@ -105,6 +126,8 @@ export class Canvas {
|
||||||
this.imageData.data[(y * this.width + x) * 4 + 2] = b
|
this.imageData.data[(y * this.width + x) * 4 + 2] = b
|
||||||
this.imageData.data[(y * this.width + x) * 4 + 3] = a
|
this.imageData.data[(y * this.width + x) * 4 + 3] = a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addPaletteColor adds the provided RGBA values to the palette if it does not already exist, and returns the index of the color in the palette.
|
||||||
addPaletteColor(r: number, g: number, b: number, a: number): number {
|
addPaletteColor(r: number, g: number, b: number, a: number): number {
|
||||||
// Check if the color is already in the palette
|
// Check if the color is already in the palette
|
||||||
for (let i = 0; i < this.palette.length; i++) {
|
for (let i = 0; i < this.palette.length; i++) {
|
||||||
|
|
@ -124,6 +147,8 @@ export class Canvas {
|
||||||
|
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getClosestPaletteColor returns the RGBA and index values of the closest color in the palette to the provided RGBA values.
|
||||||
getClosestPaletteColor(r: number, g: number, b: number, a: number): {r: number, g: number, b: number, a: number, index: number} {
|
getClosestPaletteColor(r: number, g: number, b: number, a: number): {r: number, g: number, b: number, a: number, index: number} {
|
||||||
let similarityMap = this.palette.map((color) => {
|
let similarityMap = this.palette.map((color) => {
|
||||||
let r2 = color & 0xFF
|
let r2 = color & 0xFF
|
||||||
|
|
@ -148,9 +173,13 @@ export class Canvas {
|
||||||
index: closestIndex
|
index: closestIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addNewPaletteColor adds the provided RGBA values to the palette.
|
||||||
addNewPaletteColor(r: number, g: number, b: number, a: number) {
|
addNewPaletteColor(r: number, g: number, b: number, a: number) {
|
||||||
this.palette = new Uint32Array([...this.palette, new Uint32Array([(a << 24) | (b << 16) | (g << 8) | r])[0]])
|
this.palette = new Uint32Array([...this.palette, new Uint32Array([(a << 24) | (b << 16) | (g << 8) | r])[0]])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insertPaletteColor inserts the provided RGBA values into the palette at the provided index.
|
||||||
insertPaletteColor(index: number, r: number, g: number, b: number, a: number) {
|
insertPaletteColor(index: number, r: number, g: number, b: number, a: number) {
|
||||||
let newPalette = new Uint32Array(this.palette.length + 1)
|
let newPalette = new Uint32Array(this.palette.length + 1)
|
||||||
for (let i = 0; i < index; i++) {
|
for (let i = 0; i < index; i++) {
|
||||||
|
|
@ -162,6 +191,8 @@ export class Canvas {
|
||||||
}
|
}
|
||||||
this.palette = newPalette
|
this.palette = newPalette
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// removePaletteIndex removes the palette entry at the provided index. This also updates the pixel data to reflect the change.
|
||||||
removePaletteIndex(index: number) {
|
removePaletteIndex(index: number) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
index = this.palette.length - index
|
index = this.palette.length - index
|
||||||
|
|
@ -182,9 +213,13 @@ export class Canvas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replacePaletteColor replaces the palette entry at the provided index with the provided RGBA values.
|
||||||
replacePaletteColor(index: number, r: number, g: number, b: number, a: number) {
|
replacePaletteColor(index: number, r: number, g: number, b: number, a: number) {
|
||||||
this.palette[index] = new Uint32Array([(a << 24) | (b << 16) | (g << 8) | r])[0]
|
this.palette[index] = new Uint32Array([(a << 24) | (b << 16) | (g << 8) | r])[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasPaletteColor returns whether the palette contains the provided RGBA values.
|
||||||
hasPaletteColor(r: number, g: number, b: number, a: number): boolean {
|
hasPaletteColor(r: number, g: number, b: number, a: number): boolean {
|
||||||
for (let color of this.palette) {
|
for (let color of this.palette) {
|
||||||
if ((color & 0xFF) === r && ((color >> 8) & 0xFF) === g && ((color >> 16) & 0xFF) === b && ((color >> 24) & 0xFF) === a) {
|
if ((color & 0xFF) === r && ((color >> 8) & 0xFF) === g && ((color >> 16) & 0xFF) === b && ((color >> 24) & 0xFF) === a) {
|
||||||
|
|
@ -193,11 +228,15 @@ export class Canvas {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swapPaletteColors swaps the palette entries at the provided indices.
|
||||||
swapPaletteColors(index1: number, index2: number) {
|
swapPaletteColors(index1: number, index2: number) {
|
||||||
let temp = this.palette[index1]
|
let temp = this.palette[index1]
|
||||||
this.palette[index1] = this.palette[index2]
|
this.palette[index1] = this.palette[index2]
|
||||||
this.palette[index2] = temp
|
this.palette[index2] = temp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// movePaletteColor moves the palette entry at the provided index to the new index.
|
||||||
movePaletteColor(from: number, to: number) {
|
movePaletteColor(from: number, to: number) {
|
||||||
let temp = this.palette[from]
|
let temp = this.palette[from]
|
||||||
if (from < to) {
|
if (from < to) {
|
||||||
|
|
@ -211,9 +250,13 @@ export class Canvas {
|
||||||
}
|
}
|
||||||
this.palette[to] = temp
|
this.palette[to] = temp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setFakePalette updates the fake palette to the provided value.
|
||||||
setFakePalette(palette: Uint32Array | undefined) {
|
setFakePalette(palette: Uint32Array | undefined) {
|
||||||
this.fakePalette = palette
|
this.fakePalette = palette
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// refreshImageData updates the ImageData with the current pixel data.
|
||||||
refreshImageData() {
|
refreshImageData() {
|
||||||
let palette = this.palette
|
let palette = this.palette
|
||||||
if (this.fakePalette) {
|
if (this.fakePalette) {
|
||||||
|
|
@ -231,7 +274,7 @@ export class Canvas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the an ImageData containing the canvas contents clipped to the provided pixel mask.
|
// getImageDataFromMask the ImageData containing the canvas contents clipped to the provided pixel mask.
|
||||||
getImageDataFromMask(mask: PixelPosition[]): {imageData: ImageData, x: number, y: number, w: number, h: number} {
|
getImageDataFromMask(mask: PixelPosition[]): {imageData: ImageData, x: number, y: number, w: number, h: number} {
|
||||||
// Get minimum x position from mask.
|
// Get minimum x position from mask.
|
||||||
let minX = 9999999
|
let minX = 9999999
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// floatsToBytes converts 0-1 to a 0-255 range.
|
||||||
function floatsToBytes(colors: number[]): number[] {
|
function floatsToBytes(colors: number[]): number[] {
|
||||||
if (colors.length <= 0) return colors
|
if (colors.length <= 0) return colors
|
||||||
if (colors[0] <= 1.0) {
|
if (colors[0] <= 1.0) {
|
||||||
|
|
@ -8,6 +9,7 @@ function floatsToBytes(colors: number[]): number[] {
|
||||||
return colors
|
return colors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bytesToFloats converts 0-255 to a 0-1 range.
|
||||||
function bytesToFloats(colors: number[]): number[] {
|
function bytesToFloats(colors: number[]): number[] {
|
||||||
if (colors.length <= 0) return colors
|
if (colors.length <= 0) return colors
|
||||||
if (colors[0] > 1.0) {
|
if (colors[0] > 1.0) {
|
||||||
|
|
@ -18,6 +20,7 @@ function bytesToFloats(colors: number[]): number[] {
|
||||||
return colors
|
return colors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HSV2RGB converts HSV to RGB.
|
||||||
export function HSV2RGB(hsv: number[]): number[] {
|
export function HSV2RGB(hsv: number[]): number[] {
|
||||||
let hh: number, p: number, q: number, t: number, ff: number, i: number
|
let hh: number, p: number, q: number, t: number, ff: number, i: number
|
||||||
let rgb: number[] = []
|
let rgb: number[] = []
|
||||||
|
|
@ -75,6 +78,7 @@ export function HSV2RGB(hsv: number[]): number[] {
|
||||||
return floatsToBytes(rgb)
|
return floatsToBytes(rgb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RGB2HSV converts RGB to HSV.
|
||||||
export function RGB2HSV(rgb: number[]): number[] {
|
export function RGB2HSV(rgb: number[]): number[] {
|
||||||
rgb = bytesToFloats(rgb)
|
rgb = bytesToFloats(rgb)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { PixelPosition } from "./shapes"
|
import type { PixelPosition } from "./shapes"
|
||||||
|
|
||||||
|
// SelectionArea is basically a canvas and pixel mask that is used to represent a selection area. It provides marching ants to visualize the selection.
|
||||||
export class SelectionArea {
|
export class SelectionArea {
|
||||||
public marchingCanvas: HTMLCanvasElement
|
public marchingCanvas: HTMLCanvasElement
|
||||||
private marchStep: number = 0
|
private marchStep: number = 0
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
// PixelPosition represents a coordinate and index.
|
||||||
export interface PixelPosition {
|
export interface PixelPosition {
|
||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilledCircle returns an array of PixelPositions that correspond to a filled circle.
|
||||||
export function FilledCircle(x: number, y: number, radius: number, index: number): PixelPosition[] {
|
export function FilledCircle(x: number, y: number, radius: number, index: number): PixelPosition[] {
|
||||||
let pixels: PixelPosition[] = []
|
let pixels: PixelPosition[] = []
|
||||||
|
|
||||||
|
|
@ -18,6 +20,7 @@ export function FilledCircle(x: number, y: number, radius: number, index: number
|
||||||
return pixels
|
return pixels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilledSquare returns an array of PixelPositions that correspond to a filled square.
|
||||||
export function FilledSquare(x: number, y: number, size: number, index: number): PixelPosition[] {
|
export function FilledSquare(x: number, y: number, size: number, index: number): PixelPosition[] {
|
||||||
let pixels: PixelPosition[] = []
|
let pixels: PixelPosition[] = []
|
||||||
|
|
||||||
|
|
@ -32,6 +35,7 @@ export function FilledSquare(x: number, y: number, size: number, index: number):
|
||||||
return pixels
|
return pixels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RandomSpray returns an array of PixelPositions that correspond to a random spray of pixels.
|
||||||
export function RandomSpray(x: number, y: number, radius: number, density: number, index: number): PixelPosition[] {
|
export function RandomSpray(x: number, y: number, radius: number, density: number, index: number): PixelPosition[] {
|
||||||
let pixels: PixelPosition[] = []
|
let pixels: PixelPosition[] = []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ interface Pointer {
|
||||||
|
|
||||||
export type BrushType = "circle" | "square"
|
export type BrushType = "circle" | "square"
|
||||||
|
|
||||||
|
// Tool is an interface that receives pointer events and can act upon a ToolContext (which contains data such as the current file).
|
||||||
export interface Tool {
|
export interface Tool {
|
||||||
isActive(): boolean
|
isActive(): boolean
|
||||||
pointerDown(ctx: ToolContext, ptr: Pointer): void
|
pointerDown(ctx: ToolContext, ptr: Pointer): void
|
||||||
|
|
@ -23,34 +24,41 @@ export interface Tool {
|
||||||
pointerUp(ctx: ToolContext, ptr: Pointer): void
|
pointerUp(ctx: ToolContext, ptr: Pointer): void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BrushToolContext provides context specific to the brush tool.
|
||||||
export interface BrushToolContext {
|
export interface BrushToolContext {
|
||||||
brushSize: number
|
brushSize: number
|
||||||
brushType: BrushType
|
brushType: BrushType
|
||||||
colorIndex: number
|
colorIndex: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SprayToolContext provides context specific to the spray tool.
|
||||||
export interface SprayToolContext {
|
export interface SprayToolContext {
|
||||||
radius: number
|
radius: number
|
||||||
density: number
|
density: number
|
||||||
colorIndex: number
|
colorIndex: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EraserToolContext provides context specific to the eraser tool.
|
||||||
export interface EraserToolContext {
|
export interface EraserToolContext {
|
||||||
brushSize: number
|
brushSize: number
|
||||||
brushType: BrushType
|
brushType: BrushType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FloodToolContext provides context specific to the flood tool.
|
||||||
export interface FloodToolContext {
|
export interface FloodToolContext {
|
||||||
colorIndex: number
|
colorIndex: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectionToolContext provides context specific to the selection tool.
|
||||||
export interface SelectionToolContext {
|
export interface SelectionToolContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PickerToolContext provides context specific to the picker tool.
|
||||||
export interface PickerToolContext {
|
export interface PickerToolContext {
|
||||||
setColorIndex(index: number): void
|
setColorIndex(index: number): void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BrushTool is a tool that allows the user to draw with a brush.
|
||||||
export class BrushTool implements Tool {
|
export class BrushTool implements Tool {
|
||||||
private lastX: number
|
private lastX: number
|
||||||
private lastY: number
|
private lastY: number
|
||||||
|
|
@ -139,6 +147,7 @@ export class BrushTool implements Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EraserTool is basically the BrushTool, but with the color index set to 0 (meaning a transparent pixel).
|
||||||
export class EraserTool extends BrushTool {
|
export class EraserTool extends BrushTool {
|
||||||
pointerDown(ctx: ToolContext & EraserToolContext, ptr: Pointer) {
|
pointerDown(ctx: ToolContext & EraserToolContext, ptr: Pointer) {
|
||||||
super.pointerDown({...ctx, colorIndex: 0}, ptr)
|
super.pointerDown({...ctx, colorIndex: 0}, ptr)
|
||||||
|
|
@ -148,6 +157,7 @@ export class EraserTool extends BrushTool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SprayTool implements a spray can tool.
|
||||||
export class SprayTool implements Tool {
|
export class SprayTool implements Tool {
|
||||||
private active: boolean
|
private active: boolean
|
||||||
private lastX: number
|
private lastX: number
|
||||||
|
|
@ -192,6 +202,7 @@ export class SprayTool implements Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FillTool implements a flood fill tool.
|
||||||
export class FillTool implements Tool {
|
export class FillTool implements Tool {
|
||||||
private active: boolean
|
private active: boolean
|
||||||
isActive(): boolean {
|
isActive(): boolean {
|
||||||
|
|
@ -239,6 +250,7 @@ export class FillTool implements Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PickerTool allows picking the color index from the canvas.
|
||||||
export class PickerTool implements Tool {
|
export class PickerTool implements Tool {
|
||||||
private active: boolean
|
private active: boolean
|
||||||
isActive(): boolean {
|
isActive(): boolean {
|
||||||
|
|
@ -263,6 +275,7 @@ export class PickerTool implements Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectionTool allows selecting an area of the canvas.
|
||||||
export class SelectionTool implements Tool {
|
export class SelectionTool implements Tool {
|
||||||
private active: boolean
|
private active: boolean
|
||||||
private startX: number
|
private startX: number
|
||||||
|
|
@ -332,6 +345,7 @@ export class SelectionTool implements Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MagicWandTool implements a magic wand tool.
|
||||||
export class MagicWandTool implements Tool {
|
export class MagicWandTool implements Tool {
|
||||||
private active: boolean
|
private active: boolean
|
||||||
|
|
||||||
|
|
@ -385,6 +399,7 @@ export class MagicWandTool implements Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MoveTool implements a tool to move pixels within a selection.
|
||||||
export class MoveTool implements Tool {
|
export class MoveTool implements Tool {
|
||||||
private active: boolean
|
private active: boolean
|
||||||
private startX: number
|
private startX: number
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// UndoableStack provides an undo/redo system.
|
||||||
export class UndoableStack<T> {
|
export class UndoableStack<T> {
|
||||||
private target: T;
|
private target: T;
|
||||||
private stack: Undoable<T>[] = [];
|
private stack: Undoable<T>[] = [];
|
||||||
|
|
@ -97,11 +98,13 @@ export class UndoableStack<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Undoable is an interface for undoable actions stored in the stack.
|
||||||
export interface Undoable<T> {
|
export interface Undoable<T> {
|
||||||
apply(t: T): void;
|
apply(t: T): void;
|
||||||
unapply(t: T): void;
|
unapply(t: T): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UndoableGroup is a group of undoable actions. This is used to group multiple actions, such as drawing with a pen, into one undoable action.
|
||||||
export class UndoableGroup<T> {
|
export class UndoableGroup<T> {
|
||||||
private items: Undoable<T>[];
|
private items: Undoable<T>[];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue