Moved into /desktop

This commit is contained in:
Devine Lu Linvega
2018-02-18 16:25:34 +13:00
parent cc1735081d
commit f0c7fa3469
26 changed files with 0 additions and 1 deletions

View File

@@ -0,0 +1,556 @@
function Dotgrid(width,height,grid_x,grid_y,block_x,block_y,thickness = 3,linecap = "round",linejoin = "round", color = "#000000")
{
this.controller = new Controller();
this.theme = new Theme();
this.interface = new Interface();
this.history = new History();
this.guide = new Guide();
this.render = new Render();
this.tool = new Tool();
this.width = width;
this.height = height;
this.grid_x = grid_x;
this.grid_y = grid_y;
this.block_x = block_x;
this.block_y = block_y;
this.thickness = thickness;
this.linecap = linecap;
this.linejoin = linejoin;
this.color = color;
this.offset = new Pos(0,0);
// Dotgrid
this.element = document.createElement("div");
this.element.id = "dotgrid";
this.element.style.width = this.width;
this.element.style.height = this.height;
this.wrapper = document.createElement("div");
this.wrapper.id = "wrapper";
this.grid_width = this.width/this.grid_x;
this.grid_height = this.height/this.grid_y;
var cursor = null;
this.svg_el = null;
this.mirror_el = null;
this.mirror = false;
this.fill = false;
this.layer_1 = document.createElementNS("http://www.w3.org/2000/svg", "path"); this.layer_1.id = "layer_1"; this.layer_1.style.stroke = "black";
this.layer_2 = document.createElementNS("http://www.w3.org/2000/svg", "path"); this.layer_2.id = "layer_2"; this.layer_2.style.stroke = "#999";
this.layer_3 = document.createElementNS("http://www.w3.org/2000/svg", "path"); this.layer_3.id = "layer_3"; this.layer_3.style.stroke = "#ccc";
this.mirror_layer_1 = document.createElementNS("http://www.w3.org/2000/svg", "path"); this.mirror_layer_1.id = "mirror_layer_1"; this.mirror_layer_1.style.stroke = "black";
this.mirror_layer_2 = document.createElementNS("http://www.w3.org/2000/svg", "path"); this.mirror_layer_2.id = "mirror_layer_2"; this.mirror_layer_2.style.stroke = "#999";
this.mirror_layer_3 = document.createElementNS("http://www.w3.org/2000/svg", "path"); this.mirror_layer_3.id = "mirror_layer_3"; this.mirror_layer_3.style.stroke = "#ccc";
this.scale = 1;
this.install = function()
{
document.getElementById("app").appendChild(this.wrapper);
this.wrapper.appendChild(this.element);
this.element.appendChild(this.guide.el);
this.element.appendChild(this.guide.widgets);
this.wrapper.appendChild(this.render.el);
// Cursors
this.cursor = document.createElement("div");
this.cursor.id = "cursor";
this.element.appendChild(this.cursor);
this.cursor_x = document.createElement("t");
this.cursor_x.id = "cursor_x";
this.cursor_x.className = "fl"
this.element.appendChild(this.cursor_x);
this.cursor_y = document.createElement("t");
this.cursor_y.id = "cursor_y";
this.cursor_y.className = "fl"
this.element.appendChild(this.cursor_y);
this.offset_el = document.createElementNS("http://www.w3.org/2000/svg", "g");
this.mirror_el = document.createElementNS("http://www.w3.org/2000/svg", "g");
// Vector
this.svg_el = document.createElementNS("http://www.w3.org/2000/svg", "svg");
this.svg_el.setAttribute("class","vector");
this.svg_el.setAttribute("width",this.width+"px");
this.svg_el.setAttribute("height",this.height+"px");
this.svg_el.setAttribute("xmlns","http://www.w3.org/2000/svg");
this.svg_el.setAttribute("baseProfile","full");
this.svg_el.setAttribute("version","1.1");
this.svg_el.style.width = this.width;
this.svg_el.style.height = this.height;
this.svg_el.style.stroke = this.color;
this.svg_el.style.strokeWidth = this.thickness;
this.svg_el.style.fill = "none";
this.svg_el.style.strokeLinecap = this.linecap;
this.svg_el.style.strokeLinejoin = this.linejoin;
this.element.appendChild(this.svg_el);
// Preview
this.preview_el = document.createElementNS("http://www.w3.org/2000/svg", "svg");
this.preview_el.id = "preview"
this.preview_el.setAttribute("class","vector");
this.preview_el.setAttribute("width",this.width+"px");
this.preview_el.setAttribute("height",this.height+"px");
this.preview_el.setAttribute("xmlns","http://www.w3.org/2000/svg");
this.preview_el.setAttribute("baseProfile","full");
this.preview_el.setAttribute("version","1.1");
this.preview_el.style.width = this.width;
this.preview_el.style.height = this.height;
this.preview_el.style.strokeWidth = 2;
this.preview_el.style.fill = "none";
this.preview_el.style.strokeLinecap = "round";
this.element.appendChild(this.preview_el);
this.mirror_el.appendChild(this.mirror_layer_3)
this.offset_el.appendChild(this.layer_3)
this.mirror_el.appendChild(this.mirror_layer_2)
this.offset_el.appendChild(this.layer_2)
this.mirror_el.appendChild(this.mirror_layer_1)
this.offset_el.appendChild(this.layer_1)
this.svg_el.appendChild(this.offset_el);
this.svg_el.appendChild(this.mirror_el);
this.theme.start();
this.guide.start();
this.interface.start();
this.controller.add("default","*","About",() => { require('electron').shell.openExternal('https://github.com/hundredrabbits/Dotgrid'); },"CmdOrCtrl+,");
this.controller.add("default","*","Fullscreen",() => { app.toggle_fullscreen(); },"CmdOrCtrl+Enter");
this.controller.add("default","*","Hide",() => { app.toggle_visible(); },"CmdOrCtrl+H");
this.controller.add("default","*","Inspect",() => { app.inspect(); },"CmdOrCtrl+.");
this.controller.add("default","*","Documentation",() => { dotgrid.controller.docs(); },"CmdOrCtrl+Esc");
this.controller.add("default","*","Reset",() => { dotgrid.reset(); dotgrid.theme.reset(); },"CmdOrCtrl+Backspace");
this.controller.add("default","*","Quit",() => { app.exit(); },"CmdOrCtrl+Q");
this.controller.add("default","File","New",() => { dotgrid.new(); },"CmdOrCtrl+N");
this.controller.add("default","File","Open",() => { dotgrid.open(); },"CmdOrCtrl+O");
this.controller.add("default","File","Save",() => { dotgrid.save(); },"CmdOrCtrl+S");
this.controller.add("default","Edit","Copy",() => { document.execCommand('copy'); },"CmdOrCtrl+C");
this.controller.add("default","Edit","Cut",() => { document.execCommand('cut'); },"CmdOrCtrl+X");
this.controller.add("default","Edit","Paste",() => { document.execCommand('paste'); },"CmdOrCtrl+V");
this.controller.add("default","Edit","Undo",() => { dotgrid.tool.undo(); },"CmdOrCtrl+Z");
this.controller.add("default","Edit","Redo",() => { dotgrid.tool.redo(); },"CmdOrCtrl+Shift+Z");
this.controller.add("default","Edit","Delete",() => { dotgrid.tool.remove_segment(); },"Backspace");
this.controller.add("default","Edit","Deselect",() => { dotgrid.tool.clear(); },"Esc");
this.controller.add("default","Select","Foreground",() => { dotgrid.tool.select_layer(0); },"1");
this.controller.add("default","Select","Middleground",() => { dotgrid.tool.select_layer(1); },"2");
this.controller.add("default","Select","Background",() => { dotgrid.tool.select_layer(2); },"3");
this.controller.add("default","Stroke","Line",() => { dotgrid.tool.cast("line"); },"A");
this.controller.add("default","Stroke","Arc",() => { dotgrid.tool.cast("arc_c"); },"S"); // 0,1
this.controller.add("default","Stroke","Arc Rev",() => { dotgrid.tool.cast("arc_r")},"D"); // 0,0
this.controller.add("default","Stroke","Bezier",() => { dotgrid.tool.cast("bezier") },"F");
this.controller.add("default","Stroke","Connect",() => { dotgrid.tool.cast("close") },"Z");
this.controller.add("default","Effect","Linecap",() => { dotgrid.mod_linecap(); },"Q");
this.controller.add("default","Effect","Linejoin",() => { dotgrid.mod_linejoin(); },"W");
this.controller.add("default","Effect","Mirror",() => { dotgrid.mod_mirror(); },"E");
this.controller.add("default","Effect","Fill",() => { dotgrid.toggle_fill(); },"R");
this.controller.add("default","Effect","Thicker",() => { dotgrid.mod_thickness(1) },"}");
this.controller.add("default","Effect","Thinner",() => { dotgrid.mod_thickness(-1) },"{");
this.controller.add("default","Effect","Thicker +5",() => { dotgrid.mod_thickness(5,true) },"]");
this.controller.add("default","Effect","Thinner -5",() => { dotgrid.mod_thickness(-5,true) },"[");
this.controller.add("default","Layers","Move Above",() => { dotgrid.tool.layer_up() },"Up");
this.controller.add("default","Layers","Move Below",() => { dotgrid.tool.layer_down() },"Down");
this.controller.add("default","View","Tools",() => { dotgrid.interface.toggle(); },"U");
this.controller.add("default","View","Grid",() => { dotgrid.guide.toggle(); },"H");
this.controller.add("default","View","Control Points",() => { dotgrid.guide.toggle_widgets(); },"J");
this.controller.add("default","View","Expert Mode",() => { dotgrid.interface.toggle_zoom(); },":");
this.controller.commit();
document.addEventListener('mousedown', function(e){ dotgrid.mouse_down(e); }, false);
document.addEventListener('mousemove', function(e){ dotgrid.mouse_move(e); }, false);
document.addEventListener('contextmenu', function(e){ dotgrid.mouse_alt(e); }, false);
document.addEventListener('mouseup', function(e){ dotgrid.mouse_up(e);}, false);
document.addEventListener('copy', function(e){ dotgrid.copy(e); e.preventDefault(); }, false);
document.addEventListener('cut', function(e){ dotgrid.cut(e); e.preventDefault(); }, false);
document.addEventListener('paste', function(e){ dotgrid.paste(e); e.preventDefault(); }, false);
window.addEventListener('drop', dotgrid.drag);
dotgrid.set_size({width:300,height:300});
this.new();
}
// FILE
this.new = function()
{
this.history.push(this.tool.layers);
dotgrid.clear();
}
this.save = function()
{
this.scale = 1
this.draw();
if(dotgrid.fill){ dotgrid.svg_el.style.fill = "black"; dotgrid.render.draw(); }
var svg = dotgrid.svg_el.outerHTML;
dialog.showSaveDialog((fileName) => {
if (fileName === undefined){ return; }
fs.writeFile(fileName+".svg", svg);
fs.writeFile(fileName+'.png', dotgrid.render.buffer());
fs.writeFile(fileName+'.dot', dotgrid.tool.export());
dotgrid.draw()
});
}
this.open = function()
{
var paths = dialog.showOpenDialog({properties: ['openFile'],filters:[{name:"Dotgrid Image",extensions:["dot"]}]});
if(!paths){ console.log("Nothing to load"); return; }
fs.readFile(paths[0], 'utf-8', (err, data) => {
if(err){ alert("An error ocurred reading the file :" + err.message); return; }
dotgrid.tool.replace(JSON.parse(data.toString().trim()));
dotgrid.draw();
});
}
// Cursor
this.translation = null;
this.mouse_down = function(e)
{
var pos = this.position_in_grid(new Pos(e.clientX+5,e.clientY-5)); pos = this.position_on_grid(pos);
if(e.altKey){ dotgrid.tool.remove_segments_at(pos); return; }
if(dotgrid.tool.vertex_at(pos)){ console.log("Begin translation"); dotgrid.translation = {from:pos,to:pos}; return; }
var o = e.target.getAttribute("ar");
if(!o){ return; }
if(o == "line"){ this.tool.cast("line"); }
if(o == "arc_c"){ this.tool.cast("arc_c"); }
if(o == "arc_r"){ this.tool.cast("arc_r"); }
if(o == "bezier"){ this.tool.cast("bezier"); }
if(o == "close"){ this.tool.cast("close"); }
if(o == "thickness"){ this.mod_thickness(); }
if(o == "linecap"){ this.mod_linecap(); }
if(o == "linejoin"){ this.mod_linejoin(); }
if(o == "mirror"){ this.mod_mirror(); }
if(o == "fill"){ this.toggle_fill(); }
if(o == "export"){ this.save(); }
}
this.mouse_move = function(e)
{
var pos = this.position_in_grid(new Pos(e.clientX+5,e.clientY-5)); pos = this.position_on_grid(pos);
if(dotgrid.translation && (Math.abs(dotgrid.translation.from.x) != Math.abs(pos.x) || Math.abs(dotgrid.translation.from.y) != Math.abs(pos.y))){ dotgrid.translation.to = pos; }
dotgrid.preview(e.target.getAttribute("ar"));
dotgrid.move_cursor(pos)
dotgrid.guide.update();
}
this.mouse_up = function(e)
{
var pos = this.position_in_grid(new Pos(e.clientX+5,e.clientY-5)); pos = this.position_on_grid(pos);
if(e.altKey){ return; }
if(pos.x > 0) { dotgrid.translation = null; return; }
if(dotgrid.translation && (Math.abs(dotgrid.translation.from.x) != Math.abs(dotgrid.translation.to.x) || Math.abs(dotgrid.translation.from.y) != Math.abs(dotgrid.translation.to.y))){
dotgrid.tool.translate(dotgrid.translation.from,dotgrid.translation.to);
dotgrid.translation = null;
this.draw();
return;
}
this.tool.add_vertex({x:pos.x * -1,y:pos.y});
dotgrid.translation = null;
this.draw();
}
this.mouse_alt = function(e)
{
var pos = this.position_in_grid(new Pos(e.clientX+5,e.clientY-5)); pos = this.position_on_grid(pos);
dotgrid.tool.remove_segments_at(pos);
e.preventDefault();
setTimeout(() => { dotgrid.tool.clear(); },150);
}
this.move_cursor = function(pos)
{
if(pos.x>0) {
this.cursor.style.visibility = "hidden"
} else {
if(this.cursor.style.visibility == "hidden") {
this.cursor.style.transition = "initial"
}
this.cursor.style.visibility = "visible"
this.cursor.style.left = Math.floor(-(pos.x-this.grid_width));
this.cursor.style.top = Math.floor(pos.y+this.grid_height);
this.update_cursor(pos);
window.setTimeout(() => dotgrid.cursor.style.transition = "all 50ms", 17 /*one frame*/)
}
}
this.update_cursor = function(pos)
{
this.cursor_x.style.left = `${-pos.x}px`;
this.cursor_x.textContent = parseInt(-pos.x/this.grid_width)
this.cursor_y.style.top = `${pos.y}px`;
this.cursor_y.textContent = parseInt(pos.y/this.grid_width)
}
this.preview = function(operation)
{
if(!operation){ return `<path d='M0,0'></path>`;}
if(operation != "line" && operation != "arc_c" && operation != "arc_r" && operation != "bezier" && operation != "close"){ return `<path d='M0,0'></path>`; }
this.preview_el.innerHTML = `<path d='${dotgrid.tool.path([{type:operation,verteces:dotgrid.tool.verteces}])}'></path>`;
}
// Toggles
this.mod_thickness = function(mod,step = false)
{
if(!mod){ mod = 1; this.thickness = this.thickness > 30 ? 1 : this.thickness }
if(step){
this.thickness = parseInt(this.thickness/5) * 5;
}
this.thickness = Math.max(this.thickness+mod,0);
this.cursor_x.textContent = this.thickness;
this.draw();
}
this.mod_linecap_index = 1;
this.mod_linecap = function(mod)
{
var a = ["butt","square","round"];
this.mod_linecap_index += 1;
this.linecap = a[this.mod_linecap_index % a.length];
this.draw();
}
this.mod_linejoin_index = 1;
this.mod_linejoin = function(mod)
{
var a = ["miter","round","bevel"];
this.mod_linejoin_index += 1;
this.linejoin = a[this.mod_linejoin_index % a.length];
this.draw();
}
this.mirror_index = 0;
this.mod_mirror = function()
{
this.mirror_index += 1;
this.mirror_index = this.mirror_index > 3 ? 0 : this.mirror_index;
this.draw();
}
this.toggle_fill = function()
{
dotgrid.fill = dotgrid.fill ? false : true;
this.draw();
}
this.set_size = function(size = {width:300,height:300},interface = true)
{
var win = require('electron').remote.getCurrentWindow();
win.setSize(size.width+100,size.height+100+(interface ? 10 : 0),true);
this.width = size.width
this.height = size.height
this.element.style.width = size.width+10
this.element.style.height = size.height+10
this.grid_x = size.width/15
this.grid_y = size.height/15
this.svg_el.setAttribute("width",size.width+"px");
this.svg_el.setAttribute("height",size.height+"px");
this.preview_el.style.width = size.width+10
this.preview_el.style.height = size.height+10
this.preview_el.setAttribute("width",size.width+"px");
this.preview_el.setAttribute("height",size.height+"px");
dotgrid.guide.resize(size);
this.interface.update();
this.draw();
}
this.draw = function(exp = false)
{
var paths = this.tool.paths();
var d = this.tool.path();
this.layer_1.setAttribute("d",paths[0]);
this.layer_2.setAttribute("d",paths[1]);
this.layer_3.setAttribute("d",paths[2]);
this.mirror_layer_1.setAttribute("d",this.mirror_index > 0 ? paths[0] : "M0,0");
this.mirror_layer_2.setAttribute("d",this.mirror_index > 0 ? paths[1] : "M0,0");
this.mirror_layer_3.setAttribute("d",this.mirror_index > 0 ? paths[2] : "M0,0");
this.svg_el.style.width = this.width;
this.svg_el.style.height = this.height;
this.svg_el.style.stroke = this.color;
this.svg_el.style.strokeLinecap = this.linecap;
this.svg_el.style.strokeLinejoin = this.linejoin;
this.svg_el.style.strokeWidth = this.thickness*this.scale;
this.svg_el.style.fill = this.fill ? this.theme.active.f_high : "none";
// Draw Mirror
if(this.mirror_index == 1){
this.mirror_layer_1.setAttribute("transform","translate("+(this.width - (this.offset.x*this.scale))+","+(this.offset.y*this.scale)+"),scale(-1,1)")
this.mirror_layer_2.setAttribute("transform","translate("+(this.width - (this.offset.x*this.scale))+","+(this.offset.y*this.scale)+"),scale(-1,1)")
this.mirror_layer_3.setAttribute("transform","translate("+(this.width - (this.offset.x*this.scale))+","+(this.offset.y*this.scale)+"),scale(-1,1)")
}
else if(this.mirror_index == 2){
this.mirror_layer_1.setAttribute("transform","translate("+((this.offset.x*this.scale))+","+(this.height - (this.offset.y*this.scale))+"),scale(1,-1)")
this.mirror_layer_2.setAttribute("transform","translate("+((this.offset.x*this.scale))+","+(this.height - (this.offset.y*this.scale))+"),scale(1,-1)")
this.mirror_layer_3.setAttribute("transform","translate("+((this.offset.x*this.scale))+","+(this.height - (this.offset.y*this.scale))+"),scale(1,-1)")
}
else if(this.mirror_index == 3){
this.mirror_layer_1.setAttribute("transform","translate("+(this.width -(this.offset.x*this.scale))+","+(this.height - (this.offset.y*this.scale))+"),scale(-1,-1)")
this.mirror_layer_2.setAttribute("transform","translate("+(this.width -(this.offset.x*this.scale))+","+(this.height - (this.offset.y*this.scale))+"),scale(-1,-1)")
this.mirror_layer_3.setAttribute("transform","translate("+(this.width -(this.offset.x*this.scale))+","+(this.height - (this.offset.y*this.scale))+"),scale(-1,-1)")
}
else{
this.mirror_layer_1.setAttribute("transform","")
this.mirror_layer_2.setAttribute("transform","")
this.mirror_layer_3.setAttribute("transform","")
}
this.offset_el.setAttribute("transform","translate("+(this.offset.x*this.scale)+","+(this.offset.y*this.scale)+")")
this.preview();
this.render.draw();
this.interface.update();
this.guide.update();
}
// Draw
this.reset = function()
{
this.tool.clear();
}
this.clear = function()
{
this.history.clear();
this.tool.reset();
this.reset();
this.thickness = 10
this.linecap = "round"
this.linejoin = "round"
this.color = "#000000"
this.draw();
}
this.drag = function(e)
{
e.preventDefault();
e.stopPropagation();
var file = e.dataTransfer.files[0];
if(!file.path || file.path.indexOf(".dot") < 0){ console.log("Dotgrid","Not a dot file"); return; }
var reader = new FileReader();
reader.onload = function(e){
dotgrid.tool.replace(JSON.parse(e.target.result.toString().trim()));
dotgrid.draw();
};
reader.readAsText(file);
}
this.copy = function(e)
{
dotgrid.scale = 1
dotgrid.width = 300
dotgrid.height = 300
dotgrid.draw();
var svg = dotgrid.svg_el.outerHTML;
e.clipboardData.setData('text/plain', dotgrid.tool.export(dotgrid.tool.layer()));
e.clipboardData.setData('text/html', svg);
e.clipboardData.setData('text/svg+xml', svg);
this.draw();
}
this.cut = function(e)
{
dotgrid.scale = 1
dotgrid.width = 300
dotgrid.height = 300
dotgrid.draw();
var svg = dotgrid.svg_el.outerHTML;
e.clipboardData.setData('text/plain', dotgrid.tool.export(dotgrid.tool.layer()));
e.clipboardData.setData('text/html', svg);
e.clipboardData.setData('text/svg+xml', svg);
dotgrid.tool.layers[dotgrid.tool.index] = [];
this.draw();
}
this.paste = function(e)
{
var data = e.clipboardData.getData("text/plain");
data = JSON.parse(data.trim());
dotgrid.tool.import(data);
this.draw();
}
// Normalizers
this.position_in_grid = function(pos)
{
return new Pos((window.innerWidth/2) - (this.width/2) - pos.x,pos.y - (30+10*(this.scale)))
}
this.position_on_grid = function(pos)
{
pos.y = pos.y - 7.5
pos.x = pos.x + 7.5
x = Math.round(pos.x/this.grid_width)*this.grid_width
y = Math.round(pos.y/this.grid_height)*this.grid_height
off = (x<-this.width || x>0 || y>this.height || y<0)
if(off) {
x = 50
y = -50
}
return new Pos(x,y);
}
}
window.addEventListener('resize', function(e)
{
dotgrid.draw()
}, false);
window.addEventListener('dragover',function(e)
{
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});

