Started Live version

This commit is contained in:
Devine Lu Linvega 2018-07-17 13:14:27 +12:00
parent 2d141a8806
commit 0b68bf94d6
16 changed files with 1544 additions and 1 deletions

View File

@ -23,7 +23,6 @@ function Tool()
this.layers = [[],[],[]];
this.vertices = [];
this.index = 0;
dotgrid.set_size({width:300,height:300})
}
this.clear = function()

28
index.html Normal file
View File

@ -0,0 +1,28 @@
<html>
<head>
<script type="text/javascript" src="web/scripts/lib/theme.js"></script>
<script type="text/javascript" src="web/scripts/lib/history.js"></script>
<script type="text/javascript" src="web/scripts/dotgrid.js"></script>
<script type="text/javascript" src="web/scripts/guide.js"></script>
<script type="text/javascript" src="web/scripts/renderer.js"></script>
<script type="text/javascript" src="web/scripts/interface.js"></script>
<script type="text/javascript" src="web/scripts/tool.js"></script>
<script type="text/javascript" src="web/scripts/generator.js"></script>
<script type="text/javascript" src="web/scripts/picker.js"></script>
<link rel="stylesheet" type="text/css" href="web/links/reset.css"/>
<link rel="stylesheet" type="text/css" href="web/links/fonts.css"/>
<link rel="stylesheet" type="text/css" href="web/links/main.css"/>
<title>Dotgrid</title>
</head>
<body>
<div id="app">
<script>
dotgrid = new Dotgrid(300,300,20,20,4,4);
dotgrid.install();
</script>
</div>
</body>
</html>

15
web/links/fonts.css Normal file
View File

@ -0,0 +1,15 @@
/* 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;
}

42
web/links/main.css Normal file
View File

@ -0,0 +1,42 @@
body { padding: 5px; font-family: 'input_mono_regular'; -webkit-user-select: none; overflow: hidden; padding-left:5px; transition: background 500ms}
/* Core */
#app { display: flex; flex-direction: column; align-items: center; -webkit-app-region: drag; padding-top:30px;}
#guide { position: absolute;width: 300px;height: 300px; transition: opacity 150ms; margin-left:50%;}
#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: 315px; position:fixed; bottom:20px; left:calc(50vw - 155px); height:30px;}
#interface svg.inactive { opacity: 0.2 }
#interface svg path.inactive { opacity: 0.2 }
#interface svg:hover { opacity: 0.5 }
#interface svg.icon:last-child { margin-right: 0; }
#interface svg path { fill:none; stroke-linecap: round; stroke-linejoin: round; stroke-width:12px; }
#interface.hidden { bottom:10px;opacity: 0 }
#interface.visible { bottom:20px; opacity: 1 }
#interface #menu { opacity: 1; position: absolute; top:0px; transition: all 250ms; z-index: 900}
#interface #picker { background:red; position: absolute; line-height: 30px; z-index: 0; width:250px; top:5px; opacity: 0; transition: all 250ms;}
#interface.picker #menu { opacity: 0; top:-5px; z-index: 0 }
#interface.picker #picker { opacity: 1; top:0px; z-index: 900 }
#interface .icon { width:30px; height:30px; margin-right:-2px; opacity: 1}
#interface .icon:hover { cursor: pointer; opacity: 1 }
/* Theme Overrides */
:root { --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"; }
body { background:var(--background) !important; }
#picker { background:var(--background) !important; color:var(--f_high) !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; }
.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; }

1
web/links/reset.css Normal file
View File

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

Binary file not shown.

Binary file not shown.

410
web/scripts/dotgrid.js Normal file
View File

