diff --git a/PREVIEW.jpg b/PREVIEW.jpg deleted file mode 100644 index 657f335..0000000 Binary files a/PREVIEW.jpg and /dev/null differ diff --git a/README.md b/README.md deleted file mode 100644 index 1301a1c..0000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Dotgrid - - - -Dotgrid is a grid-based vector drawing software designed to create logos, icons and type. It supports layers, the full SVG specs and additional effects such as mirroring and radial drawing. Dotgrid exports to both PNG and SVG files. - -The application was initially created for internal use, and later made available as a free and open source software. - -Learn more by reading the manual, or have a look at a tutorial video. If you need help, visit the Community. - - - -## Guide - -View the [guide](https://100r.co/site/dotgrid.html). - -## Electron Build - -``` -cd desktop -npm install -npm start -``` - -## UDP Controls - -Dotgrid can be controlled by UDP via the port `49161`. It expects messages up to 6 characters. - -- **layer** `0/1/2` -- **type** `l/c/r/z` (`l`:line, `c`:clock-wise arc, `r`: cc-wise arc, `z`: close, `.`:clear, `*`: draw) -- **from** `0-z``0-z` -- **to** `0-z``0-z` - -## Extras - -- This application supports the [Ecosystem Theme](https://github.com/hundredrabbits/Themes). -- Support this project through [Patreon](https://patreon.com/100). -- See the [License](LICENSE.md) file for license rights and limitations (MIT). -- Pull Requests are welcome! diff --git a/desktop/icon.icns b/desktop/icon.icns deleted file mode 100644 index 30c8ce0..0000000 Binary files a/desktop/icon.icns and /dev/null differ diff --git a/desktop/icon.ico b/desktop/icon.ico deleted file mode 100644 index 77bf4dc..0000000 Binary files a/desktop/icon.ico and /dev/null differ diff --git a/desktop/icon.svg b/desktop/icon.svg deleted file mode 100644 index d8337c8..0000000 --- a/desktop/icon.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/desktop/main.js b/desktop/main.js deleted file mode 100644 index 0693125..0000000 --- a/desktop/main.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict' - -/* global createWindow */ - -const { app, BrowserWindow, Menu } = require('electron') -const path = require('path') - -let isShown = true - -app.win = null - -app.on('ready', () => { - app.win = new BrowserWindow({ - width: 780, - height: 462, - minWidth: 380, - minHeight: 360, - backgroundColor: '#000', - icon: path.join(__dirname, { darwin: 'icon.icns', linux: 'icon.png', win32: 'icon.ico' }[process.platform] || 'icon.ico'), - resizable: true, - frame: process.platform !== 'darwin', - skipTaskbar: process.platform === 'darwin', - autoHideMenuBar: process.platform === 'darwin', - webPreferences: { zoomFactor: 1.0, nodeIntegration: true, backgroundThrottling: false } - }) - - app.win.loadURL(`file://${__dirname}/sources/index.html`) - // app.inspect() - - app.win.on('closed', () => { - app.quit() - }) - - app.win.on('hide', function () { - isShown = false - }) - - app.win.on('show', function () { - isShown = true - }) - - app.on('window-all-closed', () => { - app.quit() - }) - - app.on('activate', () => { - if (app.win === null) { - createWindow() - } else { - app.win.show() - } - }) -}) - -app.inspect = function () { - app.win.toggleDevTools() -} - -app.toggleFullscreen = function () { - app.win.setFullScreen(!app.win.isFullScreen()) -} - -app.toggleMenubar = function () { - app.win.setMenuBarVisibility(!app.win.isMenuBarVisible()) -} - -app.toggleVisible = function () { - if (process.platform !== 'darwin') { - if (!app.win.isMinimized()) { app.win.minimize() } else { app.win.restore() } - } else { - if (isShown && !app.win.isFullScreen()) { app.win.hide() } else { app.win.show() } - } -} - -app.injectMenu = function (menu) { - try { - Menu.setApplicationMenu(Menu.buildFromTemplate(menu)) - } catch (err) { - console.warn('Cannot inject menu.') - } -} diff --git a/desktop/package.json b/desktop/package.json deleted file mode 100644 index e153de2..0000000 --- a/desktop/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "Dotgrid", - "version": "0.1.0", - "main": "main.js", - "scripts": { - "start": "electron . --disable-gpu", - "fix": "standard --fix", - "clean": "rm -r ~/Documents/Dotgrid-darwin-x64/ ; rm -r ~/Documents/Dotgrid-linux-x64/ ; rm -r ~/Documents/Dotgrid-win32-x64/ ; echo 'cleaned build location'", - "build_osx": "electron-packager . Dotgrid --platform=darwin --arch=x64 --out ~/Documents/ --overwrite --icon=icon.icns ; echo 'Built for OSX'", - "build_linux": "electron-packager . Dotgrid --platform=linux --arch=x64 --out ~/Documents/ --overwrite --icon=icon.ico ; echo 'Built for LINUX'", - "build_win": "electron-packager . Dotgrid --platform=win32 --arch=x64 --out ~/Documents/ --overwrite --icon=icon.ico ; echo 'Built for WIN'", - "build": "npm run clean ; npm run build_osx ; npm run build_linux ; npm run build_win", - "push_osx": "~/Applications/butler push ~/Documents/Dotgrid-darwin-x64/ hundredrabbits/dotgrid:osx-64", - "push_linux": "~/Applications/butler push ~/Documents/Dotgrid-linux-x64/ hundredrabbits/dotgrid:linux-64", - "push_win": "~/Applications/butler push ~/Documents/Dotgrid-win32-x64/ hundredrabbits/dotgrid:windows-64", - "status": "~/Applications/butler status hundredrabbits/dotgrid", - "push": "npm run build ; npm run push_osx ; npm run push_linux ; npm run push_win ; npm run clean ; npm run status" - }, - "devDependencies": { - "electron": "^8.1.1", - "electron-packager": "^14.2.1" - }, - "standard": { - "globals": [ - "localStorage", - "DOMParser", - "onMessage", - "postMessage", - "FileReader", - "performance", - "Worker" - ], - "ignore": [ - "/node_modules/*" - ] - } -} diff --git a/desktop/sources/index.html b/desktop/sources/index.html deleted file mode 100644 index 18a14d0..0000000 --- a/desktop/sources/index.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dotgrid - - - - - diff --git a/desktop/sources/links/fonts.css b/desktop/sources/links/fonts.css deleted file mode 100644 index be688de..0000000 --- a/desktop/sources/links/fonts.css +++ /dev/null @@ -1,15 +0,0 @@ -/* Input */ - -@font-face { - font-family: 'input_mono_regular'; - src: url('../media/fonts/input_mono_regular.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'input_mono_medium'; - src: url('../media/fonts/input_mono_medium.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} \ No newline at end of file diff --git a/desktop/sources/links/main.css b/desktop/sources/links/main.css deleted file mode 100644 index fb924e2..0000000 --- a/desktop/sources/links/main.css +++ /dev/null @@ -1,50 +0,0 @@ -body { padding: 0px; font-family: 'input_mono_regular'; -webkit-user-select: none; overflow: hidden; transition: background-color 500ms; -webkit-app-region: drag; padding: 30px;width:calc(100vw - 60px);height:calc(100vh - 60px)} - -/* Core */ - -#guide { position: absolute;width: 300px;height: 300px; transition: opacity 150ms; -webkit-app-region: no-drag; border-radius: 3px;} -#render { display: none } -#vector { z-index: 1000;position: relative;width:300px; height:300px; } - -/* Interface */ - -#interface { font-size: 11px;line-height: 30px;text-transform: uppercase;-webkit-app-region: no-drag; transition: all 150ms; width: 100%; position:fixed; bottom:30px; left:40px; height:30px; max-width:calc(100vw - 75px); overflow: hidden;} -#interface.hidden { bottom:10px !important;opacity: 0 !important } -#interface.visible { bottom:28px !important; opacity: 1 !important} -#interface #menu { opacity: 1; position: absolute; top:0px; transition: all 250ms; z-index: 900; overflow: hidden; height:30px; width:100%;} -#interface #menu svg.icon { width:30px; height:30px; margin-right:-9px; opacity: 0.6; transition: opacity 250ms; } -#interface #menu svg.icon.inactive { opacity: 0.2 } -#interface #menu svg.icon:hover { cursor: pointer; opacity: 1.0 } -#interface #menu svg.icon:last-child { margin-right: 0; } -#interface #menu svg.icon path { fill:none; stroke-linecap: round; stroke-linejoin: round; stroke-width:12px; } -#interface #menu svg.icon.source { float:right; margin-left:-2px; margin-right:0px; } -#interface #menu svg.icon#option_color { opacity: 1.0; z-index:1001; position: relative; } -#interface #menu svg.icon#option_color:hover { opacity: 0.8 } - -#interface #picker { position: absolute; line-height: 20px; z-index: 0; width: 30px; opacity: 0; transition: all 250ms; font-size: 11px; border-radius: 3px; left: 200px; top: 0px; text-transform: uppercase; font-family: 'input_mono_medium';height:20px; padding:5px 0px;left:280px; overflow:hidden;} -#interface #picker:before { content:"#"; position: absolute; left:10px; opacity: 0; transition: opacity 500ms} -#interface #picker input { background:transparent; position: absolute; left: 20px; height: 20px; width: 60px; line-height: 20px; opacity: 0; transition: opacity 500ms; text-transform: uppercase;} -#interface #color_path { transition: all 500ms; } -#interface.picker #menu { z-index: 0 } - -#interface.picker #picker { width:30px; padding: 5px 15px; padding-right: 45px; opacity: 1; z-index: 900; width: 50px; left:200px; opacity: 1} -#interface.picker #picker:before { opacity: 1; } -#interface.picker #picker input { opacity: 1 } -#interface.picker #option_thickness { opacity: 0 !important } -#interface.picker #option_mirror { opacity: 0 !important } -#interface.picker #option_fill { opacity: 0 !important } - -/* Web Specific */ - -body.web #interface #menu #option_open { display: none; } - -/* Ready */ - -body #guide { opacity: 0; transition: opacity 500ms; } -body.ready #guide { opacity: 1 } -body #interface { opacity: 0; transition: opacity 250ms, bottom 500ms; bottom:15px; } -body.ready #interface { opacity: 1; bottom:30px; } - -@media (max-width: 560px) { - #interface #menu svg.icon.source { opacity: 0; } -} diff --git a/desktop/sources/links/reset.css b/desktop/sources/links/reset.css deleted file mode 100644 index 9f57584..0000000 --- a/desktop/sources/links/reset.css +++ /dev/null @@ -1 +0,0 @@ -* { margin:0;padding:0;border:0;outline:0;text-decoration:none;font-weight:inherit;font-style:inherit;color:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;list-style:none;border-collapse:collapse;border-spacing:0; -webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;} \ No newline at end of file diff --git a/desktop/sources/links/theme.css b/desktop/sources/links/theme.css deleted file mode 100644 index 0389a71..0000000 --- a/desktop/sources/links/theme.css +++ /dev/null @@ -1,13 +0,0 @@ -body { background:var(--background) !important; } -#picker { background-color:var(--b_inv) !important; color:var(--f_inv) !important; } -#picker:before { color:var(--f_med) !important; } -#picker input::placeholder { color:var(--f_med) !important; } -.fh { color:var(--f_high) !important; stroke:var(--f_high) !important; } -.fm { color:var(--f_med) !important; stroke:var(--f_med) !important; } -.fl { color:var(--f_low) !important; stroke:var(--f_low) !important; } -.f_inv { color:var(--f_inv) !important; stroke:var(--f_inv) !important; } -.bh { background:var(--b_high) !important; } -.bm { background:var(--b_med) !important; } -.bl { background:var(--b_low) !important; } -.b_inv { background:var(--b_inv) !important; } -.icon { color:var(--f_high) !important; stroke:var(--f_high) !important; } diff --git a/desktop/sources/media/fonts/input_mono_medium.ttf b/desktop/sources/media/fonts/input_mono_medium.ttf deleted file mode 100644 index 0d488bf..0000000 Binary files a/desktop/sources/media/fonts/input_mono_medium.ttf and /dev/null differ diff --git a/desktop/sources/media/fonts/input_mono_regular.ttf b/desktop/sources/media/fonts/input_mono_regular.ttf deleted file mode 100644 index c19c287..0000000 Binary files a/desktop/sources/media/fonts/input_mono_regular.ttf and /dev/null differ diff --git a/desktop/sources/media/icons/arc_clockwise.svg b/desktop/sources/media/icons/arc_clockwise.svg deleted file mode 100644 index e9b26c1..0000000 --- a/desktop/sources/media/icons/arc_clockwise.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/desktop/sources/media/icons/arc_reverse.svg b/desktop/sources/media/icons/arc_reverse.svg deleted file mode 100644 index 929ecc2..0000000 --- a/desktop/sources/media/icons/arc_reverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/desktop/sources/media/icons/bezier.svg b/desktop/sources/media/icons/bezier.svg deleted file mode 100644 index 61677a2..0000000 --- a/desktop/sources/media/icons/bezier.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/desktop/sources/media/icons/close.svg b/desktop/sources/media/icons/close.svg deleted file mode 100644 index a152521..0000000 --- a/desktop/sources/media/icons/close.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/desktop/sources/media/icons/export.svg b/desktop/sources/media/icons/export.svg deleted file mode 100644 index 9c910b8..0000000 --- a/desktop/sources/media/icons/export.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/desktop/sources/media/icons/line.svg b/desktop/sources/media/icons/line.svg deleted file mode 100644 index b57d3af..0000000 --- a/desktop/sources/media/icons/line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/desktop/sources/media/icons/mirror.svg b/desktop/sources/media/icons/mirror.svg deleted file mode 100644 index 30cc4f9..0000000 --- a/desktop/sources/media/icons/mirror.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/desktop/sources/scripts/client.js b/desktop/sources/scripts/client.js deleted file mode 100644 index fb01644..0000000 --- a/desktop/sources/scripts/client.js +++ /dev/null @@ -1,267 +0,0 @@ -'use strict' - -/* global Acels */ -/* global Theme */ -/* global Source */ -/* global History */ - -/* global Manager */ -/* global Renderer */ -/* global Tool */ -/* global Interface */ -/* global Picker */ -/* global Cursor */ - -/* global FileReader */ - -function Client () { - this.install = function (host) { - console.info('Client', 'Installing..') - - this.acels = new Acels(this) - this.theme = new Theme(this) - this.history = new History(this) - this.source = new Source(this) - - this.listener = new Listener(this) - this.manager = new Manager(this) - this.renderer = new Renderer(this) - this.tool = new Tool(this) - this.interface = new Interface(this) - this.picker = new Picker(this) - this.cursor = new Cursor(this) - - host.appendChild(this.renderer.el) - - document.addEventListener('mousedown', (e) => { this.cursor.down(e) }, false) - document.addEventListener('mousemove', (e) => { this.cursor.move(e) }, false) - document.addEventListener('contextmenu', (e) => { this.cursor.alt(e) }, false) - document.addEventListener('mouseup', (e) => { this.cursor.up(e) }, false) - document.addEventListener('copy', (e) => { this.copy(e) }, false) - document.addEventListener('cut', (e) => { this.cut(e) }, false) - document.addEventListener('paste', (e) => { this.paste(e) }, false) - window.addEventListener('resize', (e) => { this.onResize() }, false) - window.addEventListener('dragover', (e) => { e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy' }) - window.addEventListener('drop', this.onDrop) - - this.acels.set('File', 'New', 'CmdOrCtrl+N', () => { this.source.new() }) - this.acels.set('File', 'Open', 'CmdOrCtrl+O', () => { this.source.open('grid', this.whenOpen) }) - this.acels.set('File', 'Save', 'CmdOrCtrl+S', () => { this.source.write('dotgrid', 'grid', this.tool.export(), 'text/plain') }) - this.acels.set('File', 'Export Vector', 'CmdOrCtrl+E', () => { this.source.write('dotgrid', 'svg', this.manager.toString(), 'image/svg+xml') }) - this.acels.set('File', 'Export Image', 'CmdOrCtrl+Shift+E', () => { this.manager.toPNG(this.tool.settings.size, (dataUrl) => { this.source.write('dotgrid', 'png', dataUrl, 'image/png') }) }) - this.acels.set('History', 'Undo', 'CmdOrCtrl+Z', () => { this.tool.undo() }) - this.acels.set('History', 'Redo', 'CmdOrCtrl+Shift+Z', () => { this.tool.redo() }) - this.acels.set('Stroke', 'Line', 'A', () => { this.tool.cast('line') }) - this.acels.set('Stroke', 'Arc', 'S', () => { this.tool.cast('arc_c') }) - this.acels.set('Stroke', 'Arc Rev', 'D', () => { this.tool.cast('arc_r') }) - this.acels.set('Stroke', 'Bezier', 'F', () => { this.tool.cast('bezier') }) - this.acels.set('Stroke', 'Close', 'Z', () => { this.tool.cast('close') }) - this.acels.set('Stroke', 'Arc(full)', 'T', () => { this.tool.cast('arc_c_full') }) - this.acels.set('Stroke', 'Arc Rev(full)', 'Y', () => { this.tool.cast('arc_r_full') }) - this.acels.set('Stroke', 'Clear Selection', 'Escape', () => { this.tool.clear() }) - this.acels.set('Effect', 'Linecap', 'Q', () => { this.tool.toggle('linecap') }) - this.acels.set('Effect', 'Linejoin', 'W', () => { this.tool.toggle('linejoin') }) - this.acels.set('Effect', 'Mirror', 'E', () => { this.tool.toggle('mirror') }) - this.acels.set('Effect', 'Fill', 'R', () => { this.tool.toggle('fill') }) - this.acels.set('Effect', 'Thicker', '}', () => { this.tool.toggle('thickness', 1) }) - this.acels.set('Effect', 'Thinner', '{', () => { this.tool.toggle('thickness', -1) }) - this.acels.set('Effect', 'Thicker +5', ']', () => { this.tool.toggle('thickness', 5) }) - this.acels.set('Effect', 'Thinner -5', '[', () => { this.tool.toggle('thickness', -5) }) - this.acels.set('Manual', 'Add Point', 'Enter', () => { this.tool.addVertex(this.cursor.pos); this.renderer.update() }) - this.acels.set('Manual', 'Move Up', 'Up', () => { this.cursor.pos.y -= 15; this.renderer.update() }) - this.acels.set('Manual', 'Move Right', 'Right', () => { this.cursor.pos.x += 15; this.renderer.update() }) - this.acels.set('Manual', 'Move Down', 'Down', () => { this.cursor.pos.y += 15; this.renderer.update() }) - this.acels.set('Manual', 'Move Left', 'Left', () => { this.cursor.pos.x -= 15; this.renderer.update() }) - this.acels.set('Manual', 'Remove Point', 'Shift+Backspace', () => { this.tool.removeSegmentsAt(this.cursor.pos) }) - this.acels.set('Manual', 'Remove Segment', 'Backspace', () => { this.tool.removeSegment() }) - this.acels.set('Layers', 'Foreground', 'CmdOrCtrl+1', () => { this.tool.selectLayer(0) }) - this.acels.set('Layers', 'Middleground', 'CmdOrCtrl+2', () => { this.tool.selectLayer(1) }) - this.acels.set('Layers', 'Background', 'CmdOrCtrl+3', () => { this.tool.selectLayer(2) }) - this.acels.set('Layers', 'Merge Layers', 'CmdOrCtrl+M', () => { this.tool.merge() }) - this.acels.set('View', 'Color Picker', 'G', () => { this.picker.start() }) - this.acels.set('View', 'Toggle Grid', 'H', () => { this.renderer.toggle() }) - this.acels.install(window) - this.acels.pipe(this) - - this.manager.install() - this.interface.install(host) - this.theme.install(host, () => { this.update() }) - } - - this.start = () => { - console.log('Client', 'Starting..') - console.info(`${this.acels}`) - - this.theme.start() - this.tool.start() - this.renderer.start() - this.interface.start() - - this.source.new() - this.onResize() - - setTimeout(() => { document.body.className += ' ready' }, 250) - } - - this.update = () => { - this.manager.update() - this.interface.update() - this.renderer.update() - } - - this.clear = () => { - this.history.clear() - this.tool.reset() - this.reset() - this.renderer.update() - this.interface.update(true) - } - - this.reset = () => { - this.tool.clear() - this.update() - } - - this.whenOpen = (file, data) => { - this.tool.replace(JSON.parse(data)) - this.onResize() - } - - // Resize Tools - - this.fitSize = () => { - if (this.requireResize() === false) { return } - console.log('Client', `Will resize to: ${printSize(this.getRequiredSize())}`) - this.update() - } - - this.getPadding = () => { - return { x: 60, y: 90 } - } - - this.getWindowSize = () => { - return { width: window.innerWidth, height: window.innerHeight } - } - - this.getProjectSize = () => { - return this.tool.settings.size - } - - this.getPaddedSize = () => { - const rect = this.getWindowSize() - const pad = this.getPadding() - return { width: step(rect.width - pad.x, 15), height: step(rect.height - pad.y, 15) } - } - - this.getRequiredSize = () => { - const rect = this.getProjectSize() - const pad = this.getPadding() - return { width: step(rect.width, 15) + pad.x, height: step(rect.height, 15) + pad.y } - } - - this.requireResize = () => { - const _window = this.getWindowSize() - const _required = this.getRequiredSize() - const offset = sizeOffset(_window, _required) - if (offset.width !== 0 || offset.height !== 0) { - console.log('Client', `Require ${printSize(_required)}, but window is ${printSize(_window)}(${printSize(offset)})`) - return true - } - return false - } - - this.onResize = () => { - const _project = this.getProjectSize() - const _padded = this.getPaddedSize() - const offset = sizeOffset(_padded, _project) - if (offset.width !== 0 || offset.height !== 0) { - console.log('Client', `Resize project to ${printSize(_padded)}`) - this.tool.settings.size = _padded - } - this.update() - } - - // Events - - this.drag = function (e) { - e.preventDefault() - e.stopPropagation() - - const file = e.dataTransfer.files[0] - const filename = file.path ? file.path : file.name ? file.name : '' - - if (filename.indexOf('.grid') < 0) { console.warn('Client', 'Not a .grid file'); return } - - const reader = new FileReader() - - reader.onload = function (e) { - const data = e.target && e.target.result ? e.target.result : '' - this.source.load(filename, data) - this.fitSize() - } - reader.readAsText(file) - } - - this.onDrop = (e) => { - e.preventDefault() - e.stopPropagation() - const file = e.dataTransfer.files[0] - - if (file.name.indexOf('.grid') > -1) { - this.source.read(e.dataTransfer.files[0], this.whenOpen) - } - } - - this.copy = function (e) { - this.renderer.update() - - if (e.target !== this.picker.input) { - e.clipboardData.setData('text/source', this.tool.export(this.tool.layer())) - e.clipboardData.setData('text/plain', this.tool.path()) - e.clipboardData.setData('text/html', this.manager.el.outerHTML) - e.clipboardData.setData('text/svg+xml', this.manager.el.outerHTML) - e.preventDefault() - } - - this.renderer.update() - } - - this.cut = function (e) { - this.renderer.update() - - if (e.target !== this.picker.input) { - e.clipboardData.setData('text/source', this.tool.export(this.tool.layer())) - e.clipboardData.setData('text/plain', this.tool.export(this.tool.layer())) - e.clipboardData.setData('text/html', this.manager.el.outerHTML) - e.clipboardData.setData('text/svg+xml', this.manager.el.outerHTML) - this.tool.layers[this.tool.index] = [] - e.preventDefault() - } - - this.renderer.update() - } - - this.paste = function (e) { - if (e.target !== this.picker.el) { - let data = e.clipboardData.getData('text/source') - if (isJson(data)) { - data = JSON.parse(data.trim()) - this.tool.import(data) - } - e.preventDefault() - } - - this.renderer.update() - } - - this.onKeyDown = (e) => { - } - - this.onKeyUp = (e) => { - } - - function sizeOffset (a, b) { return { width: a.width - b.width, height: a.height - b.height } } - function printSize (size) { return `${size.width}x${size.height}` } - function isJson (text) { try { JSON.parse(text); return true } catch (error) { return false } } - function step (v, s) { return Math.round(v / s) * s } -} diff --git a/desktop/sources/scripts/cursor.js b/desktop/sources/scripts/cursor.js deleted file mode 100644 index cd333ac..0000000 --- a/desktop/sources/scripts/cursor.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict' - -function Cursor (client) { - this.pos = { x: 0, y: 0 } - this.lastPos = { x: 0, y: 0 } - this.translation = null - this.operation = null - - this.translate = function (from = null, to = null, multi = false, copy = false, layer = false) { - if ((from || to) && this.translation === null) { this.translation = { multi: multi, copy: copy, layer: layer } } - if (from) { this.translation.from = from } - if (to) { this.translation.to = to } - if (!from && !to) { - this.translation = null - } - } - - this.down = function (e) { - this.pos = this.atEvent(e) - if (client.tool.vertexAt(this.pos)) { - this.translate(this.pos, this.pos, e.shiftKey, e.ctrlKey || e.metaKey, e.altKey) - } - client.renderer.update() - client.interface.update() - e.preventDefault() - } - - this.move = function (e) { - this.pos = this.atEvent(e) - if (this.translation) { - this.translate(null, this.pos) - } - if (this.lastPos.x !== this.pos.x || this.lastPos.y !== this.pos.y) { - client.renderer.update() - } - client.interface.update() - this.lastPos = this.pos - e.preventDefault() - } - - this.up = function (e) { - this.pos = this.atEvent(e) - if (this.translation && !isEqual(this.translation.from, this.translation.to)) { - if (this.translation.layer === true) { client.tool.translateLayer(this.translation.from, this.translation.to) } else if (this.translation.copy) { client.tool.translateCopy(this.translation.from, this.translation.to) } else if (this.translation.multi) { client.tool.translateMulti(this.translation.from, this.translation.to) } else { client.tool.translate(this.translation.from, this.translation.to) } - } else if (e.target.id === 'guide') { - client.tool.addVertex({ x: this.pos.x, y: this.pos.y }) - client.picker.stop() - } - this.translate() - client.interface.update() - client.renderer.update() - e.preventDefault() - } - - this.alt = function (e) { - this.pos = this.atEvent(e) - client.tool.removeSegmentsAt(this.pos) - e.preventDefault() - setTimeout(() => { - client.tool.clear() - }, 150) - } - - this.atEvent = function (e) { - return this.snapPos(this.relativePos({ x: e.clientX, y: e.clientY })) - } - - this.relativePos = function (pos) { - return { - x: pos.x - client.renderer.el.offsetLeft, - y: pos.y - client.renderer.el.offsetTop - } - } - - this.snapPos = function (pos) { - return { - x: clamp(step(pos.x, 15), 15, client.tool.settings.size.width - 15), - y: clamp(step(pos.y, 15), 15, client.tool.settings.size.height - 15) - } - } - - function isEqual (a, b) { return a.x === b.x && a.y === b.y } - function clamp (v, min, max) { return v < min ? min : v > max ? max : v } - function step (v, s) { return Math.round(v / s) * s } -} diff --git a/desktop/sources/scripts/generator.js b/desktop/sources/scripts/generator.js deleted file mode 100644 index 8304325..0000000 --- a/desktop/sources/scripts/generator.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict' - -/* global client */ - -function Generator (layer, style) { - this.layer = layer - this.style = style - - function operate (layer, offset, scale, mirror = 0, angle = 0) { - const l = copy(layer) - - for (const k1 in l) { - const seg = l[k1] - for (const k2 in seg.vertices) { - if (mirror === 1 || mirror === 3) { seg.vertices[k2].x = (client.tool.settings.size.width) - seg.vertices[k2].x } - if (mirror === 2 || mirror === 3) { seg.vertices[k2].y = (client.tool.settings.size.height) - seg.vertices[k2].y } - // Offset - seg.vertices[k2].x += offset.x - seg.vertices[k2].y += offset.y - // Rotate - const center = { x: (client.tool.settings.size.width / 2) + offset.x + (7.5), y: (client.tool.settings.size.height / 2) + offset.y + 30 } - seg.vertices[k2] = rotatePoint(seg.vertices[k2], center, angle) - // Scale - seg.vertices[k2].x *= scale - seg.vertices[k2].y *= scale - } - } - return l - } - - this.render = function (prev, segment, mirror = 0) { - const type = segment.type - const vertices = segment.vertices - let html = '' - let skip = 0 - - for (const id in vertices) { - if (skip > 0) { skip -= 1; continue } - - const vertex = vertices[parseInt(id)] - const next = vertices[parseInt(id) + 1] - const afterNext = vertices[parseInt(id) + 2] - - if (parseInt(id) === 0 && !prev) { - html += `M${vertex.x},${vertex.y} ` - } else if (parseInt(id) === 0 && prev && (prev.x !== vertex.x || prev.y !== vertex.y)) { - html += `M${vertex.x},${vertex.y} ` - } - - if (type === 'line') { - html += this._line(vertex) - } else if (type === 'arc_c') { - const clock = mirror > 0 && mirror < 3 ? '0,0' : '0,1' - html += this._arc(vertex, next, clock) - } else if (type === 'arc_r') { - const clock = mirror > 0 && mirror < 3 ? '0,1' : '0,0' - html += this._arc(vertex, next, clock) - } else if (type === 'arc_c_full') { - const clock = mirror > 0 ? '1,0' : '1,1' - html += this._arc(vertex, next, clock) - } else if (type === 'arc_r_full') { - const clock = mirror > 0 ? '1,1' : '1,0' - html += this._arc(vertex, next, clock) - } else if (type === 'bezier') { - html += this._bezier(next, afterNext) - skip = 1 - } - } - - if (segment.type === 'close') { - html += 'Z ' - } - - return html - } - - this._line = function (a) { - return `L${a.x},${a.y} ` - } - - this._arc = function (a, b, c) { - if (!a || !b || !c) { return '' } - - const offset = { x: b.x - a.x, y: b.y - a.y } - - if (offset.x === 0 || offset.y === 0) { return this._line(b) } - return `A${Math.abs(b.x - a.x)},${Math.abs(b.y - a.y)} 0 ${c} ${b.x},${b.y} ` - } - - this._bezier = function (a, b) { - if (!a || !b) { return '' } - return `Q${a.x},${a.y} ${b.x},${b.y} ` - } - - this.convert = function (layer, mirror, angle) { - let s = '' - let prev = null - for (const id in layer) { - const seg = layer[parseInt(id)] - s += `${this.render(prev, seg, mirror)}` - prev = seg.vertices ? seg.vertices[seg.vertices.length - 1] : null - } - return s - } - - this.toString = function (offset = { x: 0, y: 0 }, scale = 1, mirror = this.style && this.style.mirror_style ? this.style.mirror_style : 0) { - let s = this.convert(operate(this.layer, offset, scale)) - - if (mirror === 1 || mirror === 2 || mirror === 3) { - s += this.convert(operate(this.layer, offset, scale, mirror), mirror) - } - - return s - } - - function copy (data) { return data ? JSON.parse(JSON.stringify(data)) : [] } - function rotatePoint (point, origin, angle) { angle = angle * Math.PI / 180.0; return { x: (Math.cos(angle) * (point.x - origin.x) - Math.sin(angle) * (point.y - origin.y) + origin.x).toFixed(1), y: (Math.sin(angle) * (point.x - origin.x) + Math.cos(angle) * (point.y - origin.y) + origin.y).toFixed(1) } } -} diff --git a/desktop/sources/scripts/interface.js b/desktop/sources/scripts/interface.js deleted file mode 100644 index 3bedd8d..0000000 --- a/desktop/sources/scripts/interface.js +++ /dev/null @@ -1,159 +0,0 @@ -'use strict' - -function Interface (client) { - this.el = document.createElement('div') - this.el.id = 'interface' - - this.el.appendChild(this.menu_el = document.createElement('div')) - this.menu_el.id = 'menu' - - this.isVisible = true - this.zoom = false - - this.install = function (host) { - host.appendChild(this.el) - } - - this.start = function (host) { - let html = '' - const options = { - cast: { - line: { key: 'A', icon: 'M60,60 L240,240' }, - arc_c: { key: 'S', icon: 'M60,60 A180,180 0 0,1 240,240' }, - arc_r: { key: 'D', icon: 'M60,60 A180,180 0 0,0 240,240' }, - bezier: { key: 'F', icon: 'M60,60 Q60,150 150,150 Q240,150 240,240' }, - close: { key: 'Z', icon: 'M60,60 A180,180 0 0,1 240,240 M60,60 A180,180 0 0,0 240,240' } - }, - toggle: { - linecap: { key: 'Q', icon: 'M60,60 L60,60 L180,180 L240,180 L240,240 L180,240 L180,180' }, - linejoin: { key: 'W', icon: 'M60,60 L120,120 L180,120 M120,180 L180,180 L240,240' }, - thickness: { key: '', icon: 'M120,90 L120,90 L90,120 L180,210 L210,180 Z M105,105 L105,105 L60,60 M195,195 L195,195 L240,240' }, - mirror: { key: 'E', icon: 'M60,60 L60,60 L120,120 M180,180 L180,180 L240,240 M210,90 L210,90 L180,120 M120,180 L120,180 L90,210' }, - fill: { key: 'R', icon: 'M60,60 L60,150 L150,150 L240,150 L240,240 Z' } - }, - misc: { - color: { key: 'G', icon: 'M150,60 A90,90 0 0,1 240,150 A-90,90 0 0,1 150,240 A-90,-90 0 0,1 60,150 A90,-90 0 0,1 150,60' } - }, - source: { - open: { key: 'c-O', icon: 'M155,65 A90,90 0 0,1 245,155 A90,90 0 0,1 155,245 A90,90 0 0,1 65,155 A90,90 0 0,1 155,65 M155,95 A60,60 0 0,1 215,155 A60,60 0 0,1 155,215 A60,60 0 0,1 95,155 A60,60 0 0,1 155,95 ' }, - render: { key: 'c-R', icon: 'M155,65 A90,90 0 0,1 245,155 A90,90 0 0,1 155,245 A90,90 0 0,1 65,155 A90,90 0 0,1 155,65 M110,155 L110,155 L200,155 ' }, - export: { key: 'c-E', icon: 'M155,65 A90,90 0 0,1 245,155 A90,90 0 0,1 155,245 A90,90 0 0,1 65,155 A90,90 0 0,1 155,65 M110,140 L110,140 L200,140 M110,170 L110,170 L200,170' }, - save: { key: 'c-S', icon: 'M155,65 A90,90 0 0,1 245,155 A90,90 0 0,1 155,245 A90,90 0 0,1 65,155 A90,90 0 0,1 155,65 M110,155 L110,155 L200,155 M110,185 L110,185 L200,185 M110,125 L110,125 L200,125' }, - grid: { key: 'H', icon: 'M65,155 Q155,245 245,155 M65,155 Q155,65 245,155 M155,125 A30,30 0 0,1 185,155 A30,30 0 0,1 155,185 A30,30 0 0,1 125,155 A30,30 0 0,1 155,125 ' } - } - } - - for (const type in options) { - const tools = options[type] - for (const name in tools) { - const tool = tools[name] - html += ` - - ${name === 'depth' ? '' : ''} - - ${capitalize(name)}${tool.key ? '(' + tool.key + ')' : ''} - - ` - } - } - this.menu_el.innerHTML = html - this.menu_el.appendChild(client.picker.el) - } - - this.over = function (type, name) { - client.cursor.operation = {} - client.cursor.operation[type] = name - this.update(true) - client.renderer.update(true) - } - - this.out = function (type, name) { - client.cursor.operation = '' - client.renderer.update(true) - } - - this.up = function (type, name) { - if (!client.tool[type]) { console.warn(`Unknown option(type): ${type}.${name}`, client.tool); return } - - this.update(true) - client.renderer.update(true) - } - - this.down = function (type, name, event) { - if (!client.tool[type]) { console.warn(`Unknown option(type): ${type}.${name}`, client.tool); return } - const mod = event.button === 2 ? -1 : 1 - client.tool[type](name, mod) - this.update(true) - client.renderer.update(true) - } - - this.prev_operation = null - - this.update = function (force = false, id) { - if (this.prev_operation === client.cursor.operation && force === false) { return } - - let multiVertices = null - const segments = client.tool.layer() - const sumSegments = client.tool.length() - - for (const i in segments) { - if (segments[i].vertices.length > 2) { multiVertices = true; break } - } - - document.getElementById('option_line').className.baseVal = !client.tool.canCast('line') ? 'icon inactive' : 'icon' - document.getElementById('option_arc_c').className.baseVal = !client.tool.canCast('arc_c') ? 'icon inactive' : 'icon' - document.getElementById('option_arc_r').className.baseVal = !client.tool.canCast('arc_r') ? 'icon inactive' : 'icon' - document.getElementById('option_bezier').className.baseVal = !client.tool.canCast('bezier') ? 'icon inactive' : 'icon' - document.getElementById('option_close').className.baseVal = !client.tool.canCast('close') ? 'icon inactive' : 'icon' - - document.getElementById('option_thickness').className.baseVal = client.tool.layer().length < 1 ? 'icon inactive' : 'icon' - document.getElementById('option_linecap').className.baseVal = client.tool.layer().length < 1 ? 'icon inactive' : 'icon' - document.getElementById('option_linejoin').className.baseVal = client.tool.layer().length < 1 || !multiVertices ? 'icon inactive' : 'icon' - document.getElementById('option_mirror').className.baseVal = client.tool.layer().length < 1 ? 'icon inactive' : 'icon' - document.getElementById('option_fill').className.baseVal = client.tool.layer().length < 1 ? 'icon inactive' : 'icon' - - document.getElementById('option_color').children[0].style.fill = client.tool.style().color - document.getElementById('option_color').children[0].style.stroke = client.tool.style().color - document.getElementById('option_color').className.baseVal = 'icon' - - // Source - - document.getElementById('option_save').className.baseVal = sumSegments < 1 ? 'icon inactive source' : 'icon source' - document.getElementById('option_export').className.baseVal = sumSegments < 1 ? 'icon inactive source' : 'icon source' - document.getElementById('option_render').className.baseVal = sumSegments < 1 ? 'icon inactive source' : 'icon source' - - document.getElementById('option_grid').className.baseVal = client.renderer.showExtras ? 'icon inactive source' : 'icon source' - - // Grid - if (client.renderer.showExtras) { document.getElementById('grid_path').setAttribute('d', 'M65,155 Q155,245 245,155 M65,155 Q155,65 245,155 M155,125 A30,30 0 0,1 185,155 A30,30 0 0,1 155,185 A30,30 0 0,1 125,155 A30,30 0 0,1 155,125 ') } else { document.getElementById('grid_path').setAttribute('d', 'M65,155 Q155,245 245,155 M65,155 ') } - - // Mirror - if (client.tool.style().mirror_style === 0) { document.getElementById('mirror_path').setAttribute('d', 'M60,60 L60,60 L120,120 M180,180 L180,180 L240,240 M210,90 L210,90 L180,120 M120,180 L120,180 L90,210') } else if (client.tool.style().mirror_style === 1) { document.getElementById('mirror_path').setAttribute('d', 'M60,60 L240,240 M180,120 L210,90 M120,180 L90,210') } else if (client.tool.style().mirror_style === 2) { document.getElementById('mirror_path').setAttribute('d', 'M210,90 L210,90 L90,210 M60,60 L60,60 L120,120 M180,180 L180,180 L240,240') } else if (client.tool.style().mirror_style === 3) { document.getElementById('mirror_path').setAttribute('d', 'M60,60 L60,60 L120,120 L180,120 L210,90 M240,240 L240,240 L180,180 L120,180 L90,210') } else if (client.tool.style().mirror_style === 4) { document.getElementById('mirror_path').setAttribute('d', 'M120,120 L120,120 L120,120 L180,120 M120,150 L120,150 L180,150 M120,180 L120,180 L180,180 L180,180 L180,180 L240,240 M120,210 L120,210 L180,210 M120,90 L120,90 L180,90 M60,60 L60,60 L120,120 ') } - - this.prev_operation = client.cursor.operation - } - - this.toggle = function () { - this.isVisible = !this.isVisible - this.el.className = this.isVisible ? 'visible' : 'hidden' - } - - document.onkeydown = function (e) { - if (e.key === 'Tab') { - client.interface.toggle() - e.preventDefault() - } - } - - function capitalize (str) { - return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase() - } -} diff --git a/desktop/sources/scripts/lib/acels.js b/desktop/sources/scripts/lib/acels.js deleted file mode 100644 index 62f630c..0000000 --- a/desktop/sources/scripts/lib/acels.js +++ /dev/null @@ -1,136 +0,0 @@ -'use strict' - -function Acels (client) { - this.all = {} - this.roles = {} - this.pipe = null - - this.install = (host = window) => { - host.addEventListener('keydown', this.onKeyDown, false) - host.addEventListener('keyup', this.onKeyUp, false) - } - - this.set = (cat, name, accelerator, downfn, upfn) => { - if (this.all[accelerator]) { console.warn('Acels', `Trying to overwrite ${this.all[accelerator].name}, with ${name}.`) } - this.all[accelerator] = { cat, name, downfn, upfn, accelerator } - } - - this.add = (cat, role) => { - this.all[':' + role] = { cat, name: role, role } - } - - this.get = (accelerator) => { - return this.all[accelerator] - } - - this.sort = () => { - const h = {} - for (const item of Object.values(this.all)) { - if (!h[item.cat]) { h[item.cat] = [] } - h[item.cat].push(item) - } - return h - } - - this.convert = (event) => { - const accelerator = event.key === ' ' ? 'Space' : event.key.substr(0, 1).toUpperCase() + event.key.substr(1) - if ((event.ctrlKey || event.metaKey) && event.shiftKey) { - return `CmdOrCtrl+Shift+${accelerator}` - } - if (event.shiftKey && event.key.toUpperCase() !== event.key) { - return `Shift+${accelerator}` - } - if (event.altKey && event.key.length !== 1) { - return `Alt+${accelerator}` - } - if (event.ctrlKey || event.metaKey) { - return `CmdOrCtrl+${accelerator}` - } - return accelerator - } - - this.pipe = (obj) => { - this.pipe = obj - } - - this.onKeyDown = (e) => { - const target = this.get(this.convert(e)) - if (!target || !target.downfn) { return this.pipe ? this.pipe.onKeyDown(e) : null } - target.downfn() - e.preventDefault() - } - - this.onKeyUp = (e) => { - const target = this.get(this.convert(e)) - if (!target || !target.upfn) { return this.pipe ? this.pipe.onKeyUp(e) : null } - target.upfn() - e.preventDefault() - } - - this.toMarkdown = () => { - const cats = this.sort() - let text = '' - for (const cat in cats) { - text += `\n### ${cat}\n\n` - for (const item of cats[cat]) { - text += item.accelerator ? `- \`${item.accelerator.replace('`', 'tilde')}\`: ${item.name}\n` : '' - } - } - return text.trim() - } - - this.toString = () => { - const cats = this.sort() - let text = '' - for (const cat in cats) { - text += `\n${cat}\n\n` - for (const item of cats[cat]) { - text += item.accelerator ? `${item.name.padEnd(25, '.')} ${item.accelerator}\n` : '' - } - } - return text.trim() - } - - // Electron specifics - - this.inject = (name = 'Untitled') => { - const app = require('electron').remote.app - const injection = [] - - injection.push({ - label: name, - submenu: [ - { label: 'About', click: () => { require('electron').shell.openExternal('https://github.com/hundredrabbits/' + name) } }, - { - label: 'Theme', - submenu: [ - { label: 'Download Themes', click: () => { require('electron').shell.openExternal('https://github.com/hundredrabbits/Themes') } }, - { label: 'Open Theme', click: () => { client.theme.open() } }, - { label: 'Reset Theme', accelerator: 'CmdOrCtrl+Escape', click: () => { client.theme.reset() } } - ] - }, - { label: 'Fullscreen', accelerator: 'CmdOrCtrl+Enter', click: () => { app.toggleFullscreen() } }, - { label: 'Hide', accelerator: 'CmdOrCtrl+H', click: () => { app.toggleVisible() } }, - { label: 'Toggle Menubar', accelerator: 'Alt+H', click: () => { app.toggleMenubar() } }, - { label: 'Inspect', accelerator: 'CmdOrCtrl+Tab', click: () => { app.inspect() } }, - { role: 'quit' } - ] - }) - - const sorted = this.sort() - for (const cat of Object.keys(sorted)) { - const submenu = [] - for (const option of sorted[cat]) { - if (option.role) { - submenu.push({ role: option.role }) - } else if (option.type) { - submenu.push({ type: option.type }) - } else { - submenu.push({ label: option.name, accelerator: option.accelerator, click: option.downfn }) - } - } - injection.push({ label: cat, submenu: submenu }) - } - app.injectMenu(injection) - } -} diff --git a/desktop/sources/scripts/lib/history.js b/desktop/sources/scripts/lib/history.js deleted file mode 100644 index 202da3e..0000000 --- a/desktop/sources/scripts/lib/history.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict' - -function History () { - this.index = 0 - this.a = [] - - this.clear = function () { - this.a = [] - this.index = 0 - } - - this.push = function (data) { - if (this.index < this.a.length - 1) { - this.fork() - } - this.index = this.a.length - this.a = this.a.slice(0, this.index) - this.a.push(copy(data)) - - if (this.a.length > 20) { - this.a.shift() - } - } - - this.fork = function () { - this.a = this.a.slice(0, this.index + 1) - } - - this.pop = function () { - return this.a.pop() - } - - this.prev = function () { - this.index = clamp(this.index - 1, 0, this.a.length - 1) - return copy(this.a[this.index]) - } - - this.next = function () { - this.index = clamp(this.index + 1, 0, this.a.length - 1) - return copy(this.a[this.index]) - } - - function copy (data) { return data ? JSON.parse(JSON.stringify(data)) : [] } - function clamp (v, min, max) { return v < min ? min : v > max ? max : v } -} diff --git a/desktop/sources/scripts/lib/source.js b/desktop/sources/scripts/lib/source.js deleted file mode 100644 index abbdf89..0000000 --- a/desktop/sources/scripts/lib/source.js +++ /dev/null @@ -1,102 +0,0 @@ -'use strict' - -/* global FileReader */ -/* global MouseEvent */ - -function Source (client) { - this.cache = {} - - this.install = () => { - } - - this.start = () => { - this.new() - } - - this.new = () => { - console.log('Source', 'New file..') - this.cache = {} - } - - this.open = (ext, callback, store = false) => { - console.log('Source', 'Open file..') - const input = document.createElement('input') - input.type = 'file' - input.onchange = (e) => { - const file = e.target.files[0] - if (file.name.indexOf('.' + ext) < 0) { console.warn('Source', `Skipped ${file.name}`); return } - this.read(file, callback, store) - } - input.click() - } - - this.load = (ext, callback) => { - console.log('Source', 'Load files..') - const input = document.createElement('input') - input.type = 'file' - input.setAttribute('multiple', 'multiple') - input.onchange = (e) => { - for (const file of e.target.files) { - if (file.name.indexOf('.' + ext) < 0) { console.warn('Source', `Skipped ${file.name}`); return } - this.read(file, this.store) - } - } - input.click() - } - - this.store = (file, content) => { - console.info('Source', 'Stored ' + file.name) - this.cache[file.name] = content - } - - this.save = (name, content, type = 'text/plain', callback) => { - this.saveAs(name, content, type, callback) - } - - this.saveAs = (name, ext, content, type = 'text/plain', callback) => { - console.log('Source', 'Save new file..') - this.write(name, ext, content, type, callback) - } - - // I/O - - this.read = (file, callback, store = false) => { - const reader = new FileReader() - reader.onload = (event) => { - const res = event.target.result - if (callback) { callback(file, res) } - if (store) { this.store(file, res) } - } - reader.readAsText(file, 'UTF-8') - } - - this.write = (name, ext, content, type, settings = 'charset=utf-8') => { - const link = document.createElement('a') - link.setAttribute('download', `${name}-${timestamp()}.${ext}`) - if (type === 'image/png' || type === 'image/jpeg') { - link.setAttribute('href', content) - } else { - link.setAttribute('href', 'data:' + type + ';' + settings + ',' + encodeURIComponent(content)) - } - link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })) - } - - function timestamp (d = new Date(), e = new Date(d)) { - return `${arvelie()}-${neralie()}` - } - - function arvelie (date = new Date()) { - const start = new Date(date.getFullYear(), 0, 0) - const diff = (date - start) + ((start.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1000) - const doty = Math.floor(diff / 86400000) - 1 - const y = date.getFullYear().toString().substr(2, 2) - const m = doty === 364 || doty === 365 ? '+' : String.fromCharCode(97 + Math.floor(doty / 14)).toUpperCase() - const d = `${(doty === 365 ? 1 : doty === 366 ? 2 : (doty % 14)) + 1}`.padStart(2, '0') - return `${y}${m}${d}` - } - - function neralie (d = new Date(), e = new Date(d)) { - const ms = e - d.setHours(0, 0, 0, 0) - return (ms / 8640 / 10000).toFixed(6).substr(2, 6) - } -} diff --git a/desktop/sources/scripts/lib/theme.js b/desktop/sources/scripts/lib/theme.js deleted file mode 100644 index 0556fa0..0000000 --- a/desktop/sources/scripts/lib/theme.js +++ /dev/null @@ -1,170 +0,0 @@ -'use strict' - -/* global localStorage */ -/* global FileReader */ -/* global DOMParser */ - -function Theme (client) { - this.el = document.createElement('style') - this.el.type = 'text/css' - - this.active = {} - this.default = { - background: '#eeeeee', - f_high: '#0a0a0a', - f_med: '#4a4a4a', - f_low: '#6a6a6a', - f_inv: '#111111', - b_high: '#a1a1a1', - b_med: '#c1c1c1', - b_low: '#ffffff', - b_inv: '#ffb545' - } - - // Callbacks - this.onLoad = () => {} - - this.install = (host = document.body) => { - window.addEventListener('dragover', this.drag) - window.addEventListener('drop', this.drop) - host.appendChild(this.el) - } - - this.start = () => { - console.log('Theme', 'Starting..') - if (isJson(localStorage.theme)) { - const storage = JSON.parse(localStorage.theme) - if (isValid(storage)) { - console.log('Theme', 'Loading theme in localStorage..') - this.load(storage) - return - } - } - this.load(this.default) - } - - this.open = () => { - console.log('Theme', 'Open theme..') - const input = document.createElement('input') - input.type = 'file' - input.onchange = (e) => { - this.read(e.target.files[0], this.load) - } - input.click() - } - - this.load = (data) => { - const theme = this.parse(data) - if (!isValid(theme)) { console.warn('Theme', 'Invalid format'); return } - console.log('Theme', 'Loaded theme!') - this.el.innerHTML = `:root { - --background: ${theme.background}; - --f_high: ${theme.f_high}; - --f_med: ${theme.f_med}; - --f_low: ${theme.f_low}; - --f_inv: ${theme.f_inv}; - --b_high: ${theme.b_high}; - --b_med: ${theme.b_med}; - --b_low: ${theme.b_low}; - --b_inv: ${theme.b_inv}; - }` - localStorage.setItem('theme', JSON.stringify(theme)) - this.active = theme - if (this.onLoad) { - this.onLoad(data) - } - } - - this.reset = () => { - this.load(this.default) - } - - this.set = (key, val) => { - if (!val) { return } - const hex = (`${val}`.substr(0, 1) !== '#' ? '#' : '') + `${val}` - if (!isColor(hex)) { console.warn('Theme', `${hex} is not a valid color.`); return } - this.active[key] = hex - } - - this.read = (key) => { - return this.active[key] - } - - this.parse = (any) => { - if (isValid(any)) { return any } - if (isJson(any)) { return JSON.parse(any) } - if (isHtml(any)) { return extract(any) } - } - - // Drag - - this.drag = (e) => { - e.stopPropagation() - e.preventDefault() - e.dataTransfer.dropEffect = 'copy' - } - - this.drop = (e) => { - e.preventDefault() - const file = e.dataTransfer.files[0] - if (file.name.indexOf('.svg') > -1) { - this.read(file, this.load) - } - e.stopPropagation() - } - - this.read = (file, callback) => { - const reader = new FileReader() - reader.onload = (event) => { - callback(event.target.result) - } - reader.readAsText(file, 'UTF-8') - } - - // Helpers - - function extract (xml) { - const svg = new DOMParser().parseFromString(xml, 'text/xml') - try { - return { - background: svg.getElementById('background').getAttribute('fill'), - f_high: svg.getElementById('f_high').getAttribute('fill'), - f_med: svg.getElementById('f_med').getAttribute('fill'), - f_low: svg.getElementById('f_low').getAttribute('fill'), - f_inv: svg.getElementById('f_inv').getAttribute('fill'), - b_high: svg.getElementById('b_high').getAttribute('fill'), - b_med: svg.getElementById('b_med').getAttribute('fill'), - b_low: svg.getElementById('b_low').getAttribute('fill'), - b_inv: svg.getElementById('b_inv').getAttribute('fill') - } - } catch (err) { - console.warn('Theme', 'Incomplete SVG Theme', err) - } - } - - function isValid (json) { - if (!json) { return false } - if (!json.background || !isColor(json.background)) { return false } - if (!json.f_high || !isColor(json.f_high)) { return false } - if (!json.f_med || !isColor(json.f_med)) { return false } - if (!json.f_low || !isColor(json.f_low)) { return false } - if (!json.f_inv || !isColor(json.f_inv)) { return false } - if (!json.b_high || !isColor(json.b_high)) { return false } - if (!json.b_med || !isColor(json.b_med)) { return false } - if (!json.b_low || !isColor(json.b_low)) { return false } - if (!json.b_inv || !isColor(json.b_inv)) { return false } - return true - } - - function isColor (hex) { - return /^#([0-9A-F]{3}){1,2}$/i.test(hex) - } - - function isJson (text) { - try { JSON.parse(text); return true } catch (error) { return false } - } - - function isHtml (text) { - try { new DOMParser().parseFromString(text, 'text/xml'); return true } catch (error) { return false } - } -} diff --git a/desktop/sources/scripts/listener.js b/desktop/sources/scripts/listener.js deleted file mode 100644 index 80a36f2..0000000 --- a/desktop/sources/scripts/listener.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict' - -// Dotgrid UDP Listener -// Ex: 1a0156(6 characters 0-9a-z) - -// 0 layer[0-2] -// 1 type[lcrd*] -// 2 from[0-z][0-z] -// 4 to[0-z][0-z] - -const dgram = require('dgram') - -function Listener (dotgrid) { - this.server = dgram ? dgram.createSocket('udp4') : { on: () => {}, bind: () => {} } - - function base36 (c) { - return clamp(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y'].indexOf(c.toLowerCase()), 0, 36) * 15 + 15 - } - - function clear () { - dotgrid.tool.erase() - } - - function operate (data) { - if (!data) { return } - if (!dotgrid.tool.layers[data.layer]) { return } - dotgrid.tool.addSegment(data.type, [data.from, data.to], data.layer) - } - - function draw () { - dotgrid.renderer.update() - } - - function parse (msg) { - if (msg === '') { - return draw() - } - if (['0', '1', '2'].indexOf(msg) > -1) { - return clear() - } - const layer = parseInt(msg.substr(0, 1)) - const type = { l: 'line', c: 'arc_c', r: 'arc_r', '*': 'draw' }[msg.substr(1, 1).toLowerCase()] - const from = { x: base36(msg.substr(2, 1)), y: base36(msg.substr(3, 1)) } - const to = { x: base36(msg.substr(4, 1)), y: base36(msg.substr(5, 1)) } - return { layer: layer, type: type, from: from, to: to } - } - - function clamp (v, min, max) { - return v < min ? min : v > max ? max : v - } - - // Server - - this.server.on('message', (msg, rinfo) => { - operate(parse(`${msg}`)) - }) - - this.server.on('listening', () => { - const address = this.server.address() - console.log(`Server listening for UDP:\n ${address.address}:${address.port}`) - }) - - this.server.on('error', (err) => { - console.log(`Server error:\n ${err.stack}`) - this.server.close() - }) - - this.server.bind(49161) -} diff --git a/desktop/sources/scripts/manager.js b/desktop/sources/scripts/manager.js deleted file mode 100644 index 34527a4..0000000 --- a/desktop/sources/scripts/manager.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict' - -/* global XMLSerializer */ -/* global btoa */ -/* global Image */ -/* global Blob */ - -function Manager (client) { - // Create SVG parts - this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg') - this.el.setAttribute('xmlns', 'http://www.w3.org/2000/svg') - this.el.setAttribute('baseProfile', 'full') - this.el.setAttribute('version', '1.1') - this.el.style.fill = 'none' - - this.layers = [] - - this.install = function () { - this.el.appendChild(this.layers[2] = document.createElementNS('http://www.w3.org/2000/svg', 'path')) - this.el.appendChild(this.layers[1] = document.createElementNS('http://www.w3.org/2000/svg', 'path')) - this.el.appendChild(this.layers[0] = document.createElementNS('http://www.w3.org/2000/svg', 'path')) - } - - this.update = function () { - this.el.setAttribute('width', (client.tool.settings.size.width) + 'px') - this.el.setAttribute('height', (client.tool.settings.size.height) + 'px') - this.el.style.width = (client.tool.settings.size.width) - this.el.style.height = client.tool.settings.size.height - - const styles = client.tool.styles - const paths = client.tool.paths() - - for (const id in this.layers) { - const style = styles[id] - const path = paths[id] - const layer = this.layers[id] - - layer.style.strokeWidth = style.thickness - layer.style.strokeLinecap = style.strokeLinecap - layer.style.strokeLinejoin = style.strokeLinejoin - layer.style.stroke = style.color - layer.style.fill = style.fill - - layer.setAttribute('d', path) - } - } - - this.svg64 = function () { - const xml = new XMLSerializer().serializeToString(this.el) - const svg64 = btoa(xml) - const b64Start = 'data:image/svg+xml;base64,' - return b64Start + svg64 - } - - // Exporters - - this.toPNG = function (size = client.tool.settings.size, callback) { - this.update() - - const image64 = this.svg64() - const img = new Image() - const canvas = document.createElement('canvas') - canvas.width = (size.width) * 2 - canvas.height = (size.height) * 2 - img.onload = function () { - canvas.getContext('2d').drawImage(img, 0, 0, (size.width) * 2, (size.height) * 2) - callback(canvas.toDataURL('image/png')) - } - img.src = image64 - } - - this.toSVG = function (callback) { - this.update() - - const image64 = this.svg64() - callback(image64, 'export.svg') - } - - this.toGRID = function (callback) { - this.update() - - const text = client.tool.export() - const file = new Blob([text], { type: 'text/plain' }) - callback(URL.createObjectURL(file), 'export.grid') - } - - this.toString = () => { - return new XMLSerializer().serializeToString(this.el) - } -} diff --git a/desktop/sources/scripts/picker.js b/desktop/sources/scripts/picker.js deleted file mode 100644 index 8ae90be..0000000 --- a/desktop/sources/scripts/picker.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict' - -function Picker (client) { - this.memory = '' - this.el = document.createElement('div') - this.el.id = 'picker' - this.isActive = false - this.input = document.createElement('input') - this.input.id = 'picker_input' - - this.el.appendChild(this.input) - - this.start = function () { - if (this.isActive) { return } - - this.isActive = true - - this.input.setAttribute('placeholder', `${client.tool.style().color.replace('#', '').trim()}`) - this.input.setAttribute('maxlength', 6) - - this.input.addEventListener('keydown', this.onKeyDown, false) - this.input.addEventListener('keyup', this.onKeyUp, false) - - client.interface.el.className = 'picker' - this.input.focus() - this.input.value = '' - - try { client.controller.set('picker') } catch (err) { } - } - - this.update = function () { - if (!this.isActive) { return } - if (!isColor(this.input.value)) { return } - - const hex = `#${this.input.value}` - - document.getElementById('option_color').children[0].style.fill = hex - document.getElementById('option_color').children[0].style.stroke = hex - } - - this.stop = function () { - if (!this.isActive) { return } - - this.isActive = false - - client.interface.el.className = '' - this.input.blur() - this.input.value = '' - - try { client.controller.set() } catch (err) { console.log('No controller') } - - setTimeout(() => { client.interface.update(true); client.renderer.update() }, 250) - } - - this.validate = function () { - if (!isColor(this.input.value)) { return } - - const hex = `#${this.input.value}` - - client.tool.style().color = hex - client.tool.style().fill = client.tool.style().fill !== 'none' ? hex : 'none' - - this.stop() - } - - function isColor (val) { - if (val.length !== 3 && val.length !== 6) { - return false - } - - const re = /[0-9A-Fa-f]/g - return re.test(val) - } - - this.onKeyDown = (e) => { - e.stopPropagation() - if (e.key === 'Enter') { - this.validate() - e.preventDefault() - return - } - if (e.key === 'Escape') { - this.stop() - e.preventDefault() - } - } - - this.onKeyUp = (e) => { - e.stopPropagation() - this.update() - } -} diff --git a/desktop/sources/scripts/renderer.js b/desktop/sources/scripts/renderer.js deleted file mode 100644 index 1794628..0000000 --- a/desktop/sources/scripts/renderer.js +++ /dev/null @@ -1,263 +0,0 @@ -'use strict' - -/* global Image */ -/* global Path2D */ -/* global Generator */ - -function Renderer (client) { - this.el = document.createElement('canvas') - this.el.id = 'guide' - this.el.width = 640 - this.el.height = 640 - this.el.style.width = '320px' - this.el.style.height = '320px' - this.context = this.el.getContext('2d') - this.showExtras = true - - this.scale = 2 // window.devicePixelRatio - - this.start = function () { - this.update() - } - - this.update = function (force = false) { - this.resize() - client.manager.update() - const render = new Image() - render.onload = () => { - this.draw(render) - } - render.src = client.manager.svg64() - } - - this.draw = function (render) { - this.clear() - this.drawMirror() - this.drawGrid() - this.drawRulers() - this.drawRender(render) // - this.drawVertices() - this.drawHandles() - this.drawTranslation() - this.drawCursor() - this.drawPreview() - } - - this.clear = function () { - this.context.clearRect(0, 0, this.el.width * this.scale, this.el.height * this.scale) - } - - this.toggle = function () { - this.showExtras = !this.showExtras - this.update() - client.interface.update(true) - } - - this.resize = function () { - const _target = client.getPaddedSize() - const _current = { width: this.el.width / this.scale, height: this.el.height / this.scale } - const offset = sizeOffset(_target, _current) - if (offset.width === 0 && offset.height === 0) { - return - } - console.log('Renderer', `Require resize: ${printSize(_target)}, from ${printSize(_current)}`) - this.el.width = (_target.width) * this.scale - this.el.height = (_target.height) * this.scale - this.el.style.width = (_target.width) + 'px' - this.el.style.height = (_target.height) + 'px' - } - - // Collections - - this.drawMirror = function () { - if (!this.showExtras) { return } - - if (client.tool.style().mirror_style === 0) { return } - - const middle = { x: client.tool.settings.size.width, y: client.tool.settings.size.height } - - if (client.tool.style().mirror_style === 1 || client.tool.style().mirror_style === 3) { - this.drawRule({ x: middle.x, y: 15 * this.scale }, { x: middle.x, y: (client.tool.settings.size.height) * this.scale }) - } - if (client.tool.style().mirror_style === 2 || client.tool.style().mirror_style === 3) { - this.drawRule({ x: 15 * this.scale, y: middle.y }, { x: (client.tool.settings.size.width) * this.scale, y: middle.y }) - } - } - - this.drawHandles = function () { - if (!this.showExtras) { return } - - for (const segmentId in client.tool.layer()) { - const segment = client.tool.layer()[segmentId] - for (const vertexId in segment.vertices) { - const vertex = segment.vertices[vertexId] - this.drawHandle(vertex) - } - } - } - - this.drawVertices = function () { - for (const id in client.tool.vertices) { - this.drawVertex(client.tool.vertices[id]) - } - } - - this.drawGrid = function () { - if (!this.showExtras) { return } - - const markers = { w: parseInt(client.tool.settings.size.width / 15), h: parseInt(client.tool.settings.size.height / 15) } - - for (let x = markers.w - 1; x >= 0; x--) { - for (let y = markers.h - 1; y >= 0; y--) { - const isStep = x % 4 === 0 && y % 4 === 0 - // Don't draw margins - if (x === 0 || y === 0) { continue } - this.drawMarker({ - x: parseInt(x * 15), - y: parseInt(y * 15) - }, isStep ? 2.5 : 1.5, client.theme.active.b_med) - } - } - } - - this.drawRulers = function () { - if (!client.cursor.translation) { return } - - const pos = client.cursor.translation.to - const bottom = (client.tool.settings.size.height * this.scale) - const right = (client.tool.settings.size.width * this.scale) - - this.drawRule({ x: pos.x * this.scale, y: 0 }, { x: pos.x * this.scale, y: bottom }) - this.drawRule({ x: 0, y: pos.y * this.scale }, { x: right, y: pos.y * this.scale }) - } - - this.drawPreview = function () { - const operation = client.cursor.operation && client.cursor.operation.cast ? client.cursor.operation.cast : null - - if (!client.tool.canCast(operation)) { return } - if (operation === 'close') { return } - - const path = new Generator([{ vertices: client.tool.vertices, type: operation }]).toString({ x: 0, y: 0 }, 2) - const style = { - color: client.theme.active.f_med, - thickness: 2, - strokeLinecap: 'round', - strokeLinejoin: 'round', - strokeLineDash: [5, 15] - } - this.drawPath(path, style) - } - - // Elements - - this.drawMarker = function (pos, radius = 1, color) { - this.context.beginPath() - this.context.lineWidth = 2 - this.context.arc(pos.x * this.scale, pos.y * this.scale, radius, 0, 2 * Math.PI, false) - this.context.fillStyle = color - this.context.fill() - this.context.closePath() - } - - this.drawVertex = function (pos, radius = 5) { - this.context.beginPath() - this.context.lineWidth = 2 - this.context.arc((pos.x * this.scale), (pos.y * this.scale), radius, 0, 2 * Math.PI, false) - this.context.fillStyle = client.theme.active.f_low - this.context.fill() - this.context.closePath() - } - - this.drawRule = function (from, to) { - this.context.beginPath() - this.context.moveTo(from.x, from.y) - this.context.lineTo(to.x, to.y) - this.context.lineCap = 'round' - this.context.lineWidth = 3 - this.context.strokeStyle = client.theme.active.b_low - this.context.stroke() - this.context.closePath() - } - - this.drawHandle = function (pos, radius = 6) { - this.context.beginPath() - this.context.arc(Math.abs(pos.x * -this.scale), Math.abs(pos.y * this.scale), radius + 3, 0, 2 * Math.PI, false) - this.context.fillStyle = client.theme.active.f_high - this.context.fill() - this.context.closePath() - this.context.beginPath() - this.context.arc((pos.x * this.scale), (pos.y * this.scale), radius - 3, 0, 2 * Math.PI, false) - this.context.fillStyle = client.theme.active.b_low - this.context.fill() - this.context.closePath() - } - - this.drawPath = function (path, style) { - const p = new Path2D(path) - - this.context.strokeStyle = style.color - this.context.lineWidth = style.thickness * this.scale - this.context.lineCap = style.strokeLinecap - this.context.lineJoin = style.strokeLinejoin - - if (style.fill && style.fill !== 'none') { - this.context.fillStyle = style.color - this.context.fill(p) - } - - // Dash - this.context.save() - if (style.strokeLineDash) { this.context.setLineDash(style.strokeLineDash) } else { this.context.setLineDash([]) } - this.context.stroke(p) - this.context.restore() - } - - this.drawTranslation = function () { - if (!client.cursor.translation) { return } - - this.context.save() - - this.context.beginPath() - this.context.moveTo((client.cursor.translation.from.x * this.scale), (client.cursor.translation.from.y * this.scale)) - this.context.lineTo((client.cursor.translation.to.x * this.scale), (client.cursor.translation.to.y * this.scale)) - this.context.lineCap = 'round' - this.context.lineWidth = 5 - this.context.strokeStyle = client.cursor.translation.multi === true ? client.theme.active.b_inv : client.cursor.translation.copy === true ? client.theme.active.f_med : client.theme.active.f_low - this.context.setLineDash([5, 10]) - this.context.stroke() - this.context.closePath() - - this.context.setLineDash([]) - this.context.restore() - } - - this.drawCursor = function (pos = client.cursor.pos, radius = client.tool.style().thickness - 1) { - this.context.save() - - this.context.beginPath() - this.context.lineWidth = 3 - this.context.lineCap = 'round' - this.context.arc(Math.abs(pos.x * -this.scale), Math.abs(pos.y * this.scale), 5, 0, 2 * Math.PI, false) - this.context.strokeStyle = client.theme.active.background - this.context.stroke() - this.context.closePath() - - this.context.beginPath() - this.context.lineWidth = 3 - this.context.lineCap = 'round' - this.context.arc(Math.abs(pos.x * -this.scale), Math.abs(pos.y * this.scale), clamp(radius, 5, 100), 0, 2 * Math.PI, false) - this.context.strokeStyle = client.theme.active.f_med - this.context.stroke() - this.context.closePath() - - this.context.restore() - } - - this.drawRender = function (render) { - this.context.drawImage(render, 0, 0, this.el.width, this.el.height) - } - - function printSize (size) { return `${size.width}x${size.height}` } - function sizeOffset (a, b) { return { width: a.width - b.width, height: a.height - b.height } } - function clamp (v, min, max) { return v < min ? min : v > max ? max : v } -} diff --git a/desktop/sources/scripts/tool.js b/desktop/sources/scripts/tool.js deleted file mode 100644 index 02ef7b4..0000000 --- a/desktop/sources/scripts/tool.js +++ /dev/null @@ -1,366 +0,0 @@ -'use strict' - -/* global Generator */ - -function Tool (client) { - this.index = 0 - this.settings = { size: { width: 600, height: 300 } } - this.layers = [[], [], []] - this.styles = [ - { thickness: 15, strokeLinecap: 'round', strokeLinejoin: 'round', color: '#f00', fill: 'none', mirror_style: 0, transform: 'rotate(45)' }, - { thickness: 15, strokeLinecap: 'round', strokeLinejoin: 'round', color: '#0f0', fill: 'none', mirror_style: 0, transform: 'rotate(45)' }, - { thickness: 15, strokeLinecap: 'round', strokeLinejoin: 'round', color: '#00f', fill: 'none', mirror_style: 0, transform: 'rotate(45)' } - ] - this.vertices = [] - this.reqs = { line: 2, arc_c: 2, arc_r: 2, arc_c_full: 2, arc_r_full: 2, bezier: 3, close: 0 } - - this.start = function () { - this.styles[0].color = client.theme.active.f_high - this.styles[1].color = client.theme.active.f_med - this.styles[2].color = client.theme.active.f_low - } - - this.erase = function () { - this.layers = [[], [], []] - } - - this.reset = function () { - this.styles[0].mirror_style = 0 - this.styles[1].mirror_style = 0 - this.styles[2].mirror_style = 0 - this.styles[0].fill = 'none' - this.styles[1].fill = 'none' - this.styles[2].fill = 'none' - this.erase() - this.vertices = [] - this.index = 0 - } - - this.clear = function () { - this.vertices = [] - client.renderer.update() - client.interface.update(true) - } - - this.undo = function () { - this.layers = client.history.prev() - client.renderer.update() - client.interface.update(true) - } - - this.redo = function () { - this.layers = client.history.next() - client.renderer.update() - client.interface.update(true) - } - - this.length = function () { - return this.layers[0].length + this.layers[1].length + this.layers[2].length - } - - // I/O - - this.export = function (target = { settings: this.settings, layers: this.layers, styles: this.styles }) { - return JSON.stringify(copy(target), null, 2) - } - - this.import = function (layer) { - this.layers[this.index] = this.layers[this.index].concat(layer) - client.history.push(this.layers) - this.clear() - client.renderer.update() - client.interface.update(true) - } - - this.replace = function (dot) { - if (!dot.layers || dot.layers.length !== 3) { console.warn('Incompatible version'); return } - - if (dot.settings.width && dot.settings.height) { - dot.settings.size = { width: dot.settings.width, height: dot.settings.height } - } - - this.layers = dot.layers - this.styles = dot.styles - this.settings = dot.settings - - this.clear() - client.fitSize() - client.renderer.update() - client.interface.update(true) - client.history.push(this.layers) - } - - // EDIT - - this.removeSegment = function () { - if (this.vertices.length > 0) { this.clear(); return } - - this.layer().pop() - this.clear() - client.renderer.update() - client.interface.update(true) - } - - this.removeSegmentsAt = function (pos) { - for (const segmentId in this.layer()) { - const segment = this.layer()[segmentId] - for (const vertexId in segment.vertices) { - const vertex = segment.vertices[vertexId] - if (Math.abs(pos.x) === Math.abs(vertex.x) && Math.abs(pos.y) === Math.abs(vertex.y)) { - segment.vertices.splice(vertexId, 1) - } - } - if (segment.vertices.length < 2) { - this.layers[this.index].splice(segmentId, 1) - } - } - this.clear() - client.renderer.update() - client.interface.update(true) - } - - this.selectSegmentAt = function (pos, source = this.layer()) { - for (const segmentId in source) { - const segment = source[segmentId] - for (const vertexId in segment.vertices) { - const vertex = segment.vertices[vertexId] - if (vertex.x === Math.abs(pos.x) && vertex.y === Math.abs(pos.y)) { - return segment - } - } - } - return null - } - - this.addVertex = function (pos) { - pos = { x: Math.abs(pos.x), y: Math.abs(pos.y) } - this.vertices.push(pos) - client.interface.update(true) - } - - this.vertexAt = function (pos) { - for (const segmentId in this.layer()) { - const segment = this.layer()[segmentId] - for (const vertexId in segment.vertices) { - const vertex = segment.vertices[vertexId] - if (vertex.x === Math.abs(pos.x) && vertex.y === Math.abs(pos.y)) { - return vertex - } - } - } - return null - } - - this.addSegment = function (type, vertices, index = this.index) { - const appendTarget = this.canAppend({ type: type, vertices: vertices }, index) - if (appendTarget) { - this.layer(index)[appendTarget].vertices = this.layer(index)[appendTarget].vertices.concat(vertices) - } else { - this.layer(index).push({ type: type, vertices: vertices }) - } - } - - this.cast = function (type) { - if (!this.layer()) { this.layers[this.index] = [] } - if (!this.canCast(type)) { console.warn('Cannot cast'); return } - - this.addSegment(type, this.vertices.slice()) - - client.history.push(this.layers) - - this.clear() - client.renderer.update() - client.interface.update(true) - - console.log(`Casted ${type} -> ${this.layer().length} elements`) - } - - this.i = { linecap: 0, linejoin: 0, thickness: 5 } - - this.toggle = function (type, mod = 1) { - if (type === 'linecap') { - const a = ['butt', 'square', 'round'] - this.i.linecap += mod - this.style().strokeLinecap = a[this.i.linecap % a.length] - } else if (type === 'linejoin') { - const a = ['miter', 'round', 'bevel'] - this.i.linejoin += mod - this.style().strokeLinejoin = a[this.i.linejoin % a.length] - } else if (type === 'fill') { - this.style().fill = this.style().fill === 'none' ? this.style().color : 'none' - } else if (type === 'thickness') { - this.style().thickness = clamp(this.style().thickness + mod, 1, 100) - } else if (type === 'mirror') { - this.style().mirror_style = this.style().mirror_style > 2 ? 0 : this.style().mirror_style + 1 - } else { - console.warn('Unknown', type) - } - client.interface.update(true) - client.renderer.update() - } - - this.misc = function (type) { - client.picker.start() - } - - this.source = function (type) { - if (type === 'grid') { client.renderer.toggle() } - if (type === 'open') { client.source.open('grid', client.whenOpen) } - if (type === 'save') { client.source.write('dotgrid', 'grid', client.tool.export(), 'text/plain') } - if (type === 'export') { client.source.write('dotgrid', 'svg', client.manager.toString(), 'image/svg+xml') } - if (type === 'render') { client.manager.toPNG(client.tool.settings.size, (dataUrl) => { client.source.write('dotgrid', 'png', dataUrl, 'image/png') }) } - } - - this.canAppend = function (content, index = this.index) { - for (const id in this.layer(index)) { - const stroke = this.layer(index)[id] - if (stroke.type !== content.type) { continue } - if (!stroke.vertices) { continue } - if (!stroke.vertices[stroke.vertices.length - 1]) { continue } - if (stroke.vertices[stroke.vertices.length - 1].x !== content.vertices[0].x) { continue } - if (stroke.vertices[stroke.vertices.length - 1].y !== content.vertices[0].y) { continue } - return id - } - return false - } - - this.canCast = function (type) { - if (!type) { return false } - // Cannot cast close twice - if (type === 'close') { - const prev = this.layer()[this.layer().length - 1] - if (!prev || prev.type === 'close') { - return false - } - } - if (type === 'bezier') { - if (this.vertices.length !== 3 && this.vertices.length !== 5 && this.vertices.length !== 7 && this.vertices.length !== 9) { - return false - } - } - return this.vertices.length >= this.reqs[type] - } - - this.paths = function () { - const l1 = new Generator(client.tool.layers[0], client.tool.styles[0]).toString({ x: 0, y: 0 }, 1) - const l2 = new Generator(client.tool.layers[1], client.tool.styles[1]).toString({ x: 0, y: 0 }, 1) - const l3 = new Generator(client.tool.layers[2], client.tool.styles[2]).toString({ x: 0, y: 0 }, 1) - - return [l1, l2, l3] - } - - this.path = function () { - return new Generator(client.tool.layer(), client.tool.style()).toString({ x: 0, y: 0 }, 1) - } - - this.translate = function (a, b) { - for (const segmentId in this.layer()) { - const segment = this.layer()[segmentId] - for (const vertexId in segment.vertices) { - const vertex = segment.vertices[vertexId] - if (vertex.x === Math.abs(a.x) && vertex.y === Math.abs(a.y)) { - segment.vertices[vertexId] = { x: Math.abs(b.x), y: Math.abs(b.y) } - } - } - } - client.history.push(this.layers) - this.clear() - client.renderer.update() - } - - this.translateMulti = function (a, b) { - const offset = { x: a.x - b.x, y: a.y - b.y } - const segment = this.selectSegmentAt(a) - - if (!segment) { return } - - for (const vertexId in segment.vertices) { - const vertex = segment.vertices[vertexId] - segment.vertices[vertexId] = { x: vertex.x - offset.x, y: vertex.y - offset.y } - } - - client.history.push(this.layers) - this.clear() - client.renderer.update() - } - - this.translateLayer = function (a, b) { - const offset = { x: a.x - b.x, y: a.y - b.y } - for (const segmentId in this.layer()) { - const segment = this.layer()[segmentId] - for (const vertexId in segment.vertices) { - const vertex = segment.vertices[vertexId] - segment.vertices[vertexId] = { x: vertex.x - offset.x, y: vertex.y - offset.y } - } - } - client.history.push(this.layers) - this.clear() - client.renderer.update() - } - - this.translateCopy = function (a, b) { - const offset = { x: a.x - b.x, y: a.y - b.y } - const segment = this.selectSegmentAt(a, copy(this.layer())) - - if (!segment) { return } - - for (const vertexId in segment.vertices) { - const vertex = segment.vertices[vertexId] - segment.vertices[vertexId] = { x: vertex.x - offset.x, y: vertex.y - offset.y } - } - this.layer().push(segment) - - client.history.push(this.layers) - this.clear() - client.renderer.update() - } - - this.merge = function () { - const merged = [].concat(this.layers[0]).concat(this.layers[1]).concat(this.layers[2]) - this.erase() - this.layers[this.index] = merged - - client.history.push(this.layers) - this.clear() - client.renderer.update() - } - - // Style - - this.style = function () { - if (!this.styles[this.index]) { - this.styles[this.index] = [] - } - return this.styles[this.index] - } - - // Layers - - this.layer = function (index = this.index) { - if (!this.layers[index]) { - this.layers[index] = [] - } - return this.layers[index] - } - - this.selectLayer = function (id) { - this.index = clamp(id, 0, 2) - this.clear() - client.renderer.update() - client.interface.update(true) - console.log(`layer:${this.index}`) - } - - this.selectNextLayer = function () { - this.index = this.index >= 2 ? 0 : this.index++ - this.selectLayer(this.index) - } - - this.selectPrevLayer = function () { - this.index = this.index >= 0 ? 2 : this.index-- - this.selectLayer(this.index) - } - - function copy (data) { return data ? JSON.parse(JSON.stringify(data)) : [] } - function clamp (v, min, max) { return v < min ? min : v > max ? max : v } -} diff --git a/examples/taijitu.grid b/examples/taijitu.grid deleted file mode 100644 index 1410cf1..0000000 --- a/examples/taijitu.grid +++ /dev/null @@ -1,205 +0,0 @@ -{ - "settings": { - "size": { - "width": 300, - "height": 285 - }, - "crest": false - }, - "layers": [ - [ - { - "type": "arc_r", - "vertices": [ - { - "x": 120, - "y": 210 - }, - { - "x": 180, - "y": 150 - } - ] - }, - { - "type": "arc_c", - "vertices": [ - { - "x": 120, - "y": 150 - }, - { - "x": 180, - "y": 90 - }, - { - "x": 180, - "y": 90 - }, - { - "x": 240, - "y": 150 - } - ] - }, - { - "type": "arc_c", - "vertices": [ - { - "x": 120, - "y": 210 - }, - { - "x": 60, - "y": 150 - } - ] - }, - { - "type": "arc_c", - "vertices": [ - { - "x": 120, - "y": 150 - }, - { - "x": 150, - "y": 120 - }, - { - "x": 180, - "y": 150 - }, - { - "x": 150, - "y": 180 - }, - { - "x": 120, - "y": 150 - } - ] - }, - { - "type": "arc_c", - "vertices": [ - { - "x": 150, - "y": 60 - }, - { - "x": 240, - "y": 150 - }, - { - "x": 150, - "y": 240 - }, - { - "x": 60, - "y": 150 - }, - { - "x": 150, - "y": 60 - } - ] - }, - { - "type": "close", - "vertices": [] - } - ], - [ - { - "type": "arc_r", - "vertices": [ - { - "x": 240, - "y": 150 - }, - { - "x": 150, - "y": 60 - }, - { - "x": 60, - "y": 150 - }, - { - "x": 120, - "y": 210 - }, - { - "x": 180, - "y": 150 - } - ] - }, - { - "type": "arc_c", - "vertices": [ - { - "x": 180, - "y": 150 - }, - { - "x": 180, - "y": 150 - }, - { - "x": 150, - "y": 180 - }, - { - "x": 120, - "y": 150 - }, - { - "x": 120, - "y": 150 - }, - { - "x": 180, - "y": 90 - }, - { - "x": 240, - "y": 150 - } - ] - }, - { - "type": "close", - "vertices": [] - } - ], - [] - ], - "styles": [ - { - "thickness": 2, - "strokeLinecap": "round", - "strokeLinejoin": "round", - "color": "#000", - "fill": "none", - "mirror_style": 0 - }, - { - "thickness": 2, - "strokeLinecap": "round", - "strokeLinejoin": "round", - "color": "#000", - "fill": "#000", - "mirror_style": 0 - }, - { - "thickness": 2, - "strokeLinecap": "round", - "strokeLinejoin": "round", - "color": "#000", - "fill": "none", - "mirror_style": 0 - } - ] -} \ No newline at end of file diff --git a/media/services/meta.jpg b/media/services/meta.jpg deleted file mode 100644 index ef02730..0000000 Binary files a/media/services/meta.jpg and /dev/null differ