View File

@@ -0,0 +1,147 @@
function Guide()
{
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.widgets = document.createElement("canvas");
this.widgets.id = "widgets";
this.widgets.width = 640;
this.widgets.height = 640;
this.widgets.style.width = "320px";
this.widgets.style.height = "320px";
this.start = function()
{
this.clear();
this.draw();
}
this.toggle = function()
{
this.el.style.opacity = !this.el.style.opacity || this.el.style.opacity == 1 ? 0 : 1;
}
this.toggle_widgets = function()
{
this.widgets.style.opacity = !this.widgets.style.opacity || this.widgets.style.opacity == 1 ? 0 : 1;
}
this.draw = function()
{
for (var x = dotgrid.grid_x; x >= 0; x--) {
for (var y = dotgrid.grid_y; y >= 0; y--) {
var pos_x = parseInt(x * dotgrid.grid_width) + dotgrid.grid_width ;
var pos_y = parseInt(y * dotgrid.grid_height) + dotgrid.grid_height ;
var is_step = x % dotgrid.block_x == 0 && y % dotgrid.block_y == 0;
var radius = is_step ? 2.5 : 1.5;
dotgrid.guide.draw_marker({x:pos_x,y:pos_y},radius,is_step);
}
}
}
this.resize = function(size)
{
this.el.width = (size.width+40)*2;
this.el.height = (size.height+40)*2;
this.el.style.width = (size.width+40)+"px";
this.el.style.height = (size.height+40)+"px";
this.widgets.width = (size.width+20)*2;
this.widgets.height = (size.height+20)*2;
this.widgets.style.width = (size.width+20)+"px";
this.widgets.style.height = (size.height+20)+"px";
this.update();
}
this.clear = function()
{
this.el.getContext('2d').clearRect(0, 0, 1280, 1280);
this.widgets.getContext('2d').clearRect(0, 0, 1280, 1280);
}
this.update = function()
{
this.clear();
for(id in dotgrid.tool.verteces){
this.draw_vertex(dotgrid.tool.verteces[id]);
}
for(segment_id in dotgrid.tool.layer()){
var segment = dotgrid.tool.layer()[segment_id];
for(vertex_id in segment.verteces){
var vertex = segment.verteces[vertex_id];
this.draw_handle(vertex);
}
}
// Translations
if(dotgrid.translation){
this.draw_translation();
}
this.draw();
}
this.draw_vertex = function(pos, radius = 5)
{
var ctx = this.el.getContext('2d');
ctx.beginPath();
ctx.arc((pos.x * 2)+30, (pos.y * 2)+30, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = dotgrid.theme.active.f_med;
ctx.fill();
ctx.closePath();
}
this.draw_marker = function(pos,radius = 1,step)
{
var ctx = this.el.getContext('2d');
ctx.beginPath();
ctx.arc(pos.x * 2, pos.y * 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = step ? dotgrid.theme.active.f_med : dotgrid.theme.active.f_low;
ctx.fill();
ctx.closePath();
}
this.draw_handle = function(pos,radius = 5)
{
var ctx = this.widgets.getContext('2d');
ctx.beginPath();
ctx.arc((pos.x * 2)+20, (pos.y * 2)+20, 10, 0, 2 * Math.PI, false);
ctx.fillStyle = dotgrid.theme.active.f_high;
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc((pos.x * 2)+20, (pos.y * 2)+20, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = dotgrid.theme.active.f_high;
ctx.fill();
ctx.lineWidth = 3;
ctx.strokeStyle = dotgrid.theme.active.background;
ctx.stroke();
ctx.closePath();
}
this.draw_translation = function()
{
// From
var ctx = this.widgets.getContext('2d');
var from = dotgrid.translation.from;
var to = dotgrid.translation.to;
if(to.x<=0) {
ctx.beginPath();
ctx.moveTo((from.x * -2)+20,(from.y * 2)+20);
ctx.lineTo((to.x * -2)+20,(to.y * 2)+20);
ctx.lineCap="round";
ctx.lineWidth = 5;
ctx.strokeStyle = dotgrid.theme.active.b_inv;
ctx.stroke();
ctx.closePath();
}
}
}

View File

@@ -0,0 +1,79 @@
function Interface()
{
this.el = document.createElement("div");
this.el.id = "interface";
this.is_visible = true;
this.zoom = false;
this.start = function()
{
document.getElementById("app").appendChild(this.el);
var html = ""
var tools = {
line: ["line","M60,60 L240,240",""],
arc_c: ["arc clockwise","M60,60 A180,180 0 0,1 240,240",""],
arc_r: ["arc reverse","M60,60 A180,180 0 0,0 240,240",""],
bezier: ["bezier","M60,60 Q60,150 150,150 Q240,150 240,240",""],
close: ["close","M60,60 A180,180 0 0,1 240,240 M60,60 A180,180 0 0,0 240,240",""],
thickness: ["thickness","M60,60 L240,240","stroke-dasharray: 30,15"],
linecap: ["linecap","M60,60 L240,240 M240,180 L240,240 M180,240 L240,240"],
linejoin: ["linejoin","M60,60 L120,120 L180,120 M120,180 L180,180 L240,240"],
mirror: ["mirror","M60,60 L240,240 M180,120 L210,90 M120,180 L90,210"],
fill: ["fill","M60,60 L60,150 L150,150 L240,150 L240,240 Z"],
export: ["export","M150,50 L50,150 L150,250 L250,150 L150,50 Z"]
}
for(id in tools){
var tool = tools[id];
html += `<svg id="${id}" ar="${id}" title="${tool[0]}" viewBox="0 0 300 300" class="icon"><path class="icon_path" d="${tool[1]}" stroke-linecap: round; stroke-width="12px" fill="none" /><rect ar="${id}" width="300" height="300" opacity="0"><title>${id}</title></rect></svg>`
}
this.el.innerHTML = html
}
this.update = function()
{
var layer_path = "M150,50 L50,150 L150,250 L250,150 L150,50 Z ";
layer_path += dotgrid.tool.index == 0 ? "M105,150 L105,150 L150,105 L195,150" : "";
layer_path += dotgrid.tool.index == 1 ? "M105,150 L195,150" : "";
layer_path += dotgrid.tool.index == 2 ? "M105,150 L105,150 L150,195 L195,150" : "";
document.getElementById("export").children[0].setAttribute("d",layer_path);
document.getElementById("line").className.baseVal = !dotgrid.tool.can_cast("line") ? "icon inactive" : "icon";
document.getElementById("arc_c").className.baseVal = !dotgrid.tool.can_cast("arc_c") ? "icon inactive" : "icon";
document.getElementById("arc_r").className.baseVal = !dotgrid.tool.can_cast("arc_r") ? "icon inactive" : "icon";
document.getElementById("bezier").className.baseVal = !dotgrid.tool.can_cast("bezier") ? "icon inactive" : "icon";
document.getElementById("close").className.baseVal = !dotgrid.tool.can_cast("close") ? "icon inactive" : "icon";
document.getElementById("thickness").className.baseVal = dotgrid.tool.layer().length < 1 ? "icon inactive" : "icon";
document.getElementById("linecap").className.baseVal = dotgrid.tool.layer().length < 1 ? "icon inactive" : "icon";
document.getElementById("linejoin").className.baseVal = dotgrid.tool.layer().length < 1 ? "icon inactive" : "icon";
document.getElementById("mirror").className.baseVal = dotgrid.tool.layer().length < 1 ? "icon inactive" : "icon";
document.getElementById("fill").className.baseVal = dotgrid.tool.layer().length < 1 ? "icon inactive" : "icon";
document.getElementById("export").className.baseVal = "icon";
}
this.update_size = function()
{
var size = this.zoom ? {width:600,height:600} : {width:300,height:300};
dotgrid.set_size(size,this.is_visible);
}
this.toggle = function()
{
this.is_visible = this.is_visible ? false : true;
this.el.className = this.is_visible ? "visible" : "hidden";
this.update_size();
}
this.toggle_zoom = function()
{
this.zoom = this.zoom ? false : true;
this.update_size();
}
}

View File

@@ -0,0 +1,196 @@
function Controller()
{
this.menu = {default:{}};
this.mode = "default";
this.app = require('electron').remote.app;
this.start = function()
{
}
this.add = function(mode,cat,label,fn,accelerator)
{
if(!this.menu[mode]){ this.menu[mode] = {}; }
if(!this.menu[mode][cat]){ this.menu[mode][cat] = {}; }
this.menu[mode][cat][label] = {fn:fn,accelerator:accelerator};
console.log(`${mode}/${cat}/${label} <${accelerator}>`);
}
this.add_role = function(mode,cat,label)
{
if(!this.menu[mode]){ this.menu[mode] = {}; }
if(!this.menu[mode][cat]){ this.menu[mode][cat] = {}; }
this.menu[mode][cat][label] = {role:label};
}
this.set = function(mode = "default")
{
this.mode = mode;
this.commit();
}
this.format = function()
{
var f = [];
var m = this.menu[this.mode];
for(cat in m){
var submenu = [];
for(name in m[cat]){
var option = m[cat][name];
if(option.role){
submenu.push({role:option.role})
}
else{
submenu.push({label:name,accelerator:option.accelerator,click:option.fn})
}
}
f.push({label:cat,submenu:submenu});
}
return f;
}
this.commit = function()
{
this.app.inject_menu(this.format());
}
this.docs = function()
{
console.log("Generating docs..");
var svg = this.generate_svg(this.format())
var txt = this.documentation(this.format());
dialog.showSaveDialog((fileName) => {
if (fileName === undefined){ return; }
fileName = fileName.substr(-4,4) != ".svg" ? fileName+".svg" : fileName;
fs.writeFile(fileName,svg);
fs.writeFile(fileName.replace(".svg",".md"),txt);
});
}
this.generate_svg = function(m)
{
var svg_html = "";
for(id in this.layout){
var key = this.layout[id];
var acc = this.accelerator_for_key(key.name,m);
svg_html += `<rect x="${key.x + 1}" y="${key.y + 1}" width="${key.width - 2}" height="${key.height - 2}" rx="4" ry="4" title="${key.name}" stroke="#ccc" fill="none" stroke-width="1"/>`;
svg_html += `<rect x="${key.x + 3}" y="${key.y + 3}" width="${key.width - 6}" height="${key.height - 12}" rx="3" ry="3" title="${key.name}" stroke="${acc.basic ? '#000' : acc.ctrl ? '#ccc' : '#fff'}" fill="${acc.basic ? '#000' : acc.ctrl ? '#ccc' : '#fff'}" stroke-width="1"/>`;
svg_html += `<text x="${key.x + 10}" y="${key.y + 20}" font-size='11' font-family='Input Mono' stroke-width='0' fill='${acc.basic ? '#fff' : '#000'}'>${key.name.toUpperCase()}</text>`;
svg_html += acc && acc.basic ? `<text x="${key.x + 10}" y="${key.y + 35}" font-size='7' font-family='Input Mono' stroke-width='0' fill='#fff'>${acc.basic}</text>` : '';
svg_html += acc && acc.ctrl ? `<text x="${key.x + 10}" y="${key.y + 45}" font-size='7' font-family='Input Mono' stroke-width='0' fill='#000'>${acc.ctrl}</text>` : '';
}
return `<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="900" height="300" version="1.0" style="fill:none;stroke:black;stroke-width:2px;">${svg_html}</svg>`;
}
this.documentation = function()
{
var txt = "";
txt += this.documentation_for_mode("default",this.menu.default);
for(name in this.menu){
if(name == "default"){ continue; }
txt += this.documentation_for_mode(name,this.menu[name]);
}
return txt;
}
this.documentation_for_mode = function(name,mode)
{
var txt = `## ${name} Mode\n\n`;
for(id in mode){
if(id == "*"){ continue; }
txt += `### ${id}\n`;
for(name in mode[id]){
var option = mode[id][name];
txt += `- ${name}: \`${option.accelerator}\`\n`;
}
txt += "\n"
}
return txt+"\n";
}
this.accelerator_for_key = function(key,menu)
{
var acc = {basic:null,ctrl:null}
for(cat in menu){
var options = menu[cat];
for(id in options.submenu){
var option = options.submenu[id]; if(option.role){ continue; }
acc.basic = (option.accelerator.toLowerCase() == key.toLowerCase()) ? option.label.toUpperCase().replace("TOGGLE ","").substr(0,8).trim() : acc.basic;
acc.ctrl = (option.accelerator.toLowerCase() == ("CmdOrCtrl+"+key).toLowerCase()) ? option.label.toUpperCase().replace("TOGGLE ","").substr(0,8).trim() : acc.ctrl;
}
}
return acc;
}
this.layout = [
{x:0, y:0, width:60, height:60, name:"esc"},
{x:60, y:0, width:60, height:60, name:"1"},
{x:120, y:0, width:60, height:60, name:"2"},
{x:180, y:0, width:60, height:60, name:"3"},
{x:240, y:0, width:60, height:60, name:"4"},
{x:300, y:0, width:60, height:60, name:"5"},
{x:360, y:0, width:60, height:60, name:"6"},
{x:420, y:0, width:60, height:60, name:"7"},
{x:480, y:0, width:60, height:60, name:"8"},
{x:540, y:0, width:60, height:60, name:"9"},
{x:600, y:0, width:60, height:60, name:"0"},
{x:660, y:0, width:60, height:60, name:"-"},
{x:720, y:0, width:60, height:60, name:"plus"},
{x:780, y:0, width:120, height:60, name:"backspace"},
{x:0, y:60, width:90, height:60, name:"tab"},
{x:90, y:60, width:60, height:60, name:"q"},
{x:150, y:60, width:60, height:60, name:"w"},
{x:210, y:60, width:60, height:60, name:"e"},
{x:270, y:60, width:60, height:60, name:"r"},
{x:330, y:60, width:60, height:60, name:"t"},
{x:390, y:60, width:60, height:60, name:"y"},
{x:450, y:60, width:60, height:60, name:"u"},
{x:510, y:60, width:60, height:60, name:"i"},
{x:570, y:60, width:60, height:60, name:"o"},
{x:630, y:60, width:60, height:60, name:"p"},
{x:690, y:60, width:60, height:60, name:"["},
{x:750, y:60, width:60, height:60, name:"]"},
{x:810, y:60, width:90, height:60, name:"|"},
{x:0, y:120, width:105, height:60, name:"caps"},
{x:105, y:120, width:60, height:60, name:"a"},
{x:165, y:120, width:60, height:60, name:"s"},
{x:225, y:120, width:60, height:60, name:"d"},
{x:285, y:120, width:60, height:60, name:"f"},
{x:345, y:120, width:60, height:60, name:"g"},
{x:405, y:120, width:60, height:60, name:"h"},
{x:465, y:120, width:60, height:60, name:"j"},
{x:525, y:120, width:60, height:60, name:"k"},
{x:585, y:120, width:60, height:60, name:"l"},
{x:645, y:120, width:60, height:60, name:";"},
{x:705, y:120, width:60, height:60, name:"'"},
{x:765, y:120, width:135, height:60, name:"enter"},
{x:0, y:180, width:135, height:60, name:"shift"},
{x:135, y:180, width:60, height:60, name:"z"},
{x:195, y:180, width:60, height:60, name:"x"},
{x:255, y:180, width:60, height:60, name:"c"},
{x:315, y:180, width:60, height:60, name:"v"},
{x:375, y:180, width:60, height:60, name:"b"},
{x:435, y:180, width:60, height:60, name:"n"},
{x:495, y:180, width:60, height:60, name:"m"},
{x:555, y:180, width:60, height:60, name:","},
{x:615, y:180, width:60, height:60, name:"."},
{x:675, y:180, width:60, height:60, name:"/"},
{x:735, y:180, width:165, height:60, name:"capslock"},
{x:0, y:240, width:90, height:60, name:"ctrl"},
{x:90, y:240, width:90, height:60, name:"cmd"},
{x:180, y:240, width:90, height:60, name:"alt"},
{x:270, y:240, width:270, height:60, name:"space"},
{x:810, y:240, width:90, height:60, name:"ctrl"},
{x:720, y:240, width:90, height:60, name:"pn"},
{x:630, y:240, width:90, height:60, name:"fn"},
{x:540, y:240, width:90, height:60, name:"alt"}
];
}
module.exports = new Controller();

View File

@@ -0,0 +1,50 @@
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; }
}

View File

@@ -0,0 +1,80 @@
function Theme()
{
var app = this;
this.el = document.createElement("style");
this.el.type = 'text/css';
this.default = {meta:{}, data: { background: "#222", f_high: "#fff", f_med: "#777", f_low: "#444", f_inv: "#000", b_high: "#000", b_med: "#affec7", b_low: "#000", b_inv: "#affec7" }}
this.active = this.default;
this.start = function()
{
this.load(localStorage.theme && localStorage.theme.background ? localStorage.theme : this.default);
window.addEventListener('dragover',this.drag_enter);
window.addEventListener('drop', this.drag);
document.head.appendChild(this.el)
}
this.load = function(t)
{
var theme = is_json(t) ? JSON.parse(t).data : t.data;
if(!theme.background){ return; }
var css = `
: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};
}`;
this.active = theme;
this.el.textContent = css;
localStorage.setItem("theme", JSON.stringify(theme));
}
this.reset = function()
{
this.load(this.default);
}
this.drag_enter = function(e)
{
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
}
this.drag = function(e)
{
e.preventDefault();
e.stopPropagation();
var file = e.dataTransfer.files[0];
if(!file.name || !file.name.indexOf(".thm") < 0){ console.log("Theme","Not a theme"); return; }
var reader = new FileReader();
reader.onload = function(e){
app.load(e.target.result);
};
reader.readAsText(file);
}
function is_json(text)
{
try{
JSON.parse(text);
return true;
}
catch (error){
return false;
}
}
}

View File

@@ -0,0 +1,47 @@
function Pos(x,y)
{
this.__serialized_name__ = ".";
this.x = x;
this.y = y;
this.toString = function()
{
return x+","+y;
}
this.sub = function(pos2)
{
return new Pos(this.x - pos2.x,this.y - pos2.y)
}
this.add = function(pos2)
{
return new Pos(this.x + pos2.x,this.y + pos2.y)
}
this.is_equal = function(pos2)
{
return Math.abs(pos2.x) == Math.abs(this.x) && Math.abs(pos2.y) == Math.abs(this.y);
}
this.scale = function(a)
{
return new Pos(this.x*a,this.y*a)
}
this.mirror = function(x = -1,y = 1)
{
return new Pos(this.x * x,this.y * y);
}
this.clamp = function(min,max)
{
return new Pos(clamp(this.x,min,max),clamp(this.y,min,max));
}
function clamp(v, min, max) { return v < min ? min : v > max ? max : v; }
}
// This is ugly, but Pos.__serialized_name__ == ".";
// Let's keep the character count low.
window["."] = Pos;

View File

@@ -0,0 +1,27 @@
function Render()
{
this.el = document.createElement("canvas"); this.el.id = "render";
this.img = document.createElement("img");
this.el.width = 1280; this.el.height = 1280;
this.draw = function()
{
var xml = new XMLSerializer().serializeToString(dotgrid.svg_el);
var svg64 = btoa(xml);
var b64Start = 'data:image/svg+xml;base64,';
var image64 = b64Start + svg64;
this.img.src = image64;
this.el.getContext('2d').clearRect(0, 0, 1280, 1280);
this.el.getContext('2d').drawImage(this.img, 0, 0, 1280, 1280);
}
this.buffer = function()
{
var fs = require('fs');
var data = this.el.toDataURL('image/png').replace(/^data:image\/\w+;base64,/, "");
var buf = new Buffer(data, 'base64');
return buf
}
}

View File

@@ -0,0 +1,241 @@
function Tool()
{
this.index = 0;
this.layers = [[],[],[]];
this.verteces = [];
this.reqs = {line:2,arc_c:2,arc_r:2,bezier:3,close:0};
this.reset = function()
{
this.layers = [[],[],[]];
this.verteces = [];
this.index = 0;
}
this.layer = function()
{
if(!this.layers[this.index]){
this.layers[this.index] = [];
}
return this.layers[this.index];
}
this.remove_segment = function()
{
if(this.verteces.length > 0){ this.clear(); return; }
this.layer().pop();
this.clear();
dotgrid.draw();
}
this.remove_segments_at = function(pos)
{
for(segment_id in this.layer()){
var segment = this.layer()[segment_id];
for(vertex_id in segment.verteces){
var vertex = segment.verteces[vertex_id];
if(Math.abs(pos.x) == Math.abs(vertex.x) && Math.abs(pos.y) == Math.abs(vertex.y)){
segment.verteces.splice(vertex_id,1)
}
}
if(segment.verteces.length < 2){
this.layers[this.index].splice(segment_id,1)
}
}
this.clear();
dotgrid.draw();
}
this.add_vertex = function(pos)
{
this.verteces.push(pos);
}
this.vertex_at = function(pos)
{
for(segment_id in this.layer()){
var segment = this.layer()[segment_id];
for(vertex_id in segment.verteces){
var vertex = segment.verteces[vertex_id];
if(vertex.x == Math.abs(pos.x) && vertex.y == Math.abs(pos.y)){
return vertex;
}
}
}
return null;
}
this.cast = function(type)
{
if(!this.layer()){ this.layers[this.index] = []; }
if(!this.can_cast(type)){ console.warn("Cannot cast"); return; }
var append_target = this.can_append({type:type,verteces:this.verteces.slice()})
if(append_target){
this.layers[this.index][append_target].verteces = this.layers[this.index][append_target].verteces.concat(this.verteces.slice())
}
else{
this.layer().push({type:type,verteces:this.verteces.slice()})
}
this.clear();
dotgrid.draw();
dotgrid.history.push(this.layers);
console.log(`Casted ${type} -> ${this.layer().length} elements`);
}
this.can_append = function(content)
{
for(id in this.layer()){
var stroke = this.layer()[id];
if(stroke.type != content.type){ continue; }
if(stroke.verteces[stroke.verteces.length-1].x != content.verteces[0].x){ continue; }
if(stroke.verteces[stroke.verteces.length-1].y != content.verteces[0].y){ continue; }
return id;
}
return false;
}
this.can_cast = function(type)
{
if(!type){ return false; }
// Cannot cast close twice
if(type == "close"){
var prev = this.layer()[this.layer().length-1];
if(!prev || prev.type == "close"){
return false;
}
}
return this.verteces.length >= this.reqs[type];
}
this.path = function(layer = this.layer())
{
if(layer.length > 0 && layer[0].type == "close"){ return ""; }
var html = "";
for(id in layer){
var segment = layer[id];
html += segment.type == "close" ? "Z " : this.render(segment);
}
return html
}
this.paths = function()
{
return [this.path(this.layers[0]),this.path(this.layers[1]),this.path(this.layers[2])]
}
this.render = function(segment)
{
var type = segment.type;
var verteces = segment.verteces;
var html = ``;
var skip = 0;
for(id in verteces){
if(skip > 0){ skip -= 1; continue; }
if(id == 0){ html += `M${verteces[id].x},${verteces[id].y} `; }
var vertex = verteces[id];
var next = verteces[parseInt(id)+1]
var after_next = verteces[parseInt(id)+2]
if(type == "line"){
html += `L${vertex.x},${vertex.y} `;
}
else if(type == "arc_c" && next){
html += `A${next.x - vertex.x},${next.y - vertex.y} 0 0,1 ${next.x},${next.y} `;
}
else if(type == "arc_r" && next){
html += `A${next.x - vertex.x},${next.y - vertex.y} 0 0,0 ${next.x},${next.y} `;
}
else if(type == "bezier" && next && after_next){
html += `Q${next.x},${next.y} ${after_next.x},${after_next.y} `;
skip = 1
}
}
return html
}
this.translate = function(a,b)
{
for(segment_id in this.layer()){
var segment = this.layer()[segment_id];
for(vertex_id in segment.verteces){
var vertex = segment.verteces[vertex_id];
if(vertex.x == Math.abs(a.x) && vertex.y == Math.abs(a.y)){
segment.verteces[vertex_id] = {x:Math.abs(b.x),y:Math.abs(b.y)};
}
}
}
dotgrid.history.push(this.layers);
this.clear();
dotgrid.draw();
}
this.clear = function()
{
this.verteces = [];
dotgrid.draw();
}
this.undo = function()
{
this.layers = dotgrid.history.prev();
dotgrid.draw();
}
this.redo = function()
{
this.layers = dotgrid.history.next();
dotgrid.draw();
}
this.export = function(target = this.layers)
{
return JSON.stringify(copy(target), null, 2);
}
this.replace = function(layers)
{
if(layers.length != 3){ console.log("Incompatible"); return; }
this.layers = layers;
this.clear();
dotgrid.draw();
dotgrid.history.push(this.layers);
}
this.import = function(layer)
{
this.layers[this.index] = this.layers[this.index].concat(layer)
dotgrid.history.push(this.layers);
this.clear();
dotgrid.draw();
}
this.select_layer = function(id)
{
this.index = clamp(id,0,2);
this.clear();
dotgrid.draw();
console.log(`layer:${this.index}`)
}
this.layer_up = function()
{
this.select_layer(this.index-1);
}
this.layer_down = function()
{
this.select_layer(this.index+1);
}
function copy(data){ return data ? JSON.parse(JSON.stringify(data)) : []; }
function clamp(v, min, max) { return v < min ? min : v > max ? max : v; }
}