@ -0,0 +1,410 @@
function Dotgrid(width,height,grid_x,grid_y,block_x,block_y)
{
this.theme = new Theme();
this.interface = new Interface();
this.history = new History();
this.guide = new Guide();
this.renderer = new Renderer();
this.tool = new Tool();
this.picker = new Picker();
this.grid_x = grid_x;
this.grid_y = grid_y;
this.block_x = block_x;
this.block_y = block_y;
this.cursor = { pos:{x:0,y:0},translation:null,multi:false,updated:0 }
this.install = function()
{
document.getElementById("app").appendChild(this.guide.el);
this.theme.start();
this.tool.start();
this.guide.start();
this.interface.start();
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);
this.new();
}
// File
this.new = function()
{
this.history.push(this.tool.layers);
this.clear();
}
this.open = function()
{
var paths = dialog.showOpenDialog({properties: ['openFile'],filters:[{name:"Dotgrid Image",extensions:["dot","grid"]}]});
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; }
this.tool.replace(JSON.parse(data.toString().trim()));
this.guide.refresh();
});
}
this.save = function(content = this.tool.export())
{
dialog.showSaveDialog({
title:"Save to .grid",
filters: [{name: "Dotgrid", extensions: ["grid", "dot"]}]
},(fileName) => {
if (fileName === undefined){ return; }
fileName = fileName.substr(-5,5) != ".grid" ? fileName+".grid" : fileName;
fs.writeFileSync(fileName, content);
this.guide.refresh()
});
}
this.refresh = function()
{
this.set_size({width:window.innerWidth - 120,height:window.innerHeight- 120})
}
this.render = function(content = this.renderer.to_png({width:dotgrid.tool.settings.size.width*2,height:dotgrid.tool.settings.size.height*2}), ready = null, size = null)
{
if(!ready){return; }
dialog.showSaveDialog({title:"Render to .png"},(fileName) => {
if (fileName === undefined){ return; }
fileName = fileName.substr(-4,4) != ".png" ? fileName+".png" : fileName;
console.log(`Rendered ${size.width}x${size.height}`)
fs.writeFileSync(fileName, ready);
});
}
this.export = function(content = this.renderer.to_svg())
{
dialog.showSaveDialog({title:"Export to .svg"},(fileName) => {
if (fileName === undefined){ return; }
fileName = fileName.substr(-4,4) != ".svg" ? fileName+".svg" : fileName;
fs.writeFileSync(fileName, content);
this.guide.refresh()
});
}
this.bundle = {}
this.build = function()
{
this.bundle = {}
var sizes = [
{width:16,height:16},
{width:32,height:32},
{width:52,height:52},
{width:64,height:64},
{width:72,height:72},
{width:96,height:96},
{width:128,height:128},
{width:256,height:256},
{width:512,height:512}
]
for(id in sizes){
this.renderer.to_png(sizes[id],dotgrid.package)
}
}
this.package = function(n = null, ready,size)
{
dotgrid.bundle[`${size.width}x${size.height}`] = ready
console.log(`Rendered ${size.width}x${size.height}`,`${Object.keys(dotgrid.bundle).length}/9`)
if(Object.keys(dotgrid.bundle).length == 9){
dialog.showSaveDialog({title:"Export to Icons"},(fileName) => {
if (fileName === undefined){ return; }
for(id in dotgrid.bundle){
fs.writeFileSync(`${fileName}.${id}.png`, dotgrid.bundle[id]);
}
});
}
}
// Cursor
this.mouse_down = function(e)
{
var o = e.target.getAttribute("ar");
if(o){
if(o == "line"){ this.tool.cast("line"); return; }
if(o == "arc_c"){ this.tool.cast("arc_c"); return;}
if(o == "arc_r"){ this.tool.cast("arc_r"); return; }
if(o == "bezier"){ this.tool.cast("bezier"); return; }
if(o == "close"){ this.tool.cast("close"); return; }
if(o == "thickness"){ this.mod_thickness(10,true,true); return; }
if(o == "linecap"){ this.mod_linecap(); return; }
if(o == "linejoin"){ this.mod_linejoin(); return; }
if(o == "mirror"){ this.tool.toggle_mirror(); return; }
if(o == "fill"){ this.mod_fill(); return; }
if(o == "color"){ setTimeout(()=>{ this.picker.start(); }, 100); return; }
if(o == "depth"){ this.tool.select_next_layer(); return; }
e.preventDefault();
}
var pos = this.position_in_grid({x:e.clientX+5,y: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.cursor.translation = {from:pos,to:pos};
if(e.shiftKey){ console.log("Begin translation(multi)"); dotgrid.cursor.multi = true; }
}
dotgrid.guide.refresh();
dotgrid.interface.refresh();
}
this.mouse_move = function(e)
{
var pos = this.position_in_grid({x:e.clientX+5,y:e.clientY-5}); pos = this.position_on_grid(pos);
this.cursor.pos = pos;
this.cursor.updated = new Date().getTime();
this.cursor.operation = e.target.getAttribute("ar");
if(dotgrid.cursor.translation && (Math.abs(dotgrid.cursor.translation.from.x) != Math.abs(pos.x) || Math.abs(dotgrid.cursor.translation.from.y) != Math.abs(pos.y))){ dotgrid.cursor.translation.to = pos; }
dotgrid.guide.refresh();
dotgrid.interface.refresh();
e.preventDefault();
}
this.mouse_up = function(e)
{
if(e.target.getAttribute("ar")){ return } // If clicking on interface
var pos = this.position_in_grid({x:e.clientX+5,y:e.clientY-5});
pos = this.position_on_grid(pos);
if(e.altKey || e.target.id != "guide"){ return; }
if(pos.x > 0) { dotgrid.cursor.translation = null; return; }
if(dotgrid.cursor.translation && (Math.abs(dotgrid.cursor.translation.from.x) != Math.abs(dotgrid.cursor.translation.to.x) || Math.abs(dotgrid.cursor.translation.from.y) != Math.abs(dotgrid.cursor.translation.to.y))){
if(dotgrid.cursor.multi){
dotgrid.tool.translate_multi(dotgrid.cursor.translation.from,dotgrid.cursor.translation.to);
}
else{
dotgrid.tool.translate(dotgrid.cursor.translation.from,dotgrid.cursor.translation.to);
}
dotgrid.cursor.translation = null;
dotgrid.cursor.multi = null;
dotgrid.guide.refresh();
e.preventDefault();
return;
}
this.tool.add_vertex({x:pos.x * -1,y:pos.y});
dotgrid.cursor.translation = null;
dotgrid.interface.refresh();
dotgrid.guide.refresh();
e.preventDefault();
}
this.mouse_alt = function(e)
{
var pos = this.position_in_grid({x:e.clientX+5,y:e.clientY-5}); pos = this.position_on_grid(pos);
dotgrid.tool.remove_segments_at(pos);
e.preventDefault();
setTimeout(() => { dotgrid.tool.clear(); },150);
}
// Toggles
this.mod_thickness = function(mod = 10,step = false,cap = false)
{
if(cap){
this.tool.style().thickness = this.tool.style().thickness > 40 ? 1 : this.tool.style().thickness
}
if(step){
this.tool.style().thickness = parseInt(this.tool.style().thickness/5) * 5;
}
this.tool.style().thickness = clamp(this.tool.style().thickness+mod,1,40);
dotgrid.guide.refresh();
}
this.mod_linecap_index = 1;
this.mod_linecap = function(mod)
{
var a = ["butt","square","round"];
this.mod_linecap_index += 1;
this.tool.style().strokeLinecap = a[this.mod_linecap_index % a.length];
dotgrid.guide.refresh();
}
this.mod_linejoin_index = 1;
this.mod_linejoin = function(mod)
{
var a = ["miter","round","bevel"];
this.mod_linejoin_index += 1;
this.tool.style().strokeLinejoin = a[this.mod_linejoin_index % a.length];
dotgrid.guide.refresh();
}
this.mod_fill = function()
{
this.tool.style().fill = this.tool.style().fill == "none" ? this.tool.style().color : "none";
dotgrid.guide.refresh();
}
// Basics
this.set_size = function(size = {width:300,height:300},interface = true,scale = 1)
{
size = { width:clamp(parseInt(size.width/15)*15,120,1000),height:clamp(parseInt(size.height/15)*15,120,1000)}
if(this.tool.settings.size.width == size.width && this.tool.settings.size.height == size.height){ return; }
console.log(`Setting size: ${size.width}x${size.height}`)
this.tool.settings.size.width = size.width
this.tool.settings.size.height = size.height
this.grid_x = size.width/15
this.grid_y = size.height/15
this.grid_width = this.tool.settings.size.width/this.grid_x;
this.grid_height = this.tool.settings.size.height/this.grid_y;
dotgrid.guide.resize(size);
this.interface.refresh();
dotgrid.guide.refresh();
}
// Draw
this.reset = function()
{
this.tool.clear();
}
this.clear = function()
{
this.refresh();
this.history.clear();
this.tool.reset();
this.reset();
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
}
this.drag = function(e)
{
e.preventDefault();
e.stopPropagation();
var file = e.dataTransfer.files[0];
if(!file.path || file.path.indexOf(".dot") < 0 && file.path.indexOf(".grid") < 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.guide.refresh();
};
reader.readAsText(file);
}
this.copy = function(e)
{
dotgrid.guide.refresh();
e.clipboardData.setData('text/source', dotgrid.tool.export(dotgrid.tool.layer()));
e.clipboardData.setData('text/plain', dotgrid.tool.path());
e.clipboardData.setData('text/html', dotgrid.renderer.to_svg());
e.clipboardData.setData('text/svg+xml', dotgrid.renderer.to_svg());
dotgrid.guide.refresh();
}
this.cut = function(e)
{
dotgrid.guide.refresh();
e.clipboardData.setData('text/plain', dotgrid.tool.export(dotgrid.tool.layer()));
e.clipboardData.setData('text/html', dotgrid.renderer.to_svg());
e.clipboardData.setData('text/svg+xml', dotgrid.renderer.to_svg());
dotgrid.tool.layers[dotgrid.tool.index] = [];
dotgrid.guide.refresh();
}
this.paste = function(e)
{
var data = e.clipboardData.getData("text/source");
if(is_json(data)){
data = JSON.parse(data.trim());
dotgrid.tool.import(data);
}
dotgrid.guide.refresh();
}
// Normalizers
this.position_in_grid = function(pos)
{
return {x:(window.innerWidth/2) - (this.tool.settings.size.width/2) - pos.x,y:pos.y - (30+10)}
}
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
x = clamp(x * -1,0,this.tool.settings.size.width)
y = clamp(y,0,this.tool.settings.size.height)
return {x:x*-1,y:y};
}
function is_json(text){ try{ JSON.parse(text);return true; } catch(error){ return false; }}
function pos_is_equal(a,b){ return a && b && a.x == b.x && a.y == b.y }
function clamp(v, min, max) { return v < min ? min : v > max ? max : v; }
}
window.addEventListener('resize', function(e)
{
dotgrid.refresh()
}, false);
window.addEventListener('dragover',function(e)
{
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
String.prototype.capitalize = function()
{
return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
}

114
web/scripts/generator.js Normal file
View File

@ -0,0 +1,114 @@
function Generator(layer,style)
{
this.layer = layer;
this.style = style;
function operate(layer,offset,scale,mirror = 0,angle = 0)
{
var l = copy(layer)
for(k1 in l){
var seg = l[k1];
for(k2 in seg.vertices){
if(mirror == 1){ seg.vertices[k2].x = (dotgrid.tool.settings.size.width) - seg.vertices[k2].x }
if(mirror == 2){ seg.vertices[k2].y = (dotgrid.tool.settings.size.height) - seg.vertices[k2].y }
// Offset
seg.vertices[k2].x += offset.x
seg.vertices[k2].y += offset.y
// Rotate
var center = {x:(dotgrid.tool.settings.size.width/2)+offset.x,y:(dotgrid.tool.settings.size.height/2)+offset.y}
seg.vertices[k2] = rotate_point(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)
{
var type = segment.type;
var vertices = segment.vertices;
var html = '';
var skip = 0;
for(id in vertices){
if(skip > 0){ skip -= 1; continue; }
var vertex = vertices[id]
var next = vertices[parseInt(id)+1]
var after_next = vertices[parseInt(id)+2]
if(id == 0 && !prev || id == 0 && prev && (prev.x != vertex.x || prev.y != vertex.y)){
html += `M${vertex.x},${vertex.y} `
}
if(type == "line"){
html += `L${vertex.x},${vertex.y} `;
}
else if(type == "arc_c"){
var clock = mirror > 0 ? '0,0' : '0,1'
html += next ? `A${Math.abs(next.x - vertex.x)},${Math.abs(next.y - vertex.y)} 0 ${clock} ${next.x},${next.y} ` : '';
}
else if(type == "arc_r"){
var clock = mirror > 0 ? '0,1' : '0,0'
html += next ? `A${Math.abs(next.x - vertex.x)},${Math.abs(next.y - vertex.y)} 0 ${clock} ${next.x},${next.y} ` : '';
}
else if(type == "bezier"){
html += next && after_next ?`Q${next.x},${next.y} ${after_next.x},${after_next.y} ` : '';
skip = 1
}
else{
console.warn(`unknown type:${type}`)
}
}
if(segment.type == "close"){
html += "Z "
}
return html
}
this.convert = function(layer,mirror,angle)
{
var s = ""
var prev = null
for(id in layer){
var seg = layer[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)
{
var s = this.convert(operate(this.layer,offset,scale))
if(mirror == 1 || mirror == 2){
s += this.convert(operate(this.layer,offset,scale,mirror),mirror)
}
if(mirror == 3){
s += this.convert(operate(this.layer,offset,scale,mirror,120),mirror)
s += this.convert(operate(this.layer,offset,scale,mirror,240),mirror)
}
if(mirror == 4){
s += this.convert(operate(this.layer,offset,scale,mirror,72),mirror)
s += this.convert(operate(this.layer,offset,scale,mirror,144),mirror)
s += this.convert(operate(this.layer,offset,scale,mirror,216),mirror)
s += this.convert(operate(this.layer,offset,scale,mirror,288),mirror)
}
return s
}
function copy(data){ return data ? JSON.parse(JSON.stringify(data)) : []; }
function rotate_point(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) }; }
}

232
web/scripts/guide.js Normal file
View File

@ -0,0 +1,232 @@
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.show_extras = true;
var scale = 2;
this.start = function()
{
this.clear();
this.refresh();
}
this.refresh = function()
{
this.clear();
if(dotgrid.tool.index == 2){ this.draw_markers() ; this.draw_vertices() }
this.draw_path(new Generator(dotgrid.tool.layers[2],dotgrid.tool.styles[2]).toString({x:15,y:15},scale),dotgrid.tool.styles[2])
if(dotgrid.tool.index == 1){ this.draw_markers() ; this.draw_vertices() }
this.draw_path(new Generator(dotgrid.tool.layers[1],dotgrid.tool.styles[1]).toString({x:15,y:15},scale),dotgrid.tool.styles[1])
if(dotgrid.tool.index == 0){ this.draw_markers(); this.draw_vertices() }
this.draw_path(new Generator(dotgrid.tool.layers[0],dotgrid.tool.styles[0]).toString({x:15,y:15},scale),dotgrid.tool.styles[0])
this.draw_handles()
this.draw_translation();
this.draw_cursor();
this.draw_preview();
}
this.clear = function()
{
this.el.getContext('2d').clearRect(0, 0, this.el.width*scale, this.el.height*scale);
}
this.toggle = function()
{
this.show_extras = this.show_extras ? false : true;
this.refresh()
}
this.resize = function(size)
{
var offset = 30
this.el.width = (size.width+offset)*scale;
this.el.height = (size.height+offset)*scale;
this.el.style.width = (size.width+offset)+"px";
this.el.style.height = (size.height+offset)+"px";
this.el.style.left = (size.width/2) * -1
this.refresh();
}
this.draw_handles = function()
{
if(!this.show_extras){ return; }
for(segment_id in dotgrid.tool.layer()){
var segment = dotgrid.tool.layer()[segment_id];
for(vertex_id in segment.vertices){
var vertex = segment.vertices[vertex_id];
this.draw_handle(vertex);
}
}
}
this.draw_vertices = function()
{
for(id in dotgrid.tool.vertices){
this.draw_vertex(dotgrid.tool.vertices[id]);
}
}
this.draw_markers = function()
{
if(!this.show_extras){ return; }
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;
this.draw_marker({x:pos_x,y:pos_y},radius,is_step);
}
}
}
this.draw_vertex = function(pos, radius = 5)
{
var ctx = this.el.getContext('2d');
ctx.beginPath();
ctx.lineWidth = 2;
ctx.arc((pos.x * scale)+30, (pos.y * scale)+30, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = dotgrid.theme.active.f_med;
ctx.fill();
ctx.closePath();
}
this.draw_handle = function(pos, radius = 6)
{
var ctx = this.el.getContext('2d');
ctx.beginPath();
ctx.setLineDash([0,0]);
ctx.lineWidth = 3;
ctx.lineCap="round";
ctx.arc(Math.abs(pos.x * -scale)+30, Math.abs(pos.y * scale)+30, radius+3, 0, 2 * Math.PI, false);
ctx.fillStyle = dotgrid.theme.active.f_high;
ctx.fill();
ctx.strokeStyle = dotgrid.theme.active.f_high;
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc((pos.x * scale)+30, (pos.y * scale)+30, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = dotgrid.theme.active.f_low;
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc((pos.x * scale)+30, (pos.y * scale)+30, radius-3, 0, 2 * Math.PI, false);
ctx.fillStyle = dotgrid.theme.active.f_high;
ctx.fill();
ctx.closePath();
}
this.draw_marker = function(pos,radius = 1,step)
{
var ctx = this.el.getContext('2d');
ctx.beginPath();
ctx.lineWidth = 2;
ctx.arc(pos.x * scale, pos.y * scale, 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_path = function(path,style)
{
var ctx = this.el.getContext('2d');
var p = new Path2D(path);
ctx.setLineDash([0,0]);
ctx.strokeStyle = style.color;
ctx.lineWidth = style.thickness * scale;
ctx.lineCap = style.strokeLinecap;
ctx.lineJoin = style.strokeLinejoin;
if(style.fill && style.fill != "none"){
ctx.fillStyle = style.color
ctx.fill(p);
}
if(style.strokeLineDash){
ctx.setLineDash(style.strokeLineDash);
}
ctx.stroke(p);
}
this.draw_translation = function()
{
if(!dotgrid.cursor.translation){ return; }
// From
var ctx = this.el.getContext('2d');
var from = dotgrid.cursor.translation.from;
var to = dotgrid.cursor.translation.to;
if(to.x<=0) {
ctx.beginPath();
ctx.setLineDash([0,0]);
ctx.moveTo((from.x * -scale)+30,(from.y * scale)+30);
ctx.lineTo((to.x * -scale)+30,(to.y * scale)+30);
ctx.lineCap="round";
ctx.lineWidth = 5;
ctx.strokeStyle = dotgrid.theme.active.b_inv;
ctx.stroke();
ctx.closePath();
}
}
this.draw_cursor = function(pos = dotgrid.cursor.pos,radius = dotgrid.tool.style().thickness-1)
{
var ctx = this.el.getContext('2d');
ctx.beginPath();
ctx.setLineDash([0,0]);
ctx.lineWidth = 3;
ctx.lineCap="round";
ctx.arc(Math.abs(pos.x * -scale)+30, Math.abs(pos.y * scale)+30, 3, 0, 2 * Math.PI, false);
ctx.fillStyle = dotgrid.theme.active.f_low;
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.setLineDash([0,0]);
ctx.lineWidth = 3;
ctx.lineCap="round";
ctx.arc(Math.abs(pos.x * -scale)+30, Math.abs(pos.y * scale)+30, clamp(radius,5,100), 0, 2 * Math.PI, false);
ctx.strokeStyle = dotgrid.theme.active.f_med;
ctx.stroke();
ctx.closePath();
}
this.draw_preview = function()
{
var operation = dotgrid.cursor.operation
if(!dotgrid.tool.can_cast(operation)){ return; }
if(operation == "close"){ return; }
var path = new Generator([{vertices:dotgrid.tool.vertices,type:operation}]).toString({x:15,y:15},2)
var style = {
color:dotgrid.theme.active.f_med,
thickness:2,
strokeLinecap:"round",
strokeLinejoin:"round",
strokeLineDash:[5, 15]
}
this.draw_path(path,style)
}
function pos_is_equal(a,b){ return a && b && Math.abs(a.x) == Math.abs(b.x) && Math.abs(a.y) == Math.abs(b.y) }
function clamp(v, min, max) { return v < min ? min : v > max ? max : v; }
}

79
web/scripts/interface.js Normal file
View File

@ -0,0 +1,79 @@
function Interface()
{
this.el = document.createElement("div");
this.el.id = "interface";
this.el.appendChild(this.menu_el = document.createElement("div"));
this.menu_el.id = "menu";
this.is_visible = true;
this.zoom = false;
this.start = function()
{
document.getElementById("app").appendChild(this.el);
this.el.appendChild(dotgrid.picker.el);
var html = ""
var tools = {
line: ["line","M60,60 L240,240","A"],
arc_c: ["arc clockwise","M60,60 A180,180 0 0,1 240,240","S"],
arc_r: ["arc reverse","M60,60 A180,180 0 0,0 240,240","D"],
bezier: ["bezier","M60,60 Q60,150 150,150 Q240,150 240,240","F"],
close: ["close","M60,60 A180,180 0 0,1 240,240 M60,60 A180,180 0 0,0 240,240","Z"],
linecap: ["linecap","M60,60 L60,60 L180,180 L240,180 L240,240 L180,240 L180,180","Q"],
linejoin: ["linejoin","M60,60 L120,120 L180,120 M120,180 L180,180 L240,240","W"],
thickness: ["thickness","M120,90 L120,90 L90,120 L180,210 L210,180 Z M105,105 L105,105 L60,60 M195,195 L195,195 L240,240"],
mirror: ["mirror","M60,60 L60,60 L120,120 M180,180 L180,180 L240,240 M210,90 L210,90 L180,120 M120,180 L120,180 L90,210","E"],
fill: ["fill","M60,60 L60,150 L150,150 L240,150 L240,240 Z","R"],
color: ["color","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","G"],
}
for(id in tools){
var tool = tools[id];
var shortcut = tool[2];
html += `<svg id="${id}" ar="${id}" title="${tool[0].capitalize()}" viewBox="0 0 300 300" class="icon"><path id="${id}_path" class="icon_path" d="${tool[1]}"/>${id == "depth" ? `<path class="icon_path inactive" d=""/>` : ""}<rect ar="${id}" width="300" height="300" opacity="0"><title>${id.capitalize()}${shortcut ? '('+shortcut+')' : ''}</title></rect></svg>`
}
this.menu_el.innerHTML = html
}
this.prev_operation = null;
this.refresh = function(force = false)
{
if(this.prev_operation == dotgrid.cursor.operation && force == false){ return; }
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("color").children[0].style.fill = dotgrid.tool.style().color;
document.getElementById("color").children[0].style.stroke = dotgrid.tool.style().color;
document.getElementById("color").className.baseVal = "icon";
// Mirror
if(dotgrid.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(dotgrid.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(dotgrid.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(dotgrid.tool.style().mirror_style == 3){ document.getElementById("mirror_path").setAttribute("d","M60,60 L60,60 L120,120 L120,120 L180,120 M120,150 L120,150 L180,150 M120,180 L120,180 L180,180 L180,180 L240,240 ") }
else if(dotgrid.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 = dotgrid.cursor.operation;
}
this.toggle = function()
{
this.is_visible = this.is_visible ? false : true;
this.el.className = this.is_visible ? "visible" : "hidden";
}
}

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; }
}

86
web/scripts/lib/theme.js Normal file
View File

@ -0,0 +1,86 @@
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 : this.default, this.default);
window.addEventListener('dragover',this.drag_enter);
window.addEventListener('drop', this.drag);
document.head.appendChild(this.el)
}
this.load = function(t, fall_back)
{
var theme = is_json(t) ? JSON.parse(t).data : t.data;
if(!theme || !theme.background){
if(fall_back) {
theme = fall_back.data;
} else {
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({data: 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;
}
}
}

114
web/scripts/picker.js Normal file
View File

@ -0,0 +1,114 @@
function Picker()
{
this.memory = "";
this.el = document.createElement("input");
this.el.id = "picker"
this.original = null;
this.start = function()
{
this.el.setAttribute("placeholder",`${dotgrid.tool.style().color} ${dotgrid.tool.settings.size.width}x${dotgrid.tool.settings.size.height}`)
dotgrid.controller.set("picker");
dotgrid.interface.el.className = "picker"
this.el.focus()
this.original = dotgrid.tool.style().color
this.el.value = ""
}
this.stop = function()
{
this.cancel();
dotgrid.controller.set();
dotgrid.interface.el.className = ""
this.el.blur()
this.el.value = ""
}
this.validate = function()
{
var parts = this.parse(this.el.value)
if(parts.color){ this.set_color(parts.color); }
if(parts.size){ this.set_size(parts.size); }
dotgrid.guide.refresh();
dotgrid.controller.set();
dotgrid.interface.el.className = ""
this.el.blur()
this.el.value = ""
}
this.set_color = function(color)
{
dotgrid.tool.style().color = color;
dotgrid.tool.style().fill = dotgrid.tool.style().fill != "none" ? color : "none";
}
this.set_size = function(size)
{
dotgrid.set_size(size);
}
this.cancel = function()
{
if(!this.original){ return; }
dotgrid.tool.style().color = this.original;
dotgrid.tool.style().fill = dotgrid.tool.style().fill != "none" ? this.original : "none";
dotgrid.guide.refresh();
}
this.update = function()
{
var parts = this.parse(this.el.value)
if(!parts.color){ return; }
dotgrid.tool.style().color = parts.color;
dotgrid.tool.style().fill = dotgrid.tool.style().fill != "none" ? parts.color : "none";
dotgrid.guide.refresh();
}
this.listen = function(e)
{
if(e.key == "Enter"){
this.validate();
e.preventDefault();
return;
}
this.update();
}
this.parse = function(value)
{
var parts = value.split(" ");
var color = null;
var size = null;
for(id in parts){
var part = parts[id];
if(is_color(part) && !color){ color = part; }
if(is_size(part) && !size){ size = { width:parseInt(part.toLowerCase().split("x")[0]),height:parseInt(part.toLowerCase().split("x")[1]) }; }
}
return {color:color,size:size}
}
function is_size(val)
{
if(val.toLowerCase().indexOf("x") < 1){ return false; }
return true
}
function is_color(val)
{
if(val.length != 4 && val.length != 7){
return false
}
var re = /\#[0-9A-Fa-f]/g;
return re.test(val)
}
this.el.onkeyup = function(event){ dotgrid.picker.listen(event); };
}

87
web/scripts/renderer.js Normal file
View File

@ -0,0 +1,87 @@
function Renderer()
{
// Create SVG parts
this.svg_el = document.createElementNS("http://www.w3.org/2000/svg", "svg");
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.fill = "none";
this.layer_1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
this.layer_2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
this.layer_3 = document.createElementNS("http://www.w3.org/2000/svg", "path");
this.svg_el.appendChild(this.layer_3);
this.svg_el.appendChild(this.layer_2);
this.svg_el.appendChild(this.layer_1);
this.refresh = function()
{
this.svg_el.setAttribute("width",dotgrid.tool.settings.size.width+"px");
this.svg_el.setAttribute("height",dotgrid.tool.settings.size.height+"px");
this.svg_el.style.width = dotgrid.tool.settings.size.width;
this.svg_el.style.height = dotgrid.tool.settings.size.height;
this.svg_el.style.strokeWidth = dotgrid.tool.style().thickness;
var styles = dotgrid.tool.styles
var paths = dotgrid.tool.paths()
this.layer_1.style.strokeWidth = styles[0].thickness;
this.layer_1.style.strokeLinecap = styles[0].strokeLinecap;
this.layer_1.style.strokeLinejoin = styles[0].strokeLinejoin;
this.layer_1.style.stroke = styles[0].color;
this.layer_1.style.fill = styles[0].fill;
this.layer_1.setAttribute("d",paths[0])
this.layer_2.style.strokeWidth = styles[1].thickness;
this.layer_2.style.strokeLinecap = styles[1].strokeLinecap;
this.layer_2.style.strokeLinejoin = styles[1].strokeLinejoin;
this.layer_2.style.stroke = styles[1].color;
this.layer_2.style.fill = styles[1].fill;
this.layer_2.setAttribute("d",paths[1])
this.layer_3.style.strokeWidth = styles[2].thickness;
this.layer_3.style.strokeLinecap = styles[2].strokeLinecap;
this.layer_3.style.strokeLinejoin = styles[2].strokeLinejoin;
this.layer_3.style.stroke = styles[2].color;
this.layer_3.style.fill = styles[2].fill;
this.layer_3.setAttribute("d",paths[2])
}
this.to_png = function(size = dotgrid.tool.settings.size,callback = dotgrid.render)
{
this.refresh();
var xml = new XMLSerializer().serializeToString(this.svg_el);
var svg64 = btoa(xml);
var b64Start = 'data:image/svg+xml;base64,';
var image64 = b64Start + svg64;
var img = new Image;
var canvas = document.createElement("canvas");
canvas.width = size.width;
canvas.height = size.height;
var ctx = canvas.getContext('2d');
img.onload = function(){
ctx.drawImage(img, 0, 0, size.width, size.height);
var data = canvas.toDataURL('image/png').replace(/^data:image\/\w+;base64,/, "");
dotgrid.renderer.to_png_ready(callback, new Buffer(data, 'base64'),size)
};
img.src = image64;
}
this.to_png_ready = function(callback, buffer, size)
{
callback(null,buffer,size)
}
this.to_svg = function()
{
this.refresh();
return this.svg_el.outerHTML;
}
}

286
web/scripts/tool.js Normal file
View File

@ -0,0 +1,286 @@
function Tool()
{
this.index = 0;
this.settings = { size:{width:300,height:300} }
this.layers = [[],[],[]];
this.styles = [
{ thickness:10,strokeLinecap:"round",strokeLinejoin:"round",color:"#f00",fill:"none",mirror_style:0 },
{ thickness:10,strokeLinecap:"round",strokeLinejoin:"round",color:"#0f0",fill:"none",mirror_style:0 },
{ thickness:10,strokeLinecap:"round",strokeLinejoin:"round",color:"#00f",fill:"none",mirror_style:0 }
];
this.vertices = [];
this.reqs = { line:2,arc_c:2,arc_r:2,bezier:3,close:0 };
this.start = function()
{
this.styles[0].color = dotgrid.theme.active.f_high
this.styles[1].color = dotgrid.theme.active.f_med
this.styles[2].color = dotgrid.theme.active.f_low
}
this.reset = function()
{
this.layers = [[],[],[]];
this.vertices = [];
this.index = 0;
}
this.clear = function()
{
this.vertices = [];
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
}
this.undo = function()
{
this.layers = dotgrid.history.prev();
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
}
this.redo = function()
{
this.layers = dotgrid.history.next();
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
}
// 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)
dotgrid.history.push(this.layers);
this.clear();
dotgrid.guide.refresh();
dotgrid.interface.refresh(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}
}
if(this.settings && (this.settings.size.width != dot.settings.size.width || this.settings.size.height != dot.settings.size.height)){
dotgrid.set_size({width:dot.settings.size.width,height:dot.settings.size.height})
}
this.layers = dot.layers;
this.styles = dot.styles;
this.settings = dot.settings;
this.clear();
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
dotgrid.history.push(this.layers);
}
// EDIT
this.remove_segment = function()
{
if(this.vertices.length > 0){ this.clear(); return; }
this.layer().pop();
this.clear();
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
}
this.remove_segments_at = function(pos)
{
for(segment_id in this.layer()){
var segment = this.layer()[segment_id];
for(vertex_id in segment.vertices){
var vertex = segment.vertices[vertex_id];
if(Math.abs(pos.x) == Math.abs(vertex.x) && Math.abs(pos.y) == Math.abs(vertex.y)){
segment.vertices.splice(vertex_id,1)
}
}
if(segment.vertices.length < 2){
this.layers[this.index].splice(segment_id,1)
}
}
this.clear();
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
}
this.add_vertex = function(pos)
{
pos = {x:Math.abs(pos.x),y:Math.abs(pos.y)}
this.vertices.push(pos);
dotgrid.interface.refresh(true);
}
this.vertex_at = function(pos)
{
for(segment_id in this.layer()){
var segment = this.layer()[segment_id];
for(vertex_id in segment.vertices){
var vertex = segment.vertices[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,vertices:this.vertices.slice()})
if(append_target){
this.layers[this.index][append_target].vertices = this.layers[this.index][append_target].vertices.concat(this.vertices.slice())
}
else{
this.layer().push({type:type,vertices:this.vertices.slice()})
}
dotgrid.history.push(this.layers);
this.clear();
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
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.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.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;
}
}
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()
{
var l1 = new Generator(dotgrid.tool.layers[0],dotgrid.tool.styles[0]).toString({x:0,y:0},1)
var l2 = new Generator(dotgrid.tool.layers[1],dotgrid.tool.styles[1]).toString({x:0,y:0},1)
var l3 = new Generator(dotgrid.tool.layers[2],dotgrid.tool.styles[2]).toString({x:0,y:0},1)
return [l1,l2,l3]
}
this.path = function()
{
return new Generator(dotgrid.tool.layer(),dotgrid.tool.style()).toString({x:0,y:0},1)
}
this.translate = function(a,b)
{
for(segment_id in this.layer()){
var segment = this.layer()[segment_id];
for(vertex_id in segment.vertices){
var vertex = segment.vertices[vertex_id];
if(vertex.x == Math.abs(a.x) && vertex.y == Math.abs(a.y)){
segment.vertices[vertex_id] = {x:Math.abs(b.x),y:Math.abs(b.y)};
}
}
}
dotgrid.history.push(this.layers);
this.clear();
dotgrid.guide.refresh();
}
this.translate_multi = function(a,b)
{
var offset = {x:a.x - b.x,y:a.y - b.y}
for(segment_id in this.layer()){
var segment = this.layer()[segment_id];
for(vertex_id in segment.vertices){
var vertex = segment.vertices[vertex_id];
segment.vertices[vertex_id] = {x:vertex.x+offset.x,y:vertex.y-offset.y};
}
}
dotgrid.history.push(this.layers);
this.clear();
dotgrid.guide.refresh();
}
// Toggles
this.toggle_mirror = function()
{
this.style().mirror_style = this.style().mirror_style > 3 ? 0 : this.style().mirror_style+1;
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
}
// Style
this.style = function()
{
if(!this.styles[this.index]){
this.styles[this.index] = [];
}
return this.styles[this.index];
}
// Layers
this.layer = function()
{
if(!this.layers[this.index]){
this.layers[this.index] = [];
}
return this.layers[this.index];
}
this.select_layer = function(id)
{
this.index = clamp(id,0,2);
this.clear();
dotgrid.guide.refresh();
dotgrid.interface.refresh(true);
console.log(`layer:${this.index}`)
}
this.select_next_layer = function()
{
this.index = this.index >= 2 ? 0 : this.index+1
this.select_layer(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; }
}