diff --git a/core/core.js b/core/core.js
index 3b6b8bd..127903b 100644
--- a/core/core.js
+++ b/core/core.js
@@ -10,6 +10,36 @@
*/
let STATE = _SETTINGS.setup.initialState;
+const { pixelCrud, lineCrud, rectCrud, polyCrud, stateCrud, maskCrud } = {
+ pixelCrud: new Crud(CrudStore.Pixels),
+ lineCrud: new Crud(CrudStore.Lines),
+ rectCrud: new Crud(CrudStore.Rectangles),
+ polyCrud: new Crud(CrudStore.Polygons),
+ stateCrud: new Crud(CrudStore.State),
+ maskCrud: new Crud(CrudStore.Mask),
+};
+// Usage sample of new CRUD implementation
+/*
+lineCrud.create({point1: [0,0]});
+lineCrud.create({point1: [55,2]});
+lineCrud.create({point1: [1,4]});
+console.log(lineCrud.getAll());
+*/
+
+const PolyMan = new PolygonContainer();
+
+
+const countSizes = () => {
+ _SETTINGS.general.activeArea.top = 0;
+ _SETTINGS.general.activeArea.left = Math.floor((_SETTINGS.toolbar.width + _SETTINGS.toolbar.offset.left + _SETTINGS.toolbar.offset.right) / PIXEL_SIZE) * PIXEL_SIZE;
+ _SETTINGS.general.activeArea.width = Math.floor((CANVAS_W - (_SETTINGS.toolbar.offset.left + _SETTINGS.toolbar.width + _SETTINGS.toolbar.offset.right)) / PIXEL_SIZE) * PIXEL_SIZE;
+ _SETTINGS.general.activeArea.height = (Math.floor((CANVAS_H - _SETTINGS.general.activeArea.top) / PIXEL_SIZE) - 1) * PIXEL_SIZE;
+ _SETTINGS.general.activeArea.cellBorders.left = -Math.floor((CANVAS_W / 2 - _SETTINGS.general.activeArea.left) / PIXEL_SIZE);
+ _SETTINGS.general.activeArea.cellBorders.top = Math.floor((CANVAS_H / 2 - _SETTINGS.general.activeArea.top + PIXEL_SIZE / 2) / PIXEL_SIZE) - 1;
+ _SETTINGS.general.activeArea.cellBorders.right = Math.floor((_SETTINGS.general.activeArea.left + _SETTINGS.general.activeArea.width - CANVAS_W / 2) / PIXEL_SIZE);
+ _SETTINGS.general.activeArea.cellBorders.bottom = -Math.floor((_SETTINGS.general.activeArea.top + _SETTINGS.general.activeArea.height - CANVAS_H / 2 + PIXEL_SIZE / 2) / PIXEL_SIZE);
+
+};
const getRegion = () => {
if (testXY(_SETTINGS.general.activeArea.left,
@@ -43,15 +73,17 @@ const handleHotkey = (key) => {
};
const drawLineRaw = (point1, point2, color = COLORS.BLACK, thickness = 1) => {
- stroke(`rgba(${color.join(',')},0.9)`);
- strokeWeight(thickness);
+ STRCOLORS.STROKE && stroke(color);
+ STRCOLORS.STROKE ? strokeWeight(thickness) : strokeWeight(0);
line(point1[0], point1[1], point2[0], point2[1]);
};
const drawLine = (point1, point2, color = STATE.currentColor, thickness = 1, type = [AlgoType.BRZ]) => {
- stroke(color);
- strokeWeight(thickness);
- line(CENTER_W + (point1[0] + 0.5) * PIXEL_SIZE, CENTER_H - (point1[1] + 0.5) * PIXEL_SIZE, CENTER_W + point2[0] * PIXEL_SIZE, CENTER_H - (point2[1] + 0.5) * PIXEL_SIZE);
+ if (type.includes(AlgoType.PLAIN)) {
+ stroke(color);
+ strokeWeight(thickness);
+ line(CENTER_W + (point1[0] + 0.5) * PIXEL_SIZE, CENTER_H - (point1[1] + 0.5) * PIXEL_SIZE, CENTER_W + point2[0] * PIXEL_SIZE, CENTER_H - (point2[1] + 0.5) * PIXEL_SIZE);
+ }
if (type.includes(AlgoType.BRZ)) {
let x00, x11, y00, y11;
@@ -94,58 +126,84 @@ const drawLine = (point1, point2, color = STATE.currentColor, thickness = 1, typ
}
};
+const drawRectangle = (point1, point2, color = STATE.currentColor, thickness = 1, type = [AlgoType.BRZ]) => {
+ drawLine(point1, [point2[0], point1[1]], color, 0, [AlgoType.BRZ]);
+ drawLine([point2[0], point1[1]], point2, color, 0, [AlgoType.BRZ]);
+ drawLine(point2, [point1[0], point2[1]], color, 0, [AlgoType.BRZ]);
+ drawLine([point1[0], point2[1]], point1, color, 0, [AlgoType.BRZ]);
+};
-class LDM {
- constructor() {
- this.__reset();
- }
- firstpoint = true;
- coord;
- colors;
- mark = COLORS.WHAY;
-
- __reset = () => {
- this.colors = [null, null];
- this.coord = [[0, 0], [0, 0]];
- };
-
- setCoord = (i, x, y) => {
- if (i === 0 && this.colors[i] !== null && !arraysEqual(this.colors[i], this.mark)) {
- putPixel([this.coord[i][0], this.coord[i][1]], this.colors[i]);
- }
- this.colors[i] = get(mouseX, mouseY).slice(0, 3);
- putPixel([x, y], this.mark);
- this.coord[i] = [x, y];
- };
+const testForMask = ([x, y]) => {
+ let mask = [false, false, false, false];
- draw = () => {
- this.colors = [STATE.currentColor, STATE.currentColor];
- drawLine(this.coord[0], this.coord[1], STATE.currentColor, 0, [AlgoType.BRZ]);
+ if (testXY( // top part
+ _SETTINGS.general.activeArea.cellBorders.left,
+ _SETTINGS.general.activeArea.cellBorders.right,
+ STATE.maskOptions.lt[1],
+ _SETTINGS.general.activeArea.cellBorders.top, x, y)) {
+ mask[0] = true
}
-}
+ if (testXY( // right part
+ STATE.maskOptions.rt[0],
+ _SETTINGS.general.activeArea.cellBorders.right,
+ _SETTINGS.general.activeArea.cellBorders.bottom,
+ _SETTINGS.general.activeArea.cellBorders.top, x, y)) {
+ mask[1] = true
+ }
+ if (testXY( // bottom part
+ _SETTINGS.general.activeArea.cellBorders.left,
+ _SETTINGS.general.activeArea.cellBorders.right,
+ _SETTINGS.general.activeArea.cellBorders.bottom,
+ STATE.maskOptions.rb[1], x, y)) {
+ mask[2] = true
+ }
+ if (testXY( // left part
+ _SETTINGS.general.activeArea.cellBorders.left,
+ STATE.maskOptions.lt[0],
+ _SETTINGS.general.activeArea.cellBorders.bottom,
+ _SETTINGS.general.activeArea.cellBorders.top, x, y)) {
+ mask[3] = true
+ }
+ //console.log(['top', 'right', 'bottom', 'left'].map((v, i) => mask[i] ? v : ''));
+ return mask;
+};
const drawGrid = (drawBack = null) => {
background(COLORS.BG);
- _SETTINGS.general.activeArea.left = Math.floor((_SETTINGS.toolbar.width + _SETTINGS.toolbar.offset.left + _SETTINGS.toolbar.offset.right) / PIXEL_SIZE) * PIXEL_SIZE;
- _SETTINGS.general.activeArea.width = Math.floor((CANVAS_W - (_SETTINGS.toolbar.offset.left + _SETTINGS.toolbar.width + _SETTINGS.toolbar.offset.right)) / PIXEL_SIZE) * PIXEL_SIZE;
- _SETTINGS.general.activeArea.height = (Math.floor(CANVAS_H / PIXEL_SIZE) - 1) * PIXEL_SIZE;
+
+ /*stroke(COLORS.BLACK);
+ strokeWeight(3);
+ console.log(_SETTINGS.general.activeArea.left,
+ _SETTINGS.general.activeArea.top,
+ _SETTINGS.general.activeArea.left + _SETTINGS.general.activeArea.width,
+ _SETTINGS.general.activeArea.top + _SETTINGS.general.activeArea.height);
+ rect(
+ _SETTINGS.general.activeArea.left,
+ _SETTINGS.general.activeArea.top,
+ _SETTINGS.general.activeArea.left + _SETTINGS.general.activeArea.width,
+ _SETTINGS.general.activeArea.top + _SETTINGS.general.activeArea.height
+ );*/
for (let i = _SETTINGS.general.activeArea.left; i <= _SETTINGS.general.activeArea.left + _SETTINGS.general.activeArea.width; i += PIXEL_SIZE) {
- drawLineRaw([i, 0], [i, _SETTINGS.general.activeArea.height], COLORS.STROKE)
+ drawLineRaw([i, 0], [i, _SETTINGS.general.activeArea.height], STRCOLORS.STROKE)
}
for (let i = _SETTINGS.general.activeArea.top; i <= _SETTINGS.general.activeArea.height; i += PIXEL_SIZE) {
- drawLineRaw([_SETTINGS.general.activeArea.left, i], [_SETTINGS.general.activeArea.width + _SETTINGS.general.activeArea.left, i], COLORS.STROKE)
+ drawLineRaw([_SETTINGS.general.activeArea.left, i], [_SETTINGS.general.activeArea.width + _SETTINGS.general.activeArea.left, i], STRCOLORS.STROKE)
}
};
_SETTINGS.setup.modules.grid.render = drawGrid;
// Initializing
-const putPixel = ([cox, coy], color = STATE.currentColor) => {
- fill(color);
- stroke(`rgba(${COLORS.STROKE.join(',')},0.9)`);
- strokeWeight(1);
+const putPixel = ([cox, coy], color = STATE.currentColor, ignoreMask = false) => {
+ if (ignoreMask || STATE.maskOptions.isActive && !testForMask([cox, coy]).includes(true) || !STATE.maskOptions.isActive) {
+ fill(color);
+ } else {
+ fill(COLORS.WHAY);
+ }
+ STRCOLORS.STROKE && stroke(STRCOLORS.STROKE);
+ STRCOLORS.STROKE ? strokeWeight(1) : strokeWeight(0);
square(CENTER_W + cox * PIXEL_SIZE, CENTER_H - (coy + 1) * PIXEL_SIZE, PIXEL_SIZE);
};
@@ -159,17 +217,37 @@ const startFillFrom = async (x, y, isColored = false, startColor = COLORS.WHITE,
putPixel(C2Pix(x, y), STATE.currentColor);
blockDir !== Directions.Left && isDrawable(C2Pix(x - PIXEL_SIZE, y)) &&
- await setTimeout(() => startFillFrom(x - PIXEL_SIZE, y, true, startColor, Directions.Right), 30);
+ await setTimeout(() => startFillFrom(x - PIXEL_SIZE, y, true, startColor, Directions.Right), _SETTINGS.toolbar.toolParams.Fill.delay);
+ blockDir !== Directions.Up && isDrawable(C2Pix(x, y - PIXEL_SIZE)) &&
+ await setTimeout(() => startFillFrom(x, y - PIXEL_SIZE, true, startColor, Directions.Down), _SETTINGS.toolbar.toolParams.Fill.delay);
+ blockDir !== Directions.Right && isDrawable(C2Pix(x + PIXEL_SIZE, y)) &&
+ await setTimeout(() => startFillFrom(x + PIXEL_SIZE, y, true, startColor, Directions.Left), _SETTINGS.toolbar.toolParams.Fill.delay);
+ blockDir !== Directions.Down && isDrawable(C2Pix(x, y + PIXEL_SIZE)) &&
+ await setTimeout(() => startFillFrom(x, y + PIXEL_SIZE, true, startColor, Directions.Up), _SETTINGS.toolbar.toolParams.Fill.delay);
+ STATE.processes.terminatedByEnd++;
+};
+
+const forceFill = async ([x, y], ignoreColor = COLORS._MASK, blockDir = null) => {
+ let c = get(x, y).slice(0, 3);
+ if (arraysEqual(c, ignoreColor) || arraysEqual(c, COLORS.BG)) {
+ STATE.processes.terminatedByColor++;
+ return;
+ }
+ putPixel(C2Pix(x, y), COLORS.BG);
+
+ blockDir !== Directions.Left && isDrawable(C2Pix(x - PIXEL_SIZE, y)) &&
+ await setTimeout(() => forceFill([x - PIXEL_SIZE, y], ignoreColor, Directions.Right), _SETTINGS.toolbar.toolParams.Fill.delay);
blockDir !== Directions.Up && isDrawable(C2Pix(x, y - PIXEL_SIZE)) &&
- await setTimeout(() => startFillFrom(x, y - PIXEL_SIZE, true, startColor, Directions.Down), 30);
+ await setTimeout(() => forceFill([x, y - PIXEL_SIZE], ignoreColor, Directions.Down), _SETTINGS.toolbar.toolParams.Fill.delay);
blockDir !== Directions.Right && isDrawable(C2Pix(x + PIXEL_SIZE, y)) &&
- await setTimeout(() => startFillFrom(x + PIXEL_SIZE, y, true, startColor, Directions.Left), 30);
+ await setTimeout(() => forceFill([x + PIXEL_SIZE, y], ignoreColor, Directions.Left), _SETTINGS.toolbar.toolParams.Fill.delay);
blockDir !== Directions.Down && isDrawable(C2Pix(x, y + PIXEL_SIZE)) &&
- await setTimeout(() => startFillFrom(x, y + PIXEL_SIZE, true, startColor, Directions.Up), 30);
+ await setTimeout(() => forceFill([x, y + PIXEL_SIZE], ignoreColor, Directions.Up), _SETTINGS.toolbar.toolParams.Fill.delay);
STATE.processes.terminatedByEnd++;
};
const changeColor = (type) => {
+ if (STATE.colorBlocked) return;
switch (type) {
case LEFT:
_SETTINGS.general.color.current =
@@ -190,50 +268,80 @@ const changeColor = (type) => {
const ToolsRenderer = {
'Pixel': (x, y, sx, sy) => {
fill(50);
- text('Pix', x, y, sx, sy);
+ text('Pix', x+1, y, sx-1, sy);
},
'Line': (x, y, sx, sy) => {
fill(50);
- text('Line', x, y, sx, sy);
+ text('Line', x+1, y, sx-1, sy);
+ },
+ 'Rectangle': (x, y, sx, sy) => {
+ fill(50);
+ text('Rect', x+1, y, sx-1, sy);
+ },
+ 'Polygon': (x, y, sx, sy) => {
+ fill(50);
+ text('Poly', x+1, y, sx-1, sy);
},
'Fill': (x, y, sx, sy) => {
fill(50);
- text('Fill', x, y, sx, sy);
+ text('Fill', x+1, y, sx-1, sy);
},
'Colorizer': (x, y, sx, sy) => {
fill(STATE.currentColor);
rect(x, y, sx, sy);
fill(50);
- text('COL', x, y, sx, sy);
+ text('COL', x+1, y, sx-1, sy);
},
'Clear': (x, y, sx, sy) => {
fill(230, 40, 40);
- text('ERS', x, y, sx, sy);
+ text('ERS', x+1, y, sx-1, sy);
},
'Export': (x, y, sx, sy) => {
fill(50);
- text('SAV', x, y, sx, sy);
- }
+ text('SAV', x+1, y, sx-1, sy);
+ },
+ 'Delimiter': (x, y, sx, sy) => {
+ stroke(STRCOLORS.STROKEBLACKA);
+ line(x, y + sy/2, x + sx, y + sy/2);
+ },
+ 'Masking': (x, y, sx, sy) => {
+ fill(COLORS.RED);
+ text('MSK', x+1, y, sx-1, sy);
+ },
+ 'MaskActivate': (x, y, sx, sy) => {
+ fill(STATE.maskOptions.isActive ? COLORS._MASK : COLORS.WHAY);
+ rect(x, y, sx, sy);
+ fill(50);
+ text(STATE.maskOptions.isActive ? 'ON' : 'OFF', x+1, y, sx-1, sy);
+ },
};
const ToolActions = {
- 'Pixel': (type) => {STATE.activeTool = 'Pixel'},
- 'Line': (type) => {STATE.activeTool = 'Line'},
- 'Fill': (type) => {STATE.activeTool = 'Fill'},
- 'Colorizer': (type) => {changeColor(type)},
- 'Clear': (type) => {
+ 'Pixel': (type) => ToolManager.changeTool('Pixel'),
+ 'Line': (type) => ToolManager.changeTool('Line'),
+ 'Rectangle': (type) => ToolManager.changeTool('Rectangle'),
+ 'Polygon': (type) => ToolManager.changeTool('Polygon'),
+ 'Fill': (type) => ToolManager.changeTool('Fill'),
+ 'Colorizer': (type) => ToolManager.useCallback(() => changeColor(type)),
+ 'Clear': (type) => ToolManager.useCallback( () => {
Object.keys(_SETTINGS.setup.modules).forEach(key => {
_SETTINGS.setup.modules[key].model ?
_SETTINGS.setup.modules[key].model.render()
: (_SETTINGS.setup.modules[key].render ? _SETTINGS.setup.modules[key].render() : console.log(`${key} isn\'t inited before clearing`))
});
document.dispatchEvent(new Event('clearEvent'));
- },
- 'Export': (type) => {
- saveCanvas(createCanvasName(), 'jpg')
- }
+ }),
+ 'Export': (type) => ToolManager.useCallback(
+ () => saveCanvas(createCanvasName(), 'jpg')
+ ),
+ 'Delimiter': (type) => {},
+ 'Masking': (type) => ToolManager.changeTool('Masking'),
+ 'MaskActivate': (type) => ToolManager.useCallback(
+ () => STATE.maskOptions.isActive = !STATE.maskOptions.isActive
+ ),
};
+
class Toolbar {
constructor() {
_SETTINGS.setup.modules.toolbar.isActive = true;
@@ -308,7 +416,9 @@ class Toolbar {
callback: ToolActions[tool]
});
- rect(px, py, sx, sy);
+ if (tool !== 'Delimiter') {
+ rect(px, py, sx, sy);
+ }
ToolsRenderer[tool](px, py, sx, sy);
py += this._margins.top + this._blockSize.height;
diff --git a/core/localCrud.js b/core/localCrud.js
new file mode 100644
index 0000000..bcf42d8
--- /dev/null
+++ b/core/localCrud.js
@@ -0,0 +1,102 @@
+const CrudStore = {
+ Pixels: 'store-pixels',
+ Lines: 'store-lines',
+ Rectangles: 'store-rects',
+ Polygons: 'store-polygons',
+ Temporaries: 'store-temporaries',
+ Mask: 'store-mask',
+ State: 'store-state-saver',
+};
+
+class StorageAssistant {
+ op = window.localStorage;
+
+ // get(key: string, expectedValue: string | null) -> realValue || expectedValue[string | null]
+ get = (key, expectedValue) => {
+ let rawValue = this.op.getItem(key), value;
+ try {
+ value = JSON.parse(rawValue);
+ } catch (e) {
+ value = rawValue;
+ }
+ return value || expectedValue
+ };
+
+ // set(key: string, value: string) -> void
+ set = (key, value) => {
+ if (typeof value !== "string") value = JSON.stringify(value);
+ this.op.setItem(key, value);
+ };
+
+ // delete(key: string) -> void
+ delete = (key) => {
+ this.op.removeItem(key)
+ };
+
+ // _clear() -> [Native].clear
+ _clear = this.op.clear;
+}
+
+class Crud {
+ storeName = null;
+ prefix = 'CrudStorage__';
+ sa = new StorageAssistant();
+ state = {
+ value: [],
+ };
+
+ constructor(store) {
+ if (!store) {
+ throw new Error('There\'s no Store provided to Crud constructor! (E: emp_constructor_called)');
+ }
+ if (typeof store !== 'string') {
+ throw new Error('Incorrect Store name provided to Crud constructor! (E: incorrect_store_name)');
+ }
+ this.storeName = this.prefix + store;
+ console.log('Crud::Build :', this.storeName);
+ if (!EventManager.findListeners(this.storeName).length) {
+ //console.log('There\'s no Initialization found. Initializing manually...');
+ this.init();
+ }
+ }
+
+
+ init = () => {
+
+ console.log('Crud::Init :', this.storeName);
+
+ EventManager.addListener(this.storeName, this.eventHandler, 'crud-init');
+ //document.addEventListener(this.storeName + '_update', this.eventHandler);
+ this.__purgeData();
+ };
+
+ create = (iObjectAny) => {
+ let temp = this.sa.get(this.storeName, null) || this.state;
+ console.log(temp);
+ temp.value.push(iObjectAny);
+ this.sa.set(this.storeName, temp);
+ EventManager.dispatchEvent(this.storeName, {"type": 'add'}, 'crud-create');
+ //document.dispatchEvent(new CustomEvent(this.storeName + '_update', {"detail": {"type": 'add'}}));
+ };
+
+ update = (iGeneralObjectAny) => {
+ this.sa.set(this.storeName, iGeneralObjectAny);
+ EventManager.dispatchEvent(this.storeName, {"type": 'update'}, 'crud-update');
+ //document.dispatchEvent(new CustomEvent(this.storeName + '_update', {"detail": {"type": 'update'}}));
+ };
+
+ getAll = () => {
+ return this.sa.get(this.storeName, null) || this.state;
+ };
+
+ eventHandler = (event) => {
+ // redraw()????????
+ console.log(`UPDATE: ${event.type} with`, event.detail);
+ };
+
+ __purgeData = () => {
+ console.log('purging', this.storeName, '...');
+ this.sa.delete(this.storeName)
+ }
+
+}
diff --git a/core/settings.js b/core/settings.js
index 5ed337f..88b872c 100644
--- a/core/settings.js
+++ b/core/settings.js
@@ -1,6 +1,6 @@
let FRAME_RATE = 60;
-let PIXEL_SIZE = 15;
-let CANVAS_W = 1150;
+let PIXEL_SIZE = 14;
+let CANVAS_W = 1300;
let CANVAS_H = 930;
let CANVAS_HC = Math.round(CANVAS_H / PIXEL_SIZE);
let CANVAS_WC = Math.round(CANVAS_W / PIXEL_SIZE);
@@ -12,9 +12,14 @@ const COLORS = {
BLACK: [0, 0, 0],
WHITE: [255, 255, 255],
WHAY: [190, 190, 190],
+ GRAY: [193, 193, 193],
+ DARKGRAY: [140, 140, 140],
+ LIGHTBLACK: [40, 40, 40],
+
+ _MASK: [230, 170, 190],
+
RED: [255, 110, 110],
YELLOW: [255, 246, 138],
-
GREEN: [138, 255, 154],
BLUE: [62, 78, 235],
CYAN: [99, 244, 246],
@@ -24,14 +29,18 @@ const COLORS = {
DARKGREEN: [3, 88, 30],
ORANGE: [255, 138, 28],
- GRAY: [193, 193, 193],
- DARKGRAY: [140, 140, 140],
- LIGHTBLACK: [40, 40, 40],
STROKE: [193, 193, 193],
BG: null, // to be defined
};
COLORS.BG = COLORS.WHITE;
+const STRCOLORS = {
+ STROKEBLACKA: `rgba(0,0,0,0.3)`,
+ STROKEWHITEA: `rgba(255,255,255,0.3)`,
+ STROKE: undefined,
+};
+STRCOLORS.STROKE = STRCOLORS.STROKEBLACKA;
+
const Directions = {
Up: 1,
Left: 2,
@@ -42,21 +51,27 @@ const Directions = {
const Tools = {
Pixel: 'Pixel',
Line: 'Line',
+ Rectangle: 'Rectangle',
+ Polygon: 'Polygon',
Fill: 'Fill',
Colorizer: 'Colorizer',
Clear: 'Clear',
Export: 'Export',
+ Delimiter: 'Delimiter',
+ Masking: 'Masking',
+ MaskActivate: 'MaskActivate',
};
const AlgoType = {
BRZ: 0,
+ PLAIN: 9,
};
let _SETTINGS = {
toolbar: {
width: 60,
- height: 280,
+ height: 600,
offset: {
top: 30,
left: 15,
@@ -77,7 +92,24 @@ let _SETTINGS = {
top: 10,
left: 2,
},
- toolset: [Tools.Pixel, Tools.Line, Tools.Fill, Tools.Colorizer, Tools.Clear, Tools.Export],
+ toolset: [
+ Tools.Pixel,
+ Tools.Line,
+ Tools.Rectangle,
+ Tools.Polygon,
+ Tools.Fill,
+ Tools.Colorizer,
+ Tools.Delimiter,
+ Tools.Masking,
+ Tools.MaskActivate,
+ Tools.Clear,
+ Tools.Delimiter,
+ Tools.Export],
+ toolParams: {
+ Fill: {
+ delay: 0,
+ }
+ }
},
general: {
activeArea: {
@@ -140,6 +172,9 @@ let _SETTINGS = {
model: null,
},
},
+ system: {
+ logEnabled: true,
+ },
initialState: {
activeTool: 'Line',
activeRegion: null,
@@ -148,7 +183,17 @@ let _SETTINGS = {
created: 0,
terminatedByColor: 0,
terminatedByEnd: 0,
- }
+ },
+ operationCounter: 0,
+ lastClickedColor: null,
+ maskOptions: {
+ lt: [],
+ rt: [],
+ lb: [],
+ rb: [],
+ isActive: false,
+ },
+ colorBlocked: false,
}
}
};
@@ -167,3 +212,9 @@ const _SettingsManager = new SettingsManager();
_SETTINGS.setup.initialState.currentColor = _SETTINGS.general.color.palette[_SETTINGS.general.color.current];
+if (!_SETTINGS.setup.system.logEnabled) {
+ console.log = (a) => {
+ return;
+ }
+}
+
diff --git a/core/sketch.js b/core/sketch.js
index 5c346ed..159383d 100644
--- a/core/sketch.js
+++ b/core/sketch.js
@@ -10,16 +10,20 @@ document.addEventListener("keydown", event => {
handleHotkey(event.key);
// do something
});
+document.addEventListener("color-block", event => {
+ //console.log(event);
+ STATE.colorBlocked = event.detail.value;
+});
+
-const ldm = new LDM();
+const ldm = new LDM(PolyMan);
const tbar = new Toolbar();
let [x, y] = [0, 0];
function setup() {
createCanvas(CANVAS_W, CANVAS_H);
frameRate(FRAME_RATE);
- //console.log("Setup completed");
- //console.log(CANVAS_HC, CANVAS_WC);
+ countSizes();
STATE.activeTool = Tools.Line;
STATE.currentColor = COLORS.RED;
@@ -38,6 +42,14 @@ function setup() {
STATE.activeTool = Tools.Fill;
tbar.render();
},
+ 'r': () => {
+ STATE.activeTool = Tools.Rectangle;
+ tbar.render();
+ },
+ 'm': () => {
+ STATE.activeTool = Tools.Masking;
+ tbar.render();
+ },
'c': () => {
ToolActions[Tools.Colorizer](LEFT);
tbar.render();
@@ -53,6 +65,10 @@ function setup() {
draw();
tbar.render();
setInterval(utilsFixedUpdate, 50);
+
+ // Debug: painting area size in cells
+ // console.log(_SETTINGS.general.activeArea.cellBorders);
+
noLoop();
}
@@ -65,6 +81,8 @@ function mouseReleased() {
case 'activeArea':
switch (STATE.activeTool) {
case 'Line':
+ case 'Rectangle':
+ case 'Masking':
if (mouseButton === LEFT) {
ldm.setCoord(0, x, y);
}
@@ -73,6 +91,25 @@ function mouseReleased() {
ldm.draw();
}
break;
+ case 'Polygon':
+ if (mouseButton === LEFT) {
+ ldm.setCoord(ldm.firstpoint ? 0 : 1, x, y);
+ if (!ldm.firstpoint) {
+ ldm.draw().then(() => ldm.setCoord(0, x, y));
+ } else {
+ ldm.firstpoint = false;
+ }
+ /*
+ if (figure_closed) {
+ ldm.firstpoint = true;
+ }
+ */
+ }
+ if (mouseButton === RIGHT) {
+ ldm.setCoord(0, x, y);
+ ldm.firstpoint = false;
+ }
+ break;
case 'Fill':
if (mouseButton === LEFT) {
let sc = get(mouseX, mouseY).slice(0, 3);
@@ -97,6 +134,7 @@ function mouseReleased() {
function draw() {
[x, y] = C2Pix(mouseX, mouseY);
+ //console.log(x, y);
switch (STATE.activeRegion) {
case 'activeArea':
diff --git a/core/utils.js b/core/utils.js
index 185e9a5..aacc7cd 100644
--- a/core/utils.js
+++ b/core/utils.js
@@ -22,6 +22,14 @@ const CAr2Pix = (coords = [0, 0]) => {
return [Math.round((coords[0] - CENTER_W) / PIXEL_SIZE - 0.5), -Math.round((coords[1] - CENTER_H) / PIXEL_SIZE + 0.5)];
};
+const Pix2C = (coords = [0, 0]) => {
+ //console.log('requested', coords);
+ return [
+ CANVAS_W / 2 + (coords[0] * PIXEL_SIZE),
+ CANVAS_H / 2 - PIXEL_SIZE / 2 - (coords[1] * PIXEL_SIZE)
+ ];
+};
+
function arraysEqual(a, b) {
return Array.isArray(a) &&
Array.isArray(b) &&
@@ -46,3 +54,239 @@ const isDrawable = (coords) => {
_SETTINGS.general.activeArea.cellBorders.bottom,
_SETTINGS.general.activeArea.cellBorders.top, coords[0], coords[1]);
};
+
+const intersectLines = ({from: [x1, y1], to: [x2, y2]}, {from: [x3, y3], to: [x4, y4]}) => {
+ let ua, ub, denom = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1);
+ if (denom === 0) {
+ return null;
+ }
+ ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3))/denom;
+ ub = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3))/denom;
+ return {
+ x: x1 + ua * (x2 - x1),
+ y: y1 + ua * (y2 - y1),
+ seg1: ua >= 0 && ua <= 1,
+ seg2: ub >= 0 && ub <= 1,
+ };
+};
+
+const getMaskIterableLines = () => {
+ return [
+ {from: STATE.maskOptions.lt, to: STATE.maskOptions.rt},
+ {from: STATE.maskOptions.rt, to: STATE.maskOptions.rb},
+ {from: STATE.maskOptions.rb, to: STATE.maskOptions.lb},
+ {from: STATE.maskOptions.lb, to: STATE.maskOptions.rt},
+ ]
+};
+
+class MaskClass {
+ constructor() {
+ this.__reset();
+ document.addEventListener('Mask', this.eventHandler);
+ };
+
+ eventHandler = event => {
+ console.log(event.detail);
+ if (event.detail.data) {
+ EventManager.dispatchEvent(event.detail.initiator, {
+ data: this.testPolygon(event.detail.data)
+ }, 'polygon-closed');
+ }
+ };
+
+ __reset = () => {
+ };
+
+ testPolygon = (path) => {
+ console.log(path);
+ let ins = [];
+ path.forEach((el) => {
+ getMaskIterableLines().forEach((maskline) => {
+ let d = intersectLines(el, maskline) || {seg1: false};
+ console.log(d);
+ if (d.seg1 && d.seg2) {
+ ins.push([Math.round(d.x - 0.5) + 1, Math.round(d.y)]);
+ }
+ })
+ });
+ return ins;
+ }
+}
+let Mask = new MaskClass();
+
+class PolygonContainer {
+ constructor() {
+ this.__reset();
+ }
+ globalPolygonContainer = [];
+ storage = [];
+ isClosed = true;
+ firstPointCoords = [];
+ polygonCrud = new Crud(CrudStore.Polygons);
+
+ __reset = () => {
+ this.storage = [];
+ this.isClosed = true;
+ EventManager.dispatchEvent('color-block', {value: false}, 'polygon-closed');
+ EventManager.addListener('tool-change', this.eventHandler, 'handle-tool-in-poly');
+ };
+
+ eventHandler = event => {
+ if (event.detail.value && !this.isClosed) {
+ console.log(this.storage[this.storage.length - 1].to, this.firstPointCoords);
+ EventManager.dispatchEvent('LDM', {
+ type: 'drawRaw', data: {
+ from: this.storage[this.storage.length - 1].to,
+ to: this.firstPointCoords.slice(),
+ }
+ }, 'polygon-closed');
+ this.addSegment(this.storage[this.storage.length - 1].to, this.firstPointCoords.slice());
+
+ }
+ };
+
+ addSegment = ([xs, ys], [xe, ye]) => {
+ if (this.storage.length === 0) {
+ this.firstPointCoords = [xs, ys];
+ this.isClosed = false;
+ STATE.colorBlocked = true;
+ EventManager.dispatchEvent('color-block', {value: true}, 'polygon-first-point');
+ }
+ this.storage.push({from: [xs, ys], to: [xe, ye]});
+ if (arraysEqual([xe, ye], this.firstPointCoords)) {
+ putPixel([this.firstPointCoords[0], this.firstPointCoords[1]]);
+ this.closeFigure();
+ }
+ };
+
+ oneTimeEventHandler = event => {
+ if (event.detail.data) {
+ console.log(event.detail.data);
+ event.detail.data.forEach((el) => {
+ putPixel(el, COLORS.BLACK, true);
+ });
+ }
+ };
+
+ closeFigure = () => {
+ console.log('Figure Closed');
+ this.globalPolygonContainer.push({name: 'Polygon', path: this.storage});
+ if (STATE.maskOptions.isActive) {
+ EventManager.addListener('polygon-closed-masker', this.oneTimeEventHandler, 'handle-mask-test-result');
+ EventManager.dispatchEvent('Mask', {data: this.storage}, 'polygon-closed-masker');
+
+ }
+ this.storage = [];
+ this.polygonCrud.update(this.globalPolygonContainer);
+ this.isClosed = true;
+ EventManager.dispatchEvent('color-block', {value: false}, 'polygon-closed');
+ EventManager.dispatchEvent('LDM', {type: 'reset'}, 'polygon-closed');
+ }
+}
+
+class LDM {
+ firstpoint = true;
+ coord;
+ colors;
+ mark = COLORS.WHAY;
+ polygonManager = null;
+
+ constructor(polyMan) {
+ this.__reset();
+ if (!polyMan) {
+ throw new Error('No Polygon Manager provided to LineDrawingModule');
+ }
+ this.polygonManager = polyMan;
+
+ document.addEventListener('LDM', this.eventHandler);
+ }
+
+ __reset = () => {
+ this.colors = [null, null];
+ this.coord = [[0, 0], [0, 0]];
+ this.firstpoint = true;
+ this.polygonManager?.__reset();
+ };
+
+ eventHandler = (event) => {
+ if (event.detail.type) {
+ switch (event.detail.type) {
+ case 'reset':
+ this.__reset();
+ break;
+ case 'drawRaw':
+ console.log(event.detail);
+ this.__drawRaw(event.detail.data);
+ break;
+ }
+ }
+ };
+
+ setCoord = (i, x, y) => {
+ if (i === 0 && this.colors[i] !== null && !arraysEqual(this.colors[i], this.mark)) {
+ putPixel([this.coord[i][0], this.coord[i][1]], this.colors[i]);
+ }
+
+ this.colors[i] = get(mouseX, mouseY).slice(0, 3);
+ putPixel([x, y], this.mark);
+ this.coord[i] = [x, y];
+ };
+
+ __drawRaw = async (data) => {
+ console.log('low-level event-driven draw in LDM called', data.from, data.to);
+ drawLine(data.from, data.to, STATE.currentColor, 0, [AlgoType.BRZ]);
+ };
+
+ draw = async () => {
+ this.colors = [STATE.currentColor, STATE.currentColor];
+ switch (STATE.activeTool) {
+ case 'Line':
+ drawLine(this.coord[0], this.coord[1], STATE.currentColor, 0, [AlgoType.BRZ]);
+ break;
+ case 'Rectangle':
+ drawRectangle(this.coord[0], this.coord[1], STATE.currentColor, 0, [AlgoType.BRZ]);
+ break;
+ case 'Polygon':
+ drawLine(this.coord[0], this.coord[1], STATE.currentColor, 0, [AlgoType.BRZ]);
+ this.polygonManager.addSegment(this.coord[0], this.coord[1]);
+ break;
+ case 'Masking':
+ drawRectangle(this.coord[0], this.coord[1], COLORS._MASK, 0, [AlgoType.BRZ]);
+ STATE.maskOptions.lt = [this.coord[0][0], this.coord[0][1]];
+ STATE.maskOptions.rt = [this.coord[1][0], this.coord[0][1]];
+ STATE.maskOptions.lb = [this.coord[1][0], this.coord[1][1]];
+ STATE.maskOptions.rb = [this.coord[0][0], this.coord[1][1]];
+ this.__reset();
+ break;
+ }
+ }
+}
+
+class ToolManagerClass {
+ constructor() {
+ console.log('ToolManager created')
+ }
+ changeTool = (toolName) => {
+ STATE.activeTool = toolName;
+ EventManager.dispatchEvent('tool-change', {value: true}, 'tool-changed');
+ };
+ useCallback = callbackFn => callbackFn();
+}
+let ToolManager = new ToolManagerClass();
+
+class EventManagerClass {
+ _storage = [];
+ getFormat = (eve, initiator, payload) => {
+ return {eve, initiator, payload}
+ };
+ addListener = (eve, handler, purpose) => {
+ document.addEventListener(eve, handler);
+ this._storage.push({event: eve, handler, purpose})
+ };
+ dispatchEvent = (eve, payload, initiator) =>
+ document.dispatchEvent(new CustomEvent(eve, {"detail": {...payload, initiator}}));
+ findListeners = (eve) => {
+ return this._storage.filter(el => el.event === eve)
+ }
+}
+let EventManager = new EventManagerClass();
diff --git a/index.html b/index.html
index 3077664..d2a3527 100644
--- a/index.html
+++ b/index.html
@@ -20,6 +20,7 @@
+