Merge pull request #87 from pxlvxl/master
Various changes and features (revision 2)
4
.gitignore
vendored
@ -4,4 +4,6 @@ routes
|
||||
build
|
||||
node_modules
|
||||
.idea
|
||||
.history
|
||||
.history
|
||||
*.css
|
||||
*.map
|
||||
|
@ -24,12 +24,12 @@
|
||||
height: 400px;
|
||||
position: fixed;
|
||||
display: none;
|
||||
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.64);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#checkerboard {
|
||||
z-index: 1;
|
||||
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.64);
|
||||
}
|
||||
|
||||
#pixel-canvas {
|
||||
@ -85,9 +85,9 @@
|
||||
|
||||
#canvas-view {
|
||||
bottom: 0px;
|
||||
left: 48px;
|
||||
right: 48px;
|
||||
top: 48px;
|
||||
left: var(--top-nav-height);
|
||||
right: var(--top-nav-height);
|
||||
top: var(--top-nav-height);
|
||||
cursor: default;
|
||||
position: fixed;
|
||||
display: block;
|
||||
@ -97,9 +97,9 @@
|
||||
box-shadow: inset 0px 0px 4px 0px rgba(0, 0, 0, 0.4);
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
left: 48px;
|
||||
right: 48px;
|
||||
top: 48px;
|
||||
left: var(--top-nav-height);
|
||||
right: var(--top-nav-height);
|
||||
top: var(--top-nav-height);
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
}
|
@ -1,36 +1,51 @@
|
||||
|
||||
#colors-menu {
|
||||
right: 200px;
|
||||
width: 48px;
|
||||
width:var(--layers-width);
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
list-style-type: none;
|
||||
top: 48px;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
height: var(--palette-height);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: $basecolor;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
align-items: flex-start;
|
||||
z-index: 1120;
|
||||
overflow-y: scroll;
|
||||
resize: vertical;
|
||||
|
||||
li {
|
||||
width: 48px;
|
||||
flex-basis: 48px;
|
||||
|
||||
&:not(.noshrink) {
|
||||
flex-grow: 1;
|
||||
}
|
||||
&.noshrink {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
background: #232125;
|
||||
width: 1em;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
margin-top: -0.125em;
|
||||
width: 1em;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #332f35;
|
||||
border-radius: 0.25em;
|
||||
border: solid 0.125em #232125; //same color as scrollbar back to fake padding
|
||||
}
|
||||
&::-webkit-scrollbar-corner {
|
||||
background: #232125;
|
||||
}
|
||||
scrollbar-color: #332f35 #232125;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//added when the color is a duplicate of another
|
||||
#duplicate-color-warning {
|
||||
display: inline-block;
|
||||
@ -118,14 +133,14 @@
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
border: solid 3px #fff;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 0px 0px 3px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: 0px 0px 0px 3px rgba(0, 0, 0, 0.15) inset;
|
||||
z-index: 10;
|
||||
box-sizing: border-box;
|
||||
} //inner outline
|
||||
&.selected button::after {
|
||||
content: "";
|
||||
@ -149,7 +164,9 @@
|
||||
background: $basehover;
|
||||
}
|
||||
|
||||
|
||||
.jscolor-wrap {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
|
||||
.jscolor-picker-bottom {
|
||||
display: none;
|
||||
@ -173,7 +190,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.delete-color-button {
|
||||
background: none;
|
||||
padding: 0px;
|
||||
@ -193,4 +209,31 @@
|
||||
fill: lighten($basecolor, 10%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#colors-menu-settings {
|
||||
width: 98%;
|
||||
padding: 0px 0px 0px 0px;
|
||||
position:relative;
|
||||
top:0px;
|
||||
background: $basecolor;
|
||||
|
||||
button {
|
||||
position:relative;
|
||||
|
||||
left:0px;
|
||||
margin-left:0px;
|
||||
padding: 2px;
|
||||
height:30px;
|
||||
width:49%;
|
||||
color: $basetext;
|
||||
background: $basecolor;
|
||||
border:none;
|
||||
|
||||
&:hover {
|
||||
color: $basehovertext;
|
||||
background-color: $basehover;
|
||||
cursor:pointer;
|
||||
}
|
||||
}
|
||||
}
|
24
css/_containers.scss
Normal file
@ -0,0 +1,24 @@
|
||||
#right-container{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
top: var(--top-nav-height);
|
||||
right: var(--top-nav-height);
|
||||
bottom: 0;
|
||||
}
|
||||
#bottom-container{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
#left-container{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
top: var(--top-nav-height);
|
||||
bottom: 0;
|
||||
left: var(--top-nav-height);
|
||||
}
|
@ -43,4 +43,15 @@ svg {
|
||||
|
||||
#data-holders, .preload {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#right-nav {
|
||||
position: fixed;
|
||||
top: var(--top-nav-height);
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: var(--layers-width);
|
||||
z-index: 1100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
#layer-properties-menu {
|
||||
visibility: hidden;
|
||||
margin: 0;
|
||||
@ -8,7 +7,7 @@
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
|
||||
margin-right: 200px;
|
||||
margin-right: var(--layers-width);
|
||||
/*border:1px solid $basetext;*/
|
||||
list-style: none;
|
||||
position: relative;
|
||||
@ -72,19 +71,16 @@
|
||||
}
|
||||
scrollbar-color: #332f35 #232125;
|
||||
scroll-behavior: smooth;
|
||||
width:200px;
|
||||
top: 48px;
|
||||
bottom: 0;
|
||||
right:0;
|
||||
width:var(--layers-width);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: $basecolor;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
z-index: 1120;
|
||||
list-style-type: none;
|
||||
overflow-y:scroll;
|
||||
overflow-x:hidden;
|
||||
flex: 1;
|
||||
#add-layer-button {
|
||||
path {
|
||||
fill: $baseicon;
|
||||
@ -112,6 +108,33 @@
|
||||
color: $basehovertext;
|
||||
background-color: $basehover;
|
||||
}
|
||||
#add-reference-button {
|
||||
path {
|
||||
fill: $baseicon;
|
||||
}
|
||||
svg {
|
||||
position: relative;
|
||||
margin-right: 10px;
|
||||
}
|
||||
position:relative;
|
||||
justify-content: center;
|
||||
display:none;
|
||||
align-items:center;
|
||||
margin-top:2px;
|
||||
font-size: 1.2em;
|
||||
color: $basetext;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 17px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#add-reference-button:hover {
|
||||
color: $basehovertext;
|
||||
background-color: $basehover;
|
||||
}
|
||||
}
|
||||
|
||||
.selected-layer {
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
#main-menu {
|
||||
height: 48px;
|
||||
height: var(--top-nav-height);
|
||||
left: 0;
|
||||
right: 0;
|
||||
list-style-type: none;
|
||||
@ -35,7 +35,7 @@
|
||||
li ul {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
top: var(--top-nav-height);
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
@ -6,6 +6,18 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#new-pixel-inventory {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#new-pixel-map {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#new-voxel-world {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dimentions-x {
|
||||
margin: -2px 7px;
|
||||
path {
|
||||
@ -13,12 +25,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#no-palette-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#editor-mode-info {
|
||||
font-style: italic;
|
||||
}
|
||||
|
@ -3,11 +3,12 @@
|
||||
#splash {
|
||||
width:100% !important;
|
||||
height:100%!important;
|
||||
position:fixed;
|
||||
position:static;
|
||||
margin-top:-20px;
|
||||
|
||||
background-color: #232125 !important;
|
||||
opacity: 1 !important;
|
||||
z-index:1300;
|
||||
|
||||
#splash-input {
|
||||
width:74%;
|
||||
|
@ -1,9 +1,9 @@
|
||||
|
||||
#tools-menu {
|
||||
left: 0;
|
||||
width: 48px;
|
||||
width: var(--top-nav-height);
|
||||
list-style-type: none;
|
||||
top: 48px;
|
||||
top: var(--top-nav-height);
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@ -24,7 +24,7 @@
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
height: 48px;
|
||||
height: var(--top-nav-height);
|
||||
z-index:0;
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
#tools-menu .size-buttons {
|
||||
position:absolute;
|
||||
display: none;
|
||||
height:48px;
|
||||
height:var(--top-nav-height);
|
||||
left:8px;
|
||||
z-index:-1;
|
||||
background-color: $basecolor !important;
|
||||
@ -128,6 +128,8 @@
|
||||
|
||||
.fade-out {
|
||||
animation: fadeOut .1s forwards;
|
||||
display: none !important;
|
||||
/* TODO: Search Google for: "How to fade out and then set display none" */
|
||||
}
|
||||
|
||||
.is-paused {
|
||||
@ -138,8 +140,8 @@
|
||||
#tool-tutorial {
|
||||
display:inline-block;
|
||||
position:absolute;
|
||||
margin-left:48px;
|
||||
margin-top:48px;
|
||||
margin-left:var(--top-nav-height);
|
||||
margin-top:var(--top-nav-height);
|
||||
background-color: $basehover;
|
||||
color:$basetext;
|
||||
font-size:14px;
|
||||
|
@ -13,4 +13,12 @@ $baseselected: lighten($basecolor, 15%); //color(selectedTool, background),
|
||||
$baseselectediconhover: lighten($basecolor, 70%); //color(subbutton, foreground, hover)
|
||||
$baseselectedhover: lighten($basecolor, 25%); //color(subbutton, background, hover)
|
||||
$indent: darken($basecolor, 5%); //color(indent)
|
||||
$indenttext: lighten($basecolor, 50%); //color(indent, foreground)
|
||||
$indenttext: lighten($basecolor, 50%); //color(indent, foreground)
|
||||
|
||||
|
||||
:root{
|
||||
--layers-width: 206px;
|
||||
--palette-height: 38%;
|
||||
--top-nav-height: 48px;
|
||||
--drag-bar-size: var(--top-nav-height);
|
||||
}
|
30
file_copier.js
Normal file
@ -0,0 +1,30 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const fileArr = walk("./",n => !n.includes("node_modules")
|
||||
&& !n.includes(".git")
|
||||
&& !n.includes(".vscode")
|
||||
&& !n.includes("build")
|
||||
);
|
||||
const destPath = `C:/Users/jaman/OneDrive/Documents/GitHub/pixel-editor/`;
|
||||
console.log('fileArr === ',fileArr);
|
||||
fileArr.forEach(pathStr=>{
|
||||
const fileStr = fs.readFileSync(pathStr, "utf8");
|
||||
const destFilePathStr = destPath + pathStr;
|
||||
console.log('destFilePathStr, fileStr.length === ',destFilePathStr, fileStr.length);
|
||||
fs.writeFileSync(destFilePathStr, fileStr);
|
||||
})
|
||||
|
||||
function walk(dir, condition = () => true ) {
|
||||
// console.log('dir === ',dir);
|
||||
let results = [];
|
||||
for (let file of fs.readdirSync(dir)) {
|
||||
file = dir + '/' + file;
|
||||
const stat = fs.statSync(file);
|
||||
if (stat && stat.isDirectory()) {
|
||||
results = results.concat(walk(file, condition));
|
||||
} else {
|
||||
if(condition(file))results.push(file);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
52
hbs_parser.js
Normal file
@ -0,0 +1,52 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const HANDLEBARS = require('handlebars');
|
||||
const sass = require('sass');
|
||||
|
||||
const result = sass.compile('./css/pixel-editor.scss');
|
||||
fs.writeFileSync("./css/pixel-editor.css",result.css);
|
||||
|
||||
// fs.readFile('/css/pixel-editor.scss', function(err, scssFile) {
|
||||
// compiler.compile(scssFile.toString(), function(err, css) {
|
||||
// });
|
||||
// });
|
||||
|
||||
const hbsArr = [
|
||||
...fs.readdirSync("./views/").filter(n=>n.slice(-3)==="hbs").map(n=>"./views/" + n),
|
||||
...fs.readdirSync("./views/components/").filter(n=>n.slice(-3)==="hbs").map(n=>"./views/components/" + n),
|
||||
...fs.readdirSync("./views/logs/").filter(n=>n.slice(-3)==="hbs").map(n=>"./views/logs/" + n),
|
||||
...fs.readdirSync("./views/popups/").filter(n=>n.slice(-3)==="hbs").map(n=>"./views/popups/" + n),
|
||||
];
|
||||
const HBS_STR_MAP = {};
|
||||
const HBS_SPEC_MAP = {};
|
||||
const HBS_TEMPLATE_MAP = {};
|
||||
const HBS_META_DATA = hbsArr.reduce((r,filePath,i)=>{
|
||||
|
||||
const fileStr = fs.readFileSync(filePath,"utf8");
|
||||
const sp0 = fileStr.split("{{> ").slice(1);
|
||||
const partialArr = sp0.map(n=>n.split("}}")[0]);
|
||||
const sp1 = fileStr.split("{{").slice(1);
|
||||
|
||||
if(sp0.length || sp1.length) {
|
||||
const dblCurlsArr = sp1.map(n=>n.split("}}")[0]);
|
||||
|
||||
HBS_STR_MAP[filePath] = fileStr;
|
||||
|
||||
HBS_SPEC_MAP[filePath] = HANDLEBARS.precompile(fileStr);
|
||||
// HBS_TEMPLATE_MAP[filePath] = HANDLEBARS.template(HBS_SPEC_MAP[filePath]);
|
||||
|
||||
r[filePath] = {
|
||||
fileStr,
|
||||
filePath,
|
||||
dblCurlsArr,
|
||||
partialArr
|
||||
};
|
||||
}
|
||||
|
||||
return r;
|
||||
},{});
|
||||
|
||||
fs.writeFileSync(
|
||||
"./js/HBS_META_DATA.js",
|
||||
"const HBS_META_DATA = " + JSON.stringify(HBS_META_DATA,null,4)
|
||||
);
|
BIN
images/Logs/palettegrid.gif
Normal file
After Width: | Height: | Size: 2.5 MiB |
BIN
images/icons_14x14.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
images/le_button_18x19.png
Normal file
After Width: | Height: | Size: 301 B |
BIN
images/lospec_mock1.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
images/lospec_mock2.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
images/rotate_test_8x8_x16.png
Normal file
After Width: | Height: | Size: 859 B |
BIN
images/sked_tree_32x32.png
Normal file
After Width: | Height: | Size: 872 B |
BIN
images/sked_x1.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
images/sked_x10.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
images/test_8x8.png
Normal file
After Width: | Height: | Size: 149 B |
BIN
images/wang_tilesets_32x32.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
@ -30,7 +30,7 @@ class Color {
|
||||
this.hsv = Color.rgbToHsv(this.rgb);
|
||||
break;
|
||||
default:
|
||||
console.error("Unsupported color mode " + fmt);
|
||||
//console.error("Unsupported color mode " + fmt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,22 @@ const ColorModule = (() => {
|
||||
// Reference to the HTML palette
|
||||
const coloursList = document.getElementById("palette-list");
|
||||
// Reference to the colours menu
|
||||
const colorsMenu = document.getElementById("colors-menu");
|
||||
let colorsMenu = document.getElementById("colors-menu");
|
||||
// Square size
|
||||
const minSquareSize = 38;
|
||||
let squareSize = colorsMenu.children[0].getBoundingClientRect().width;
|
||||
|
||||
// Binding events to callbacks
|
||||
document.getElementById('jscolor-hex-input').addEventListener('change',colorChanged, false);
|
||||
document.getElementById('jscolor-hex-input').addEventListener('input', colorChanged, false);
|
||||
document.getElementById('add-color-button').addEventListener('click', addColorButtonEvent, false);
|
||||
|
||||
document.getElementById('add-color-button').addEventListener('click', addColorButtonEvent);
|
||||
|
||||
Events.on("wheel", "colors-menu", resizeSquares);
|
||||
Events.on("click", document.getElementById("cm-add"), addColorButtonEvent);
|
||||
Events.on("click", document.getElementById("cm-remove"), deleteColor, undefined);
|
||||
Events.on("click", document.getElementById("cm-zoomin"), resizeSquares, {altKey:true, deltaY: -1.0});
|
||||
Events.on("click", document.getElementById("cm-zoomout"), resizeSquares, {altKey:true, deltaY: 1.0});
|
||||
|
||||
// Making the colours in the HTML menu sortable
|
||||
new Sortable(document.getElementById("colors-menu"), {
|
||||
animation:100,
|
||||
@ -22,6 +31,17 @@ const ColorModule = (() => {
|
||||
onEnd: function() {Events.simulateMouseEvent(window, "mouseup");}
|
||||
});
|
||||
|
||||
function resizeSquares(event) {
|
||||
if (!event.altKey) return;
|
||||
|
||||
squareSize = Math.max(minSquareSize, (squareSize - 3 * Math.sign(event.deltaY)));
|
||||
|
||||
for (let i=0; i< colorsMenu.children.length; i++) {
|
||||
colorsMenu.children[i].style.width = squareSize + 'px';
|
||||
colorsMenu.children[i].style.height = squareSize + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes all of one color to another after being changed from the color picker
|
||||
*
|
||||
* @param {*} colorHexElement The element that has been changed
|
||||
@ -57,7 +77,7 @@ const ColorModule = (() => {
|
||||
if (newColorHex == colors[i].jscolor.toString()) {
|
||||
//if the color isnt the one that has the picker currently open
|
||||
if (!colors[i].parentElement.classList.contains('jscolor-active')) {
|
||||
//console.log('%cColor is duplicate', colorCheckingStyle);
|
||||
//////console.log('%cColor is duplicate', colorCheckingStyle);
|
||||
|
||||
//show the duplicate color warning
|
||||
duplicateColorWarning.style.visibility = 'visible';
|
||||
@ -94,25 +114,42 @@ const ColorModule = (() => {
|
||||
*
|
||||
* @param {*} e The event that triggered the callback
|
||||
*/
|
||||
function clickedColor (e){
|
||||
function clickedColor (e) {
|
||||
//left clicked color
|
||||
if (e.which == 1) {
|
||||
// remove current color selection
|
||||
const currentSelectedColorButton = document.querySelector('#colors-menu li.selected .color-button');
|
||||
const selectedColor = currentSelectedColorButton.style.backgroundColor;
|
||||
const clickedColor = e.target.style.backgroundColor;
|
||||
|
||||
document.querySelector('#colors-menu li.selected')?.classList.remove('selected');
|
||||
|
||||
//set current color
|
||||
updateCurrentColor(Color.cssToHex(e.target.style.backgroundColor));
|
||||
|
||||
updateCurrentColor(Color.cssToHex(clickedColor));
|
||||
//make color selected
|
||||
e.target.parentElement.classList.add('selected');
|
||||
|
||||
if(selectedColor === clickedColor) {
|
||||
if (EditorState.getCurrentMode() == "Basic") {
|
||||
e.target.parentElement.lastChild.classList.add('hidden');
|
||||
e.target.jscolor.show();
|
||||
}
|
||||
else {
|
||||
Dialogue.showDialogue("palette-block");
|
||||
}
|
||||
}
|
||||
}
|
||||
//right clicked color
|
||||
else if (e.which == 3) {
|
||||
//hide edit color button (to prevent it from showing)
|
||||
e.target.parentElement.lastChild.classList.add('hidden');
|
||||
//show color picker
|
||||
e.target.jscolor.show();
|
||||
if (EditorState.getCurrentMode() == "Basic") {
|
||||
//hide edit color button (to prevent it from showing)
|
||||
e.target.parentElement.lastChild.classList.add('hidden');
|
||||
//show color picker
|
||||
e.target.jscolor.show();
|
||||
}
|
||||
else {
|
||||
Dialogue.showDialogue("palette-block");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,15 +166,12 @@ const ColorModule = (() => {
|
||||
//add new color and make it selected
|
||||
let addedColor = addColor(newColor);
|
||||
addedColor.classList.add('selected');
|
||||
addedColor.style.width = squareSize + "px";
|
||||
addedColor.style.height = squareSize + "px";
|
||||
updateCurrentColor(newColor);
|
||||
|
||||
//add history state
|
||||
new HistoryState().AddColor(addedColor.firstElementChild.jscolor.toString());
|
||||
|
||||
//show color picker
|
||||
addedColor.firstElementChild.jscolor.show();
|
||||
//hide edit button
|
||||
addedColor.lastChild.classList.add('hidden');
|
||||
}
|
||||
|
||||
/** Adds the colors that have been added through the advanced-mode color picker to the
|
||||
@ -232,6 +266,8 @@ const ColorModule = (() => {
|
||||
Dialogue.showDialogue("palette-block", false);
|
||||
});
|
||||
|
||||
if (!document.querySelector('#colors-menu li.selected'))
|
||||
colorsMenu.children[0].classList.add('selected');
|
||||
return listItem;
|
||||
}
|
||||
|
||||
@ -241,6 +277,9 @@ const ColorModule = (() => {
|
||||
* that should be removed.
|
||||
*/
|
||||
function deleteColor (color) {
|
||||
if (!color)
|
||||
color = getSelectedColor();
|
||||
|
||||
const logStyle = 'background: #913939; color: white; padding: 5px;';
|
||||
|
||||
//if color is a string, then find the corresponding button
|
||||
@ -252,7 +291,6 @@ const ColorModule = (() => {
|
||||
|
||||
//loop through colors
|
||||
for (var i = 0; i < colors.length; i++) {
|
||||
//console.log(color,'=',colors[i].jscolor.toString());
|
||||
|
||||
if (color == colors[i].jscolor.toString()) {
|
||||
//set color to the color button
|
||||
@ -335,7 +373,11 @@ const ColorModule = (() => {
|
||||
}
|
||||
|
||||
function getCurrentPalette() {
|
||||
return currentPalette;
|
||||
let ret = [...currentPalette];
|
||||
if(ret.length === 0) {
|
||||
ret = [...document.querySelectorAll(".color-button")].map(n=>n.style.backgroundColor);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function resetPalette() {
|
||||
@ -347,7 +389,6 @@ const ColorModule = (() => {
|
||||
* @param {*} paletteColors The colours of the palette
|
||||
*/
|
||||
function createColorPalette(paletteColors) {
|
||||
console.log("creating palette");
|
||||
//remove current palette
|
||||
while (colorsMenu.childElementCount > 1)
|
||||
colorsMenu.children[0].remove();
|
||||
@ -359,7 +400,6 @@ const ColorModule = (() => {
|
||||
for (var i = 0; i < paletteColors.length; i++) {
|
||||
var newColor = new Color("hex", paletteColors[i]);
|
||||
var newColorElement = ColorModule.addColor(newColor.hex);
|
||||
|
||||
var newColRgb = newColor.rgb;
|
||||
|
||||
var lightestColorRgb = lightestColor.rgb;
|
||||
@ -383,6 +423,7 @@ const ColorModule = (() => {
|
||||
|
||||
//set as current color
|
||||
updateCurrentColor(darkestColor.hex);
|
||||
|
||||
}
|
||||
|
||||
/** Creates the palette with the colours used in all the layers
|
||||
@ -457,10 +498,20 @@ const ColorModule = (() => {
|
||||
if (refLayer)
|
||||
color = refLayer.context.fillStyle;
|
||||
|
||||
for (let i=0; i<currFile.layers.length - 1; i++) {
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
currFile.layers[i].context.fillStyle = color;
|
||||
currFile.layers[i].context.strokeStyle = color;
|
||||
}
|
||||
|
||||
for (let i=0; i<currFile.sublayers.length; i++) {
|
||||
currFile.sublayers[i].context.fillStyle = color;
|
||||
currFile.sublayers[i].context.strokeStyle = color;
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedColor() {
|
||||
const currentSelectedColorButton = document.querySelector('#colors-menu li.selected .color-button');
|
||||
return currentSelectedColorButton.jscolor.toString();
|
||||
}
|
||||
|
||||
return {
|
||||
@ -474,5 +525,6 @@ const ColorModule = (() => {
|
||||
createPaletteFromLayers,
|
||||
updatePaletteFromLayers,
|
||||
updateCurrentColor,
|
||||
getSelectedColor,
|
||||
}
|
||||
})();
|
@ -696,7 +696,7 @@ const ColorPicker = (() => {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log("How did you select the " + currentPickingMode + ", hackerman?");
|
||||
////console.log("How did you select the " + currentPickingMode + ", hackerman?");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ const Dialogue = (() => {
|
||||
let currentOpenDialogue = "";
|
||||
let dialogueOpen = true;
|
||||
|
||||
const popUpContainer = document.getElementById("pop-up-container");
|
||||
const popUpContainer = document.getElementById("pop-up-container") ?? document.createElement("div");
|
||||
const cancelButtons = popUpContainer.getElementsByClassName('close-button');
|
||||
|
||||
Events.onCustom("esc-pressed", closeDialogue);
|
||||
@ -31,6 +31,9 @@ const Dialogue = (() => {
|
||||
* @param {*} trackEvent Should I track the GA event?
|
||||
*/
|
||||
function showDialogue (dialogueName, trackEvent) {
|
||||
|
||||
|
||||
|
||||
if (typeof trackEvent === 'undefined') trackEvent = true;
|
||||
|
||||
// Updating currently open dialogue
|
||||
@ -83,4 +86,4 @@ const Dialogue = (() => {
|
||||
}
|
||||
})();
|
||||
|
||||
console.log("Dialog: " + Dialogue);
|
||||
////console.log("Dialog: " + Dialogue);
|
@ -9,15 +9,15 @@ const EditorState = (() => {
|
||||
return pixelEditorMode;
|
||||
}
|
||||
|
||||
function switchMode(newMode) {
|
||||
if (!firstFile && newMode == "Basic" && !confirm('Switching to basic mode will flatten all the visible layers. Are you sure you want to continue?')) {
|
||||
function switchMode(newMode, skipConfirm = false) {
|
||||
////console.trace();
|
||||
const switchText = 'Switching to basic mode will flatten all the visible layers. Are you sure you want to continue?';
|
||||
if (!firstFile && newMode == "Basic" && !skipConfirm && !confirm(switchText)) {
|
||||
return;
|
||||
}
|
||||
//switch to advanced mode
|
||||
if (newMode == 'Advanced') {
|
||||
Events.emit("switchedToAdvanced");
|
||||
// Hide the palette menu
|
||||
document.getElementById('colors-menu').style.right = '200px'
|
||||
|
||||
pixelEditorMode = 'Advanced';
|
||||
document.getElementById("switch-mode-button").innerHTML = 'Switch to basic mode';
|
||||
|
@ -100,8 +100,9 @@ const Events = (() => {
|
||||
*/
|
||||
function on(event, elementId, functionCallback, ...args) {
|
||||
//if element provided is string, get the actual element
|
||||
if(!elementId)return;
|
||||
const element = Util.getElement(elementId);
|
||||
|
||||
if(!element)return;
|
||||
element.addEventListener(event,
|
||||
function (e) {
|
||||
functionCallback(...args, e);
|
||||
|
65
js/File.js
@ -2,11 +2,12 @@ class File {
|
||||
// Canvas, canvas state
|
||||
canvasSize = [];
|
||||
zoom = 7;
|
||||
canvasView = document.getElementById("canvas-view");
|
||||
canvasView = document.getElementById("canvas-view") ?? document.createElement("div");
|
||||
inited = false;
|
||||
|
||||
// Layers
|
||||
layers = [];
|
||||
sublayers = [];
|
||||
currentLayer = undefined;
|
||||
VFXLayer = undefined;
|
||||
TMPLayer = undefined;
|
||||
@ -129,11 +130,9 @@ class File {
|
||||
|
||||
// Save all imageDatas
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i].hasCanvas()) {
|
||||
imageDatas.push(currFile.layers[i].context.getImageData(
|
||||
0, 0, currFile.canvasSize[0], currFile.canvasSize[1])
|
||||
);
|
||||
}
|
||||
imageDatas.push(currFile.layers[i].context.getImageData(
|
||||
0, 0, currFile.canvasSize[0], currFile.canvasSize[1])
|
||||
);
|
||||
}
|
||||
|
||||
// Saving the history only if I'm not already undoing or redoing
|
||||
@ -154,16 +153,20 @@ class File {
|
||||
currFile.canvasSize[1] = parseInt(currFile.canvasSize[1]) +
|
||||
this.rcBorders.top + this.rcBorders.bottom;
|
||||
|
||||
console.trace();
|
||||
console.log(currFile.canvasSize);
|
||||
////console.trace();
|
||||
////console.log(currFile.canvasSize);
|
||||
|
||||
// Resize the canvases
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
currFile.layers[i].canvas.width = currFile.canvasSize[0];
|
||||
currFile.layers[i].canvas.height = currFile.canvasSize[1];
|
||||
|
||||
currFile.layers[i].resize();
|
||||
}
|
||||
for (let i=0; i<currFile.sublayers.length; i++) {
|
||||
currFile.sublayers[i].canvas.width = currFile.canvasSize[0];
|
||||
currFile.sublayers[i].canvas.height = currFile.canvasSize[1];
|
||||
currFile.sublayers[i].resize();
|
||||
}
|
||||
|
||||
// Regenerate the checkerboard
|
||||
currFile.checkerBoard.fillCheckerboard();
|
||||
@ -213,16 +216,14 @@ class File {
|
||||
|
||||
// Putting all the data for each layer with the right offsets (decided by the pivot)
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i].hasCanvas()) {
|
||||
if (customData == undefined) {
|
||||
currFile.layers[i].context.putImageData(imageDatas[copiedDataIndex], leftOffset, topOffset);
|
||||
}
|
||||
else {
|
||||
currFile.layers[i].context.putImageData(customData[copiedDataIndex], 0, 0);
|
||||
}
|
||||
currFile.layers[i].updateLayerPreview();
|
||||
copiedDataIndex++;
|
||||
if (customData == undefined) {
|
||||
currFile.layers[i].context.putImageData(imageDatas[copiedDataIndex], leftOffset, topOffset);
|
||||
}
|
||||
else {
|
||||
currFile.layers[i].context.putImageData(customData[copiedDataIndex], 0, 0);
|
||||
}
|
||||
currFile.layers[i].updateLayerPreview();
|
||||
copiedDataIndex++;
|
||||
}
|
||||
|
||||
Dialogue.closeDialogue();
|
||||
@ -246,7 +247,7 @@ class File {
|
||||
this.rcPivot = "topleft";
|
||||
|
||||
// Computing the min and max coordinates in which there's a non empty pixel
|
||||
for (let i=1; i<currFile.layers.length - nAppLayers; i++) {
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
let imageData = currFile.layers[i].context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]);
|
||||
let pixelPosition;
|
||||
|
||||
@ -291,12 +292,10 @@ class File {
|
||||
|
||||
// Saving the data
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i].hasCanvas()) {
|
||||
imageDatas.push(currFile.layers[i].context.getImageData(minX - 1, currFile.layers[i].canvasSize[1] - maxY, maxX-minX + 1, maxY-minY + 1));
|
||||
}
|
||||
imageDatas.push(currFile.layers[i].context.getImageData(minX - 1, currFile.layers[i].canvasSize[1] - maxY, maxX-minX + 1, maxY-minY + 1));
|
||||
}
|
||||
|
||||
//console.log("sx: " + borders.left + "dx: " + borders.right + "top: " + borders.top + "btm: " + borders.bottom);
|
||||
//////console.log("sx: " + borders.left + "dx: " + borders.right + "top: " + borders.top + "btm: " + borders.bottom);
|
||||
|
||||
document.getElementById("rc-border-left").value = this.rcBorders.left;
|
||||
document.getElementById("rc-border-right").value = this.rcBorders.right;
|
||||
@ -430,11 +429,9 @@ class File {
|
||||
|
||||
// Get all the image datas
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i].hasCanvas()) {
|
||||
rsImageDatas.push(currFile.layers[i].context.getImageData(
|
||||
0, 0, currFile.canvasSize[0], currFile.canvasSize[1])
|
||||
);
|
||||
}
|
||||
rsImageDatas.push(currFile.layers[i].context.getImageData(
|
||||
0, 0, currFile.canvasSize[0], currFile.canvasSize[1])
|
||||
);
|
||||
}
|
||||
|
||||
// event is null when the user is undoing
|
||||
@ -450,13 +447,11 @@ class File {
|
||||
|
||||
// Put the image datas on the new canvases
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i].hasCanvas()) {
|
||||
currFile.layers[i].context.putImageData(
|
||||
this.resizeImageData(rsImageDatas[layerIndex], newWidth, newHeight, this.currentAlgo), 0, 0
|
||||
);
|
||||
currFile.layers[i].updateLayerPreview();
|
||||
layerIndex++;
|
||||
}
|
||||
currFile.layers[i].context.putImageData(
|
||||
this.resizeImageData(rsImageDatas[layerIndex], newWidth, newHeight, this.currentAlgo), 0, 0
|
||||
);
|
||||
currFile.layers[i].updateLayerPreview();
|
||||
layerIndex++;
|
||||
}
|
||||
|
||||
// Updating start values when I finish scaling the sprite
|
||||
|
@ -1,3 +1,4 @@
|
||||
// localStorage.setItem("lpe-cache",`{}`)
|
||||
const FileManager = (() => {
|
||||
|
||||
// Binding the browse holder change event to file loading
|
||||
@ -9,6 +10,7 @@ const FileManager = (() => {
|
||||
Events.on('change', browsePaletteHolder, loadPalette);
|
||||
Events.on('change', importImageHolder, loadImage);
|
||||
Events.on('click', 'export-confirm', exportProject);
|
||||
Events.on("click", "save-project-confirm", saveProject);
|
||||
|
||||
function openSaveProjectWindow() {
|
||||
//create name
|
||||
@ -23,7 +25,6 @@ const FileManager = (() => {
|
||||
}
|
||||
|
||||
Util.setValue('lpe-file-name', fileName);
|
||||
Events.on("click", "save-project-confirm", saveProject);
|
||||
Dialogue.showDialogue('save-project', false);
|
||||
}
|
||||
|
||||
@ -45,6 +46,7 @@ const FileManager = (() => {
|
||||
|
||||
function saveProject() {
|
||||
// Get name
|
||||
// debugger;
|
||||
let fileName = Util.getValue("lpe-file-name") + ".lpe";
|
||||
let selectedPalette = Util.getText('palette-button');
|
||||
//set download link
|
||||
@ -58,6 +60,9 @@ const FileManager = (() => {
|
||||
|
||||
if (typeof ga !== 'undefined')
|
||||
ga('send', 'event', 'Pixel Editor Save', selectedPalette, currFile.canvasSize[0]+'/'+currFile.canvasSize[1]); /*global ga*/
|
||||
|
||||
|
||||
LayerList.closeOptionsMenu(); // is this the right place for this?
|
||||
}
|
||||
|
||||
function exportProject() {
|
||||
@ -71,6 +76,9 @@ const FileManager = (() => {
|
||||
let emptyCanvas = document.createElement("canvas");
|
||||
let layersCopy = currFile.layers.slice();
|
||||
|
||||
exportCanvas.getContext("2d").willReadFrequently = true;
|
||||
emptyCanvas.getContext("2d").willReadFrequently = true;
|
||||
|
||||
exportCanvas.width = currFile.canvasSize[0];
|
||||
exportCanvas.height = currFile.canvasSize[1];
|
||||
|
||||
@ -120,7 +128,62 @@ const FileManager = (() => {
|
||||
//open file selection dialog
|
||||
document.getElementById('open-image-browse-holder').click();
|
||||
}
|
||||
function localStorageCheck() {
|
||||
return !!localStorage.getItem("lpe-cache");
|
||||
}
|
||||
function localStorageSave() {
|
||||
const lpeStr = getProjectData();
|
||||
const lpe = JSON.parse(lpeStr);
|
||||
console.log('BEFORE JSON.stringify(lpe.colors,null,4) === ',JSON.stringify(lpe.colors,null,4));
|
||||
console.log([...ColorModule.getCurrentPalette()]);
|
||||
if(lpe.colors.length < 1)lpe.colors = [...ColorModule.getCurrentPalette()];
|
||||
if(lpe.colors.length < 1)lpe.colors.push("#000000");
|
||||
console.log('AFTER JSON.stringify(lpe.colors,null,4) === ',JSON.stringify(lpe.colors,null,4));
|
||||
if(!lpe.canvasWidth)lpe.canvasWidth = 16;
|
||||
if(!lpe.canvasHeight)lpe.canvasHeight = 16;
|
||||
console.log('LPE saved === ',lpe);
|
||||
localStorage.setItem("lpe-cache", JSON.stringify(lpe));
|
||||
}
|
||||
function localStorageReset() {
|
||||
localStorage.setItem("lpe-cache", JSON.stringify({
|
||||
"canvasWidth":16,
|
||||
"canvasHeight":16,
|
||||
"editorMode":"Advanced",
|
||||
"colors":["#000000","#0b6082","#1d8425","#cc1919"],
|
||||
"selectedLayer":0,
|
||||
"layers":[
|
||||
{"canvas":{},"context":{"mozImageSmoothingEnabled":false},"isSelected":true,"isVisible":true,"isLocked":false,"oldLayerName":null,"menuEntry":{},"id":"layer0","name":"Layer 0","src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAHZJREFUOE9jZKAQMFKon4EuBvxHcyWKpdhcgK4BpB+nS9ElYJqJ9hqyQpI1ozsNZABRNnMnNIEt+7qgjhGrBpgCWOCBFKJHN0gNTgOQFSPbhi5OlAHYEhpBL+DThO4tgoGGHB7YwgKvAbj8j+xCgi4glNkoNgAA3JApEbHObDkAAAAASUVORK5CYII="},
|
||||
{"canvas":{},"context":{"mozImageSmoothingEnabled":false},"isSelected":false,"isVisible":true,"isLocked":false,"oldLayerName":null,"menuEntry":{},"id":"layer1","name":"Layer 1","src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAGNJREFUOE9jZKAQMCLrl21R/Q/iP665jSKOzw7qGgCyCeQKsl0AM4AUb2D1KymuoJ0BxHoDZ3QRG6V445uYsCBoACGvEExxhFxBlAH4XEHQAEKpkygDkFMoumuINgCWI9HDBAChJjwRzAXQUwAAAABJRU5ErkJggg=="},
|
||||
{"canvas":{},"context":{"mozImageSmoothingEnabled":false},"isSelected":false,"isVisible":true,"isLocked":false,"oldLayerName":null,"menuEntry":{},"id":"layer2","name":"Layer 2","src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAGNJREFUOE9jZKAQMJKi/4yk5H+YepPnz8F68RqArAFdI4yP1QBsNuFyKYYBMM0wJxLyIlYDiNWMNQxALhimBuCKUoKBSChKcRpASCPOhESsRoIGEBuVKF4g1XaMhERqMgYZAACIaEgR0hnFxgAAAABJRU5ErkJggg=="}
|
||||
]
|
||||
}));
|
||||
}
|
||||
function defaultLPE(w,h,colors) {
|
||||
return {
|
||||
"canvasWidth":w,
|
||||
"canvasHeight":h,
|
||||
"editorMode":"Advanced",
|
||||
colors,
|
||||
"selectedLayer":0,
|
||||
"layers":[
|
||||
{"canvas":{},"context":{"mozImageSmoothingEnabled":false},"isSelected":true,"isVisible":true,"isLocked":false,"oldLayerName":null,"menuEntry":{},"id":"layer0","name":"Layer 0","src":emptyCanvasSrc(w,h)}
|
||||
]
|
||||
};
|
||||
|
||||
function emptyCanvasSrc(w,h) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
}
|
||||
function localStorageLoad() {
|
||||
////console.log("loading from localStorage");
|
||||
////console.log('JSON.parse(localStorage.getItem("lpe-cache") ?? "{}") === ',JSON.parse(localStorage.getItem("lpe-cache") ?? "{}"));
|
||||
const lpe = JSON.parse(localStorage.getItem("lpe-cache") ?? "{}");
|
||||
//console.log('LPE loaded === ',lpe);
|
||||
return lpe;
|
||||
}
|
||||
function loadFile() {
|
||||
let fileName = document.getElementById("open-image-browse-holder").value;
|
||||
// Getting the extension
|
||||
@ -132,6 +195,7 @@ const FileManager = (() => {
|
||||
// If it's a Lospec Pixel Editor tm file, I load the project
|
||||
if (extension == 'lpe') {
|
||||
openProject();
|
||||
// openFile();
|
||||
}
|
||||
else {
|
||||
openFile();
|
||||
@ -142,7 +206,6 @@ const FileManager = (() => {
|
||||
|
||||
browseHolder.value = null;
|
||||
}
|
||||
|
||||
function openFile() {
|
||||
//load file
|
||||
var fileReader = new FileReader();
|
||||
@ -152,7 +215,11 @@ const FileManager = (() => {
|
||||
|
||||
img.onload = function() {
|
||||
//create a new pixel with the images dimentions
|
||||
Startup.newPixel(this.width, this.height);
|
||||
console.log('this === ',this);
|
||||
Startup.newPixel({
|
||||
canvasWidth: this.width,
|
||||
canvasHeight: this.height
|
||||
});
|
||||
EditorState.switchMode('Advanced');
|
||||
|
||||
//draw the image onto the canvas
|
||||
@ -168,59 +235,63 @@ const FileManager = (() => {
|
||||
};
|
||||
fileReader.readAsDataURL(browseHolder.files[0]);
|
||||
}
|
||||
|
||||
function openProject() {
|
||||
let file = browseHolder.files[0];
|
||||
let reader = new FileReader();
|
||||
|
||||
function openProject(lpeData) {
|
||||
console.log('lpeData === ',lpeData);
|
||||
// Getting all the data
|
||||
reader.readAsText(file, "UTF-8");
|
||||
// Converting the data to a json object and creating a new pixel (see _newPixel.js for more)
|
||||
reader.onload = function (e) {
|
||||
let dictionary = JSON.parse(e.target.result);
|
||||
Startup.newPixel(dictionary['canvasWidth'], dictionary['canvasHeight'], dictionary);
|
||||
|
||||
for (let i=0; dictionary['color' + i] != null; i++) {
|
||||
ColorModule.addColor(dictionary['color'+i]);
|
||||
if(lpeData){
|
||||
_parseLPE(lpeData);
|
||||
} else {
|
||||
let file = browseHolder.files[0];
|
||||
let reader = new FileReader();
|
||||
// Converting the data to a json object and creating a new pixel (see _newPixel.js for more)
|
||||
reader.onload = function (e) {
|
||||
console.log('this === ',this);
|
||||
console.log('e === ',e);
|
||||
let dictionary = JSON.parse(e.target.result);
|
||||
console.log('FileManager.js => openProject => loaded lpe dictionary === ',dictionary);
|
||||
_parseLPE(dictionary);
|
||||
}
|
||||
|
||||
// Removing the default colours
|
||||
ColorModule.deleteColor(ColorModule.getCurrentPalette()[0]);
|
||||
ColorModule.deleteColor(ColorModule.getCurrentPalette()[0]);
|
||||
reader.readAsText(file, "UTF-8");
|
||||
}
|
||||
|
||||
function _parseLPE(dictionary) {
|
||||
Startup.newPixel(dictionary);
|
||||
}
|
||||
}
|
||||
function loadFromLPE(dictionary) {
|
||||
ColorModule.resetPalette();
|
||||
|
||||
//console.log('dictionary === ',dictionary);
|
||||
|
||||
EditorState.switchMode(dictionary.editorMode ?? 'Advanced');
|
||||
|
||||
if(dictionary.colors)ColorModule.createColorPalette(dictionary.colors);
|
||||
|
||||
// Startup.newPixel(dictionary);
|
||||
}
|
||||
function getProjectData() {
|
||||
// use a dictionary
|
||||
let dictionary = {};
|
||||
// sorting layers by increasing z-index
|
||||
let layersCopy = currFile.layers.slice();
|
||||
layersCopy.sort((a, b) => (a.canvas.style.zIndex > b.canvas.style.zIndex) ? 1 : -1);
|
||||
// save canvas size
|
||||
let layersCopy = currFile.layers.filter(n=>!!n.menuEntry).slice();
|
||||
dictionary['canvasWidth'] = currFile.canvasSize[0];
|
||||
dictionary['canvasHeight'] = currFile.canvasSize[1];
|
||||
// save editor mode
|
||||
dictionary['editorMode'] = EditorState.getCurrentMode();
|
||||
// save palette
|
||||
for (let i=0; i<ColorModule.getCurrentPalette().length; i++) {
|
||||
dictionary["color" + i] = ColorModule.getCurrentPalette()[i];
|
||||
}
|
||||
|
||||
// save number of layers
|
||||
dictionary["nLayers"] = layersCopy.length;
|
||||
|
||||
// save layers
|
||||
for (let i=0; i<layersCopy.length; i++) {
|
||||
// Only saving the layers the user has access to (no vfx, tmp or checkerboard layers)
|
||||
if (layersCopy[i].menuEntry != null) {
|
||||
dictionary["layer" + i] = layersCopy[i];
|
||||
dictionary["layer" + i + "ImageData"] = layersCopy[i].canvas.toDataURL();
|
||||
}
|
||||
}
|
||||
dictionary.colors = [
|
||||
...ColorModule.getCurrentPalette()
|
||||
];
|
||||
dictionary.layers = layersCopy
|
||||
.map((n,i)=>{
|
||||
//console.log('n.name === ',n.name);
|
||||
if(n.isSelected)dictionary.selectedLayer = i;
|
||||
return {
|
||||
...n,
|
||||
src: n.canvas.toDataURL(),
|
||||
};
|
||||
});
|
||||
|
||||
return JSON.stringify(dictionary);
|
||||
}
|
||||
|
||||
function loadPalette() {
|
||||
if (browsePaletteHolder.files && browsePaletteHolder.files[0]) {
|
||||
//make sure file is allowed filetype
|
||||
@ -380,13 +451,79 @@ const FileManager = (() => {
|
||||
this.currentImportPivotElement = event.target;
|
||||
this.currentImportPivotElement.classList.add("rc-selected-pivot");
|
||||
}
|
||||
|
||||
function upgradeLPE(dictionary) {
|
||||
console.log('dictionary === ',dictionary);
|
||||
if(dictionary.color0 && !dictionary.colors) {
|
||||
dictionary.colors = [];
|
||||
let colorIdx = 0;
|
||||
while(dictionary[`color${colorIdx}`]) {
|
||||
dictionary.colors.push(dictionary[`color${colorIdx}`]);
|
||||
delete dictionary[`color${colorIdx}`];
|
||||
colorIdx++;
|
||||
}
|
||||
console.log('Object.keys(dictionary) === ',Object.keys(dictionary));
|
||||
dictionary.layers = Object.keys(dictionary).reduce((r,k,i)=>{
|
||||
if(k.slice(0,5) === "layer" && dictionary[k]){
|
||||
if(dictionary[k].isSelected){
|
||||
dictionary.selectedLayer = r.length;
|
||||
}
|
||||
r.push({
|
||||
...dictionary[k],
|
||||
src: dictionary[`${k}ImageData`]
|
||||
});
|
||||
|
||||
delete dictionary[k];
|
||||
delete dictionary[`${k}ImageData`];
|
||||
}
|
||||
return r;
|
||||
},[]);
|
||||
console.log('dictionary.layers === ',dictionary.layers);
|
||||
}
|
||||
console.log('dictionary === ',dictionary);
|
||||
return dictionary;
|
||||
}
|
||||
function toggleCache(elm){
|
||||
console.log('elm === ',elm);
|
||||
FileManager.cacheEnabled = !FileManager.cacheEnabled;
|
||||
localStorage.setItem("lpe-cache-enabled",FileManager.cacheEnabled ? "1" : "0");
|
||||
elm.textContent = cacheBtnText(FileManager.cacheEnabled);
|
||||
}
|
||||
function cacheBtnText(cacheEnabled) {
|
||||
return `${cacheEnabled ? "Disable" : "Enable"} auto-cache`;
|
||||
}
|
||||
|
||||
return {
|
||||
const cacheEnabled = !!Number(localStorage.getItem("lpe-cache-enabled"));
|
||||
document.getElementById("auto-cache-button").textContent = cacheBtnText(cacheEnabled);
|
||||
|
||||
const ret = {
|
||||
cacheEnabled,
|
||||
loadFromLPE,
|
||||
toggleCache,
|
||||
getProjectData,
|
||||
localStorageReset,
|
||||
localStorageCheck,
|
||||
localStorageSave,
|
||||
localStorageLoad,
|
||||
upgradeLPE,
|
||||
defaultLPE,
|
||||
saveProject,
|
||||
openProject,
|
||||
exportProject,
|
||||
openPixelExportWindow,
|
||||
openSaveProjectWindow,
|
||||
openImportImageWindow,
|
||||
open
|
||||
}
|
||||
|
||||
Object.keys(ret).forEach(k=>{
|
||||
if(typeof ret[k] === "function"){
|
||||
const orig = ret[k];
|
||||
ret[k] = function() {
|
||||
DEBUG_ARR.push(`called FileManager -> ${k}`);
|
||||
return orig.call(this,...arguments);
|
||||
}
|
||||
}
|
||||
})
|
||||
return ret;
|
||||
})();
|
332
js/HBS_META_DATA.js
Normal file
@ -0,0 +1,332 @@
|
||||
const HBS_META_DATA = {
|
||||
"./views/colors-menu.hbs": {
|
||||
"fileStr": "<ul id=\"colors-menu\">\n\t<li class=\"noshrink\"><button title=\"Add Current Color To Palette\" id=\"add-color-button\">{{svg \"./plus.svg\" width=\"30\" height=\"30\"}}</button></li>\n</ul>\n\n<div class=\"jscolor-picker-bottom\">\n\t<span>#</span><input type=\"text\" id=\"jscolor-hex-input\" />\n\t<div id=\"duplicate-color-warning\" title=\"Color is a duplicate of another in palette\">{{svg \"warning.svg\" width=\"14\"\n\t\theight=\"12\" }}</div>\n\t<button class=\"delete-color-button\">{{svg \"trash.svg\" width=\"20\" height=\"20\" }}</button>\n</div>\n\n<div class=\"color-edit-button\">\n\t{{svg \"adjust.svg\" width=\"20\" height=\"20\" }}\n</div>",
|
||||
"filePath": "./views/colors-menu.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"./plus.svg\" width=\"30\" height=\"30\"",
|
||||
"svg \"warning.svg\" width=\"14\"\n\t\theight=\"12\" ",
|
||||
"svg \"trash.svg\" width=\"20\" height=\"20\" ",
|
||||
"svg \"adjust.svg\" width=\"20\" height=\"20\" "
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/foobar.hbs": {
|
||||
"fileStr": "<!DOCTYPE html>\r\n<html>\r\n\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <title>{{title}}</title>\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <link href=\"https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,700,900\" rel=\"stylesheet\">\r\n <link rel=\"stylesheet\" href=\"/pixel-editor.css\" />\r\n <meta name=\"ROBOTS\" content=\"NOINDEX, NOFOLLOW\">\r\n</head>\r\n\r\n<body oncontextmenu=\"return false;\">\r\n\t{{> canvases}}\r\n \r\n <div id=\"pop-up-container\">\r\n </div>\r\n\r\n <script src=\"/pixel-editor.js\"></script>\r\n {{#reload}}<script src=\"/reload/reload.js\"></script>{{/reload}}\r\n</body>\r\n</html>",
|
||||
"filePath": "./views/foobar.hbs",
|
||||
"dblCurlsArr": [
|
||||
"title",
|
||||
"> canvases",
|
||||
"#reload",
|
||||
"/reload"
|
||||
],
|
||||
"partialArr": [
|
||||
"canvases"
|
||||
]
|
||||
},
|
||||
"./views/index.hbs": {
|
||||
"fileStr": "<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset=\"UTF-8\">\n <title>{{title}}</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <link href=\"https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,700,900\" rel=\"stylesheet\">\n <link rel=\"stylesheet\" href=\"/pixel-editor.css\" />\n <meta name=\"ROBOTS\" content=\"NOINDEX, NOFOLLOW\">\n {{{google-analytics}}}\n {{{favicons}}}\n</head>\n\n<body oncontextmenu=\"return false;\">\n\t{{> compatibility-warning}}\n\t{{> preload}}\n\n\t{{> main-menu}}\n\t{{> tools-menu}}\n\t{{> colors-menu}}\n\t{{> layers-menu}}\n\n\t{{> tool-previews}}\n\t{{> canvases}}\n\t{{> holders}}\n \n <div id=\"pop-up-container\">\n\t\t{{> new-pixel}}\n\t\t{{> splash-page}}\n\t\t{{> sprite-resize}}\n\t\t{{> canvas-resize}}\n\t\t{{> palette}}\n\t\t{{> help}}\n\t\t{{> about}}\n\t\t{{> changelog}}\n\t\t{{> credits}}\n\t\t{{> settings}}\n\t\t{{> pixel-export}}\n\t\t{{> save-project}}\n </div>\n\n <script src=\"/pixel-editor.js\"></script>\n {{#reload}}<script src=\"/reload/reload.js\"></script>{{/reload}}\n</body>\n</html>",
|
||||
"filePath": "./views/index.hbs",
|
||||
"dblCurlsArr": [
|
||||
"title",
|
||||
"{google-analytics",
|
||||
"{favicons",
|
||||
"> compatibility-warning",
|
||||
"> preload",
|
||||
"> main-menu",
|
||||
"> tools-menu",
|
||||
"> colors-menu",
|
||||
"> layers-menu",
|
||||
"> tool-previews",
|
||||
"> canvases",
|
||||
"> holders",
|
||||
"> new-pixel",
|
||||
"> splash-page",
|
||||
"> sprite-resize",
|
||||
"> canvas-resize",
|
||||
"> palette",
|
||||
"> help",
|
||||
"> about",
|
||||
"> changelog",
|
||||
"> credits",
|
||||
"> settings",
|
||||
"> pixel-export",
|
||||
"> save-project",
|
||||
"#reload",
|
||||
"/reload"
|
||||
],
|
||||
"partialArr": [
|
||||
"compatibility-warning",
|
||||
"preload",
|
||||
"main-menu",
|
||||
"tools-menu",
|
||||
"colors-menu",
|
||||
"layers-menu",
|
||||
"tool-previews",
|
||||
"canvases",
|
||||
"holders",
|
||||
"new-pixel",
|
||||
"splash-page",
|
||||
"sprite-resize",
|
||||
"canvas-resize",
|
||||
"palette",
|
||||
"help",
|
||||
"about",
|
||||
"changelog",
|
||||
"credits",
|
||||
"settings",
|
||||
"pixel-export",
|
||||
"save-project"
|
||||
]
|
||||
},
|
||||
"./views/layers-menu.hbs": {
|
||||
"fileStr": "<!-- LAYER MENU -->\n<ul id=\"layers-menu\">\n\t<li class = \"layers-menu-entry selected-layer\">\n\t\t<canvas class = \"preview-canvas\"></canvas>\n\t\t<ul class=\"layer-buttons\">\n\t\t\t<li class = \"layer-button\">\n\t\t\t\t<button title=\"Lock layer\" class=\"lock-layer-button\">\n\t\t\t\t\t{{svg \"unlockedpadlock.svg\" width=\"15\" height=\"15\" class = \"default-icon\"}}\n\t\t\t\t\t{{svg \"lockedpadlock.svg\" width=\"15\" height=\"15\" class = \"edited-icon\" display = \"none\"}}\n\t\t\t\t</button>\n\t\t\t</li>\n\t\t\t<li class = \"layer-button\">\n\t\t\t\t<button title=\"Show / hide layer\" class=\"hide-layer-button\">\n\t\t\t\t\t{{svg \"visible.svg\" width=\"15\" height=\"15\" class = \"default-icon\"}}\n\t\t\t\t\t{{svg \"invisible.svg\" width=\"15\" height=\"15\" class = \"edited-icon\" display = \"none\"}}\n\t\t\t\t</button>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<p>Layer 0<div class = \"gradient\"></div></p>\n\t</li>\n\n\t<li>\n\t\t<button id=\"add-layer-button\">\n\t\t\t{{svg \"plus.svg\" width=\"20\" height=\"20\"}} Add layer\n\t\t</button>\n\t</li>\n</ul>\n\n<ul id=\"layer-properties-menu\">\n\t<li>\n\t\t<button onclick = \"LayerList.renameLayer()\">Rename</button>\n\t</li>\n\t<li>\n\t\t<button onclick = \"LayerList.duplicateLayer()\">Duplicate</button>\n\t</li>\n\t<li>\n\t\t<button onclick = \"LayerList.deleteLayer()\">Delete</button>\n\t</li>\n\t<li>\n\t\t<button onclick = \"LayerList.merge()\">Merge below</button>\n\t</li>\n\t<li> \n\t\t<button onclick = \"LayerList.flatten(true)\">Flatten visible</button>\n\t</li>\n\t<li>\n\t\t<button onclick = \"LayerList.flatten(false)\">Flatten all</button>\n\t</li>\n</ul>",
|
||||
"filePath": "./views/layers-menu.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"unlockedpadlock.svg\" width=\"15\" height=\"15\" class = \"default-icon\"",
|
||||
"svg \"lockedpadlock.svg\" width=\"15\" height=\"15\" class = \"edited-icon\" display = \"none\"",
|
||||
"svg \"visible.svg\" width=\"15\" height=\"15\" class = \"default-icon\"",
|
||||
"svg \"invisible.svg\" width=\"15\" height=\"15\" class = \"edited-icon\" display = \"none\"",
|
||||
"svg \"plus.svg\" width=\"20\" height=\"20\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/layout-contribute.hbs": {
|
||||
"fileStr": "<!doctype html>\n\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n {{#title}}<title>{{this}}</title>{{/title}}\n {{#css}}<link rel=\"stylesheet\" href=\"{{this}}\">{{/css}}\n</head>\n\n<body>\n <section class=\"wrapper\">\n {{{body}}}\n </section>\n {{#js}}<script src=\"{{this}}\"></script>{{/js}}\n</body>\n</html>",
|
||||
"filePath": "./views/layout-contribute.hbs",
|
||||
"dblCurlsArr": [
|
||||
"#title",
|
||||
"this",
|
||||
"/title",
|
||||
"#css",
|
||||
"this",
|
||||
"/css",
|
||||
"{body",
|
||||
"#js",
|
||||
"this",
|
||||
"/js"
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/main-menu.hbs": {
|
||||
"fileStr": "<ul id=\"main-menu\" class=\"editor-top-menu\">\n\t<li class=\"logo\">Lospec Pixel Editor</li>\n\t<li>\n\t\t<button>File</button>\n\t\t<ul>\n\t\t\t<li><button>New</button></li>\n\t\t\t<li><button>Save project</button></li>\n\t\t\t<li><button>Open</button></li>\n\t\t\t<li><button id=\"export-button\" class=\"disabled\">Export</button></li>\n\t\t\t<li><a href=\"https://lospec.com/pixel-editor\">Exit</a></li>\n\t\t</ul>\n\t</li>\n\t<li>\n\t\t<button>Edit</button>\n\t\t<ul>\n\t\t\t<li><button id=\"resize-canvas-button\" onclick = \"currFile.openResizeCanvasWindow()\">Resize canvas</button></li>\n\t\t\t<li><button id=\"resize-sprite-button\" onclick = \"currFile.openResizeSpriteWindow()\">Scale sprite</button></li>\n\t\t\t<li><button onclick = \"currFile.trimCanvas()\">Trim canvas</button></li>\n\t\t\t<li><button id=\"undo-button\" class=\"disabled\">Undo</button></li>\n\t\t\t<li><button id=\"redo-button\" class=\"disabled\">Redo</button></li>\n\t\t</ul>\n\t</li>\n\t<li>\n\t\t<button>View</button>\n\t\t<ul>\n\t\t\t<li><button id=\"toggle-pixelgrid-button\" onclick=\"currFile.pixelGrid.togglePixelGrid()\">Show pixel grid</button></li>\n\t\t</ul>\n\t</li>\n\t<li>\n\t\t<button id=\"layer-button\">Layer</button>\n\t\t<ul>\n\t\t\t<li><button onclick = \"LayerList.addLayer()\">New layer</button></li>\n\t\t\t<li><button onclick = \"LayerList.duplicateLayer()\">Duplicate</button></li>\n\t\t\t<li><button onclick = \"LayerList.renameLayer()\">Rename</button></li>\n\t\t\t<li><button onclick = \"LayerList.deleteLayer()\">Delete</button></li>\n\t\t\t<li><button onclick = \"LayerList.merge()\">Merge below</button></li>\n\t\t\t<li><button onclick = \"LayerList.flatten(false)\">Flatten all</button></li>\n\t\t\t<li><button onclick = \"LayerList.flatten(true)\">Flatten visible</button></li>\n\t\t\t\n\t\t</ul>\n\t</li>\n\t<li>\n\t\t<button>Selection</button>\n\t\t<ul>\n\t\t\t<li><button id=\"copy-button\">Copy</button></li>\n\t\t\t<li><button id=\"cut-button\">Cut</button></li>\n\t\t\t<li><button id=\"paste-button\">Paste</button></li>\n\t\t\t<li><button id=\"cancelSelection-button\">Cancel</button></li>\n\t\t</ul>\n\t</li>\n\t<li>\n\t\t<button>Editor</button>\n\t\t<ul>\n\t\t\t<li><button id=\"switch-mode-button\">Switch to basic mode</button></li>\n\t\t\t<li><button onclick=\"Dialogue.showDialogue('splash', false)\">Splash page</button></li>\n\t\t\t<li><button>Settings</button></li>\n\t\t</ul>\n\t</li>\n\n\t<li>\n\t\t<button>Help</button>\n\t\t<ul>\n\t\t\t<li><button>Help</button></li>\n\t\t\t<li><button>About</button></li>\n\t\t\t<li><button>Changelog</button></li>\n\t\t</ul>\n\t</li>\n\n\t<li id=\"editor-info\">\n\t\t<ul>\n\t\t\t<li><label>Tool size: <input type=\"number\"/></label></li>\n\t\t\t<li>{{> checkbox}}</li>\n\t\t</ul>\n\t</li>\n</ul>",
|
||||
"filePath": "./views/main-menu.hbs",
|
||||
"dblCurlsArr": [
|
||||
"> checkbox"
|
||||
],
|
||||
"partialArr": [
|
||||
"checkbox"
|
||||
]
|
||||
},
|
||||
"./views/tools-menu.hbs": {
|
||||
"fileStr": "<ul id=\"tools-menu\">\n\t<li class=\"selected expanded\">\n\t\t<button id=\"brush-button\">{{svg \"pencil.svg\" width=\"24\" height=\"24\"}}</button>\n\t\t<ul class=\"size-buttons\">\n\t\t\t<button title=\"Increase Brush Size\" id=\"brush-bigger-button\" class=\"tools-menu-sub-button\">{{svg \"plus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t\t<button title=\"Decrease Brush Size\" id=\"brush-smaller-button\" class=\"tools-menu-sub-button\">{{svg \"minus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t</ul>\n\t</li>\n\n\t<li class = \"expanded\">\n\t\t<button id=\"eraser-button\">{{svg \"eraser.svg\" width=\"24\" height=\"24\"}}</button>\n\t\t<ul class=\"size-buttons\">\n\t\t\t<button title=\"Increase Eraser Size\" id=\"eraser-bigger-button\" class=\"tools-menu-sub-button\">{{svg \"plus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t\t<button title=\"Decrease Eraser Size\" id=\"eraser-smaller-button\" class=\"tools-menu-sub-button\">{{svg \"minus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t</ul>\n\t</li>\n\n\t<li class=\"expanded\">\n\t\t<button id=\"rectangle-button\">{{svg \"rectangle.svg\" width=\"24\" height=\"24\" id=\"rectangle-empty-button-svg\"}}\n\t\t{{svg \"fullrect.svg\" width=\"24\" height=\"24\" id=\"rectangle-full-button-svg\" display = \"none\"}}</button>\n\t\t<ul class=\"size-buttons\">\n\t\t\t<button title=\"Increase Rectangle Size\" id=\"rectangle-bigger-button\" class=\"tools-menu-sub-button\">{{svg \"plus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t\t<button title=\"Decrease Rectangle Size\" id=\"rectangle-smaller-button\" class=\"tools-menu-sub-button\">{{svg \"minus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t</ul>\n\t</li>\n\n\t<li class=\"expanded\">\n\t\t<button id=\"ellipse-button\">\n\t\t\t{{svg \"ellipse.svg\" width=\"24\" height=\"24\" id=\"ellipse-empty-button-svg\"}}\n\t\t\t{{svg \"filledellipse.svg\" width=\"24\" height=\"24\" id=\"ellipse-full-button-svg\" display = \"none\"}}\n\t\t</button>\n\t\t<ul class=\"size-buttons\">\n\t\t\t<button title=\"Increase Ellipse Size\" id=\"ellipse-bigger-button\" class=\"tools-menu-sub-button\">{{svg \"plus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t\t<button title=\"Decrease Ellipse Size\" id=\"ellipse-smaller-button\" class=\"tools-menu-sub-button\">{{svg \"minus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t</ul>\n\t</li>\n\n\t<li class=\"expanded\">\n\t\t<button id=\"line-button\">{{svg \"line.svg\" width=\"24\" height=\"24\"}}</button>\n\t\t<ul class=\"size-buttons\">\n\t\t\t<button title=\"Increase Line Size\" id=\"line-bigger-button\" class=\"tools-menu-sub-button\">{{svg \"plus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t\t<button title=\"Decrease Line Size\" id=\"line-smaller-button\" class=\"tools-menu-sub-button\">{{svg \"minus.svg\" width=\"12\" height=\"12\"}}</button>\n\t\t</ul>\n\t</li>\n\n\t<li><button id=\"fill-button\">{{svg \"fill.svg\" width=\"24\" height=\"24\"}}</button></li>\n\n\t<li><button id=\"rectselect-button\">{{svg \"rectselect.svg\" width = \"24\" height = \"24\"}}</button><li>\n\n\t<li><button id=\"lassoselect-button\">{{svg \"lasso.svg\" width = \"26\" height = \"26\"}}</button></li>\n\n\t<li><button id=\"magicwand-button\">{{svg \"magicwand.svg\" width = \"26\" height = \"26\"}}</button></li>\n\n\t<li><button id=\"eyedropper-button\">{{svg \"eyedropper.svg\" width=\"24\" height=\"24\"}}</button></li>\n\n\t<li><button id=\"pan-button\">{{svg \"pan.svg\" width=\"24\" height=\"24\"}}</button></li>\n</ul>\n\n<div id=\"tool-tutorial\" class=\"fade-in\">\n\t<h3>Brush tool</h3>\n\t<ul>\n\t\t<li><span class=\"keyboard-key\">B</span> to select the tool</li>\n\t\t<li><span class=\"keyboard-key\">Left drag</span> to use the tool</li>\n\t\t<li><span class=\"keyboard-key\">Right drag</span> to change tool size</li>\n\t\t<li><span class=\"keyboard-key\">+</span> or <span class=\"keyboard-key\">-</span> to change tool size</li>\n\t</ul>\n\t<img src=\"brush-tutorial.gif\"/>\n</div>\"",
|
||||
"filePath": "./views/tools-menu.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"pencil.svg\" width=\"24\" height=\"24\"",
|
||||
"svg \"plus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"minus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"eraser.svg\" width=\"24\" height=\"24\"",
|
||||
"svg \"plus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"minus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"rectangle.svg\" width=\"24\" height=\"24\" id=\"rectangle-empty-button-svg\"",
|
||||
"svg \"fullrect.svg\" width=\"24\" height=\"24\" id=\"rectangle-full-button-svg\" display = \"none\"",
|
||||
"svg \"plus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"minus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"ellipse.svg\" width=\"24\" height=\"24\" id=\"ellipse-empty-button-svg\"",
|
||||
"svg \"filledellipse.svg\" width=\"24\" height=\"24\" id=\"ellipse-full-button-svg\" display = \"none\"",
|
||||
"svg \"plus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"minus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"line.svg\" width=\"24\" height=\"24\"",
|
||||
"svg \"plus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"minus.svg\" width=\"12\" height=\"12\"",
|
||||
"svg \"fill.svg\" width=\"24\" height=\"24\"",
|
||||
"svg \"rectselect.svg\" width = \"24\" height = \"24\"",
|
||||
"svg \"lasso.svg\" width = \"26\" height = \"26\"",
|
||||
"svg \"magicwand.svg\" width = \"26\" height = \"26\"",
|
||||
"svg \"eyedropper.svg\" width=\"24\" height=\"24\"",
|
||||
"svg \"pan.svg\" width=\"24\" height=\"24\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/components/checkbox.hbs": {
|
||||
"fileStr": "<div class=\"checkbox-holder\">\n <div class=\"checkbox checked\">\n <label>Snap to grid</label>\n <input type=\"hidden\"/>\n <div class=\"box\">{{svg \"check\"}}</div>\n </div>\n</div>",
|
||||
"filePath": "./views/components/checkbox.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"check\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/about.hbs": {
|
||||
"fileStr": "<div id=\"about\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t<h1>About Lospec Pixel Editor</h1>\n\t<div>version 1.1.0</div>\n\t<p>This is a web-based tool for creating and editing pixel art.</p>\n\t<p>The goal of this tool is to be an accessible and intuitive tool that's simple enough for a first time pixel artist while still being usable enough for a veteran. </p>\n\t<p>In the future I hope to add enough features to become a full fledged pixel art editor, with everything an artist could need.</p>\n\t<h1>About Lospec</h1>\n\t<p>Lospec is a website created to host tools for pixel artists. To see more of our tools, visit our <a href=\"/\">homepage</a>. To hear about any updates or new tools, follow us on <a href=\"http://twitter.com/lospecofficial\">Twitter</a>.</p>\n</div>",
|
||||
"filePath": "./views/popups/about.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/canvas-resize.hbs": {
|
||||
"fileStr": "<!--CANVAS RESIZE-->\n<div class=\"update\" id=\"resize-canvas\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t<h1>Resize canvas</h1>\n\n\t<!--PIVOTS-->\n\t<span id=\"pivot-menu\">\n\t\t<button class=\"pivot-button\" value=\"topleft\">{{svg \"arrows/topleft.svg\" width=\"20\" height=\"20\"}}</button>\n\t\t<button class=\"pivot-button\" value=\"top\">{{svg \"arrows/top.svg\" width=\"20\" height=\"20\"}}</button>\n\t\t<button class=\"pivot-button\" value=\"topright\">{{svg \"arrows/topright.svg\" width=\"20\" height=\"20\"}}</button>\n\t\t<button class=\"pivot-button\" value=\"left\">{{svg \"arrows/left.svg\" width=\"20\" height=\"20\"}}</button>\n\t\t<button class=\"pivot-button rc-selected-pivot\" value=\"middle\">{{svg \"arrows/middle.svg\" width=\"20\" height=\"20\"}}</button>\n\t\t<button class=\"pivot-button\" value=\"right\">{{svg \"arrows/right.svg\" width=\"20\" height=\"20\"}}</button>\n\t\t<button class=\"pivot-button\" value=\"bottomleft\">{{svg \"arrows/bottomleft.svg\" width=\"20\" height=\"20\"}}</button>\n\t\t<button class=\"pivot-button\" value=\"bottom\">{{svg \"arrows/bottom.svg\" width=\"20\" height=\"20\"}}</button>\n\t\t<button class=\"pivot-button\" value=\"bottomright\">{{svg \"arrows/bottomright.svg\" width=\"20\" height=\"20\"}}</button>\n\t</span>\n\t<!-- SIZE-->\n\t<span id=\"rc-size-menu\">\n\t\t<h2>Size</h2>\n\t\t<div>\n\t\t\t<span>\n\t\t\t\tWidth: <input id=\"rc-width\" type=\"number\" default=\"0\" step=\"1\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/>\n\t\t\t</span>\n\t\t\t\n\t\t\t<span>\n\t\t\t\tHeight: <input id=\"rc-height\" default=\"0\" step=\"1\" type=\"number\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/>\n\t\t\t</span>\n\t\t</div>\n\t</span> \n\t<!--BORDERS-->\n\t<span id=\"borders-menu\">\n\t\t<h2>Borders offsets</h2>\n\t\t<div>\n\t\t\t<span>\n\t\t\t\tLeft: <input id=\"rc-border-left\" type=\"number\" default=\"0\" step=\"1\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/>\n\t\t\t</span>\n\t\t\t\n\t\t\t<span>\n\t\t\t\tRight: <input id=\"rc-border-right\" type=\"number\" default=\"0\" step=\"1\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/>\n\t\t\t</span>\n\t\t\t\n\t\t\t<span>\n\t\t\t\tTop: <input id=\"rc-border-top\" type=\"number\" default=\"0\" step=\"1\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/>\n\t\t\t</span>\n\t\t\t\n\t\t\t<span>\n\t\t\t\tBottom: <input id=\"rc-border-bottom\" default=\"0\" step=\"1\" type=\"number\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/>\n\t\t\t</span>\n\t\t</div>\n\t\t<button id=\"resize-canvas-confirm\">Resize canvas</button>\n\t</span> \n</div>",
|
||||
"filePath": "./views/popups/canvas-resize.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/topleft.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/top.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/topright.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/left.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/middle.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/right.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/bottomleft.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/bottom.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"arrows/bottomright.svg\" width=\"20\" height=\"20\"",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if"
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/changelog.hbs": {
|
||||
"fileStr": "<div id=\"changelog\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\n\t<h1>Changelog</h1>\n\t{{#each changelog}}\n\t\t<h2>Version {{@key}}</h2>\n\t\t<ul>{{#each this}}\n\t\t\t<li>{{change}} <span class=\"weak\">- {{author}}</span></li>\n\t\t{{/each}}</ul>\n\t{{/each}}\n</div>",
|
||||
"filePath": "./views/popups/changelog.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\"",
|
||||
"#each changelog",
|
||||
"@key",
|
||||
"#each this",
|
||||
"change",
|
||||
"author",
|
||||
"/each",
|
||||
"/each"
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/credits.hbs": {
|
||||
"fileStr": "<div id=\"credits\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t<h1>Credits</h1>\n\t<h2>Icons</h2>\n\t<ul>\n\t\t<li><div>Icons made by <a href=\"http://www.freepik.com\" title=\"Freepik\">Freepik</a> from <a href=\"http://www.flaticon.com\" title=\"Flaticon\">www.flaticon.com</a> is licensed by <a href=\"http://creativecommons.org/licenses/by/3.0/\" title=\"Creative Commons BY 3.0\" target=\"_blank\">CC 3.0 BY</a></div></li>\n\t\t<li><div>Font Awesome by Dave Gandy - <a href=\"http://fontawesome.io\">http://fontawesome.io</a></div></li>\n\t\t<li><div>Icons made by <a href=\"http://www.flaticon.com/authors/those-icons\" title=\"Those Icons\">Those Icons</a> from <a href=\"http://www.flaticon.com\" title=\"Flaticon\">www.flaticon.com</a> is licensed by <a href=\"http://creativecommons.org/licenses/by/3.0/\" title=\"Creative Commons BY 3.0\" target=\"_blank\">CC 3.0 BY</a></div></li>\n\t</ul>\n</div>",
|
||||
"filePath": "./views/popups/credits.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/help.hbs": {
|
||||
"fileStr": "<div id=\"help\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t<h1>Help</h1>\n\t<h2>Palette</h2>\n\t<ul>\n\t\t<li>Left Click - Choose Color</li>\n\t\t<li>Right Click - Edit Color</li>\n\t</ul>\n\t<h2>Hotkeys</h2>\n\t<ul>\n\t\t<li><strong>Pencil:</strong> <span class=\"keyboard-key\">B</span> or <span class=\"keyboard-key\">1</span></li>\n\t\t<li><strong>Eraser:</strong> <span class=\"keyboard-key\">R</span></li>\n\t\t<li><strong>Rectangle:</strong> <span class=\"keyboard-key\">U</span></li>\n\t\t<li><strong>Line:</strong> <span class=\"keyboard-key\">L</span></li>\n\t\t<li><strong>Fill:</strong> <span class=\"keyboard-key\">F</span> or <span class=\"keyboard-key\">2</span></li>\n\t\t<li><strong>Eyedropper:</strong> <span class=\"keyboard-key\">E</span> or <span class=\"keyboard-key\">3</span></li>\n\t\t<li><strong>Pan:</strong> <span class=\"keyboard-key\">P</span> or <span class=\"keyboard-key\">M</span> or <span class=\"keyboard-key\">4</span></li>\n\t\t<li><strong>Zoom:</strong> <span class=\"keyboard-key\">Z</span> or <span class=\"keyboard-key\">5</span></li>\n\t\t<li><strong>Undo:</strong> Ctrl + <span class=\"keyboard-key\">Z</span></li>\n\t\t<li><strong>Redo:</strong> Ctrl + <span class=\"keyboard-key\">Y</span> or Ctrl + Alt + <span class=\"keyboard-key\">Z</span></li>\n\t\t<li><strong>Rectangular selection:</strong> <span class=\"keyboard-key\">M</span></li>\n\t</ul>\n\t<h2>Mouse Shortcuts</h2>\n\t<ul>\n\t\t<li><strong>Eyedropper: </strong>Alt + Click</li>\n\t\t<li><strong>Pan: </strong>Space + Click</li>\n\t\t<li><strong>Zoom: </strong>Alt + Scroll Wheel</li>\n\t</ul>\n\t<h2>Layers</h2>\n\t<ul>\n\t\t<li>{{svg \"visible.svg\" width=\"15\" height=\"15\" class = \"default-icon\"}}: show / hide layer</li>\n\t\t<li>{{svg \"lockedpadlock.svg\" width=\"15\" height=\"15\" class = \"default-icon\"}}: lock / unlock layer, when a layer is locked it's not possible to draw on it</li>\n\t\t<li>Right click on a layer to open the <strong>menu</strong>:\n\t\t\t<ul>\n\t\t\t\t<li><strong>Rename:</strong> change the name of the layer</li>\n\t\t\t\t<li><strong>Duplicate:</strong> duplicate the layer</li>\n\t\t\t\t<li><strong>Delete:</strong> delete the layer (doesn't work if there's only one layer)</li>\n\t\t\t\t<li><strong>Merge below:</strong> merges the selected the layer with the one below it</li>\n\t\t\t\t<li><strong>Flatten visible:</strong> merges all the visible layers</li>\n\t\t\t\t<li></strong>Flatten all:</strong> merges all the layers</li>\n\t\t\t</ul>\n\t\t</li>\n\t</ul>\n</div>",
|
||||
"filePath": "./views/popups/help.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\"",
|
||||
"svg \"visible.svg\" width=\"15\" height=\"15\" class = \"default-icon\"",
|
||||
"svg \"lockedpadlock.svg\" width=\"15\" height=\"15\" class = \"default-icon\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/new-pixel.hbs": {
|
||||
"fileStr": "<!-- NEW PIXEL -->\n<div id=\"new-pixel\" class=\"update\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t<h1>New Pixel</h1>\n\n\t<!-- Preset-->\n\t<h2>Preset</h2>\n\t<button id=\"preset-button\" class=\"dropdown-button\">Choose a preset...</button>\n\t<div id=\"preset-menu\" class=\"dropdown-menu\"></div>\n\n\t<h2>Size</h2>\n\t<input id=\"size-width\" value=\"{{#if width}}{{width}}{{else}}64{{/if}}\" autocomplete=\"off\" />{{svg \"x.svg\" width=\"16\" height=\"16\" class=\"dimentions-x\"}}<input id=\"size-height\" value=\"{{#if height}}{{height}}{{else}}64{{/if}}\" autocomplete=\"off\" />\n\t<h2>Palette</h2>\n\t<button id=\"palette-button\" class=\"dropdown-button\">Choose a palette...</button>\n\t<div id=\"palette-menu\" class=\"dropdown-menu\"><button id=\"no-palette-button\">Empty Palette</button><button id=\"load-palette-button\">Load palette...</button></div>\n\n\t<div id=\"new-pixel-warning\">Creating a new pixel will discard your current one.</div>\n\t<div>\n\t\t<button id=\"create-button\" class=\"default\">Create</button>\n\t</div>\n</div>",
|
||||
"filePath": "./views/popups/new-pixel.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\"",
|
||||
"#if width",
|
||||
"width",
|
||||
"else",
|
||||
"/if",
|
||||
"svg \"x.svg\" width=\"16\" height=\"16\" class=\"dimentions-x\"",
|
||||
"#if height",
|
||||
"height",
|
||||
"else",
|
||||
"/if"
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/palette.hbs": {
|
||||
"fileStr": "<!-- PALETTE -->\n<div id=\"palette-block\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t\n\t<h1>Edit palette</h1>\n\t\n\t<div id=\"colour-picker\">\n\t\t<div id=\"cp-modes\">\n\t\t\t<button id=\"cp-rgb\" class=\"cp-selected-mode\">RGB</button>\n\t\t\t<button id=\"cp-hsv\">HSV</button>\n\t\t\t<button id=\"cp-hsl\">HSL</button>\n\t\t\t\n\t\t\t<div id=\"cp-colour-preview\" class=\"cp-colour-preview\"></div>\n\t\t\t<input id=\"cp-hex\" type=\"text\" value=\"#123456\"/>\n\t\t</div>\n\n\t\t<div id=\"sliders-container\">\n\t\t\t<div class = \"cp-slider-entry\">\n\t\t\t\t<label for = \"first-slider\">R</label>\n\t\t\t\t<input type=\"range\" min=\"0\" max=\"255\" class=\"colour-picker-slider\" id=\"first-slider\"/>\n\t\t\t\t<input type = \"text\" value = \"128\" id=\"cp-sliderText1\"/>\n\t\t\t</div>\n\n\t\t\t<div class = \"cp-slider-entry\">\n\t\t\t\t<label for = \"second-slider\">G</label>\n\t\t\t\t<input type=\"range\" min=\"0\" max =\"255\" class=\"colour-picker-slider\" id=\"second-slider\"/>\n\t\t\t\t<input type = \"text\" value = \"128\" id=\"cp-sliderText2\"/>\n\t\t\t</div>\n\n\t\t\t<div class = \"cp-slider-entry\">\n\t\t\t\t<label for = \"third-slider\">B</label>\n\t\t\t\t<input type=\"range\" min = \"0\" max = \"255\" class = \"colour-picker-slider\" id=\"third-slider\"/>\n\t\t\t\t<input type = \"text\" value = \"128\" id=\"cp-sliderText3\"/>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div id=\"cp-minipicker\">\n\t\t\t<input type = \"range\" min = \"0\" max = \"100\" id=\"cp-minipicker-slider\"/>\n\t\t\t<div id=\"cp-canvas-container\">\n\t\t\t\t<canvas id=\"cp-spectrum\"></canvas>\n\t\t\t\t<div id=\"cp-active-icon\" class=\"cp-picker-icon\"></div>\n\t\t\t</div>\n\n\t\t\t<div id=\"cp-colours-previews\">\n\t\t\t\t<div class = \"cp-colour-preview\">\n\t\t\t\t\t#123456\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div id=\"cp-colour-picking-modes\">\n\t\t\t\t<button id=\"cp-mono\" class=\"cp-selected-mode\">Mono</button>\n\t\t\t\t<button id=\"cp-analog\">Nlgs</button>\n\t\t\t\t<button id=\"cp-cmpt\">Cmpt</button>\n\t\t\t\t<button id=\"cp-tri\">Tri</button>\n\t\t\t\t<button id=\"cp-scmpt\">Scm</button>\n\t\t\t\t<button id=\"cp-tetra\">Tetra</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\n\t<div id=\"palette-container\">\n\t\t<ul id=\"palette-list\">\n\t\t\t<li style = \"background-color:rgb(255,0,0);width:40px;height:40px;\" onmousedown=\"PaletteBlock.startRampSelection(event)\"\n\t\t\tonmousemove=\"PaletteBlock.updateRampSelection(event)\" onmouseup=\"PaletteBlock.endRampSelection(event)\"></li>\n\t\t\t<li style = \"background-color:rgb(0,255,0);width:40px;height:40px;\"onmousedown=\"PaletteBlock.startRampSelection(event)\"\n\t\t\tonmousemove=\"PaletteBlock.updateRampSelection(event)\" onmouseup=\"PaletteBlock.endRampSelection(event)\"></li>\n\t\t</ul>\n\t</div>\n\n\t<div id=\"pb-options\">\n\t\t<button title=\"Add colours to palette\" id=\"pb-addcolours\">Add colours</button>\n\t\t<button title=\"Remove colours from palette\" id=\"pb-removecolours\">Remove colours</button>\n\t</div>\n</div>",
|
||||
"filePath": "./views/popups/palette.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/pixel-export.hbs": {
|
||||
"fileStr": "<div id=\"export\" class=\"pixel-export\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t\n\t<h1>Export File</h1>\n\n\t<div class=\"export-configuration\">\n\t\t<h2>File Name</h2>\n\t\t<input id=\"export-file-name\" autocomplete=\"off\"/>\n\t</div>\n\n\t<div class=\"popup-actions\">\n\t\t<button class=\"default\" id=\"export-confirm\">Export</button>\n\t</div>\n</div>",
|
||||
"filePath": "./views/popups/pixel-export.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/save-project.hbs": {
|
||||
"fileStr": "<div id=\"save-project\" class=\"save-project\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t\n\t<h1>Save Project</h1>\n\n\t<div class=\"save-project-configuration\">\n\t\t<h2>File Name</h2>\n\t\t<input id=\"lpe-file-name\" autocomplete=\"off\" />\n\t</div>\n\n\t<div class=\"popup-actions\">\n\t\t<button class=\"default\" id=\"save-project-confirm\">Save</button>\n\t</div>\n</div>",
|
||||
"filePath": "./views/popups/save-project.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/settings.hbs": {
|
||||
"fileStr": "<div id=\"settings\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t<h1>Settings</h1>\n\n\t<div id=\"settings-container\">\n\t\t<h2>History</h2>\n\t\t<div class = \"settings-entry\">\n\t\t\t<label for=\"setting-numberOfHistoryStates\">Number of History States</label> <input id=\"setting-numberOfHistoryStates\" value=\"200\" autocomplete=\"off\" />\n\t\t</div>\n\n\t\t<h2>Pixel grid</h2>\n\t\t<div class = \"settings-entry\">\n\t\t\t<label for=\"setting-pixelGridColour\">Colour of the pixel grid</label><input id=\"setting-pixelGridColour\" value = \"#0000FF\" autocomplete=\"off\"/>\n\t\t</div>\n\t</div>\n\n\t<p id=\"cookies-disabled-warning\">Your browsers cookies are disabled, settings will be lost upon closing this page.</p>\n\n\t<div>\n\t\t<button id=\"save-settings\" class=\"default\">Save</button>\n\t</div>\n</div>",
|
||||
"filePath": "./views/popups/settings.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\""
|
||||
],
|
||||
"partialArr": []
|
||||
},
|
||||
"./views/popups/splash-page.hbs": {
|
||||
"fileStr": "<!-- Splash page -->\n<div id=\"splash\">\n\t<div id=\"splash-news\">\n\t\t\t<div id=\"latest-update\">\n\t\t\t<h1>Latest updates</h1>\n\n\t\t\t{{> latestLog}}\n\t\t</div>\n\t</div>\n\n\t<div id=\"splash-input\">\n\t\t<div id=\"editor-logo\">\n\t\t\t<div id=\"black\">\n\t\t\t\t<div id=\"sp-coverdata\">\n\t\t\t\t\t<img src=\"https://cdn.lospec.com/static/brand/lospec_logo_3x.png\"/> pixel editor\n\t\t\t\t\t<p>Version 1.4.0</p>\n\t\t\t\t\t<a href=\"https://cdn.discordapp.com/attachments/506277390050131978/795660870221955082/final.png\">Art by Unsettled</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div class=\"splash-menu\">\n\t\t\t<div id=\"sp-newpixel\">\n\t\t\t\t<h1>New Custom Pixel</h1>\n\n\t\t\t\t<h2>Size</h2>\n\t\t\t\t<div class=\"sp-np-entry\">\n\t\t\t\t\t<input id=\"size-width-splash\" value=\"{{#if width}}{{width}}{{else}}64{{/if}}\" autocomplete=\"off\" />{{svg \"x.svg\" width=\"16\" height=\"16\" class=\"dimentions-x\"}}<input id=\"size-height-splash\" value=\"{{#if height}}{{height}}{{else}}64{{/if}}\" autocomplete=\"off\" />\n\t\t\t\t</div>\n\t\t\t\t\n\t\t\t\t<h2>Palette</h2>\n\t\t\t\t<button id=\"palette-button-splash\" class=\"dropdown-button\">Choose a palette...</button>\n\t\t\t\t<div id=\"palette-menu-splash\" class=\"dropdown-menu\"><button id=\"load-palette-button-splash\">Load palette...</button></div>\n\n\t\t\t\t<div id=\"new-pixel-warning\">Creating a new pixel will discard your current one.</div>\n\t\t\t\t<div class=\"sp-np-entry\">\n\t\t\t\t\t<button id=\"create-button-splash\" class=\"default\">Create</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\n\t\t\t<div id=\"sp-quickstart-container\">\n\t\t\t\t<div id=\"sp-quickstart-title\">\n\t\t\t\t\tQuickstart\n\t\t\t\t</div>\n\n\t\t\t\t<div id=\"sp-quickstart\">\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"document.getElementById('open-image-browse-holder').click()\"><p>Load</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('Gameboy Color')\"><p><span>New</span> Gameboy</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('Commodore 64')\"><p><span>New</span> C64</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('PICO-8')\"><p><span>New</span> Pico8</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('',16,16)\"><p><span>New</span> 16x16</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('',32,32)\"><p><span>New</span> 32x32</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('',64,64)\"><p><span>New</span> 64x64</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('',128,128)\"><p><span>New</span> 128x128</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('',256,256)\"><p><span>New</span> 256x256</p></div>\n\t\t\t\t\t<div class=\"sp-template\" onclick=\"Startup.newFromTemplate('',512,512)\"><p><span>New</span> 512x512</p></div>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"mode-switcher\">\n\t\t\t\t\t<span class=\"basic\">You are using Basic Mode.</span> \n\t\t\t\t\t<span class=\"advanced\">You are using Advanced Mode.</span> \n\t\t\t\t\t<a id=\"switch-editor-mode-splash\" href=\"#\">\n\t\t\t\t\t\t<span class=\"basic\">Switch to Advanced Mode.</span> \n\t\t\t\t\t\t<span class=\"advanced\">Switch to using Basic Mode.</span> \n\t\t\t\t\t\t»\n\t\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>",
|
||||
"filePath": "./views/popups/splash-page.hbs",
|
||||
"dblCurlsArr": [
|
||||
"> latestLog",
|
||||
"#if width",
|
||||
"width",
|
||||
"else",
|
||||
"/if",
|
||||
"svg \"x.svg\" width=\"16\" height=\"16\" class=\"dimentions-x\"",
|
||||
"#if height",
|
||||
"height",
|
||||
"else",
|
||||
"/if"
|
||||
],
|
||||
"partialArr": [
|
||||
"latestLog"
|
||||
]
|
||||
},
|
||||
"./views/popups/sprite-resize.hbs": {
|
||||
"fileStr": "<!--SPRITE RESIZE-->\n<div class=\"update\" id=\"resize-sprite\">\n\t<button class=\"close-button\">{{svg \"x.svg\" width=\"20\" height=\"20\"}}</button>\n\t<h1>Scale sprite</h1>\n\t<!-- SIZE-->\n\t<h2>New size</h2>\n\t<span id=\"rs-size-menu\">\n\t\t<div>\n\t\t\t<span>\n\t\t\t\tWidth: <input id=\"rs-width\" type=\"number\" default=\"0\" step=\"1\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/>\n\t\t\t</span>\n\t\t\t\n\t\t\t<span>\n\t\t\t\tHeight: <input id=\"rs-height\" default=\"0\" step=\"1\" type=\"number\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/>\n\t\t\t</span>\n\t\t</div>\n\t</span> \n\t<!--BORDERS-->\n\t<h2>Resize percentages</h2>\n\t<span id=\"rs-percentage-menu\">\n\t\t<div>\n\t\t\t<span>\n\t\t\t\tWidth <input id=\"rs-width-percentage\" type=\"number\" default=\"0\" step=\"1\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/> %\n\t\t\t</span>\n\t\t\t\n\t\t\t<span>\n\t\t\t\tHeight <input id=\"rs-height-percentage\" type=\"number\" default=\"0\" step=\"1\" \n\t\t\t\tvalue=\"{{#if border}}{{border}}{{else}}0{{/if}}\" autocomplete=\"off\"/> %\n\t\t\t</span>\n\t\t</div>\n\t\t<div id=\"rs-ratio-div\">\n\t\t\t<span>\n\t\t\t\tKeep current ratio <input type = \"checkbox\" id=\"rs-keep-ratio\"/>\n\t\t\t</span>\n\t\t\t<span>\n\t\t\t\tScaling algorithm:\n\t\t\t\t<select name = \"resize-algorithm\" id=\"resize-algorithm-combobox\">\n\t\t\t\t\t<option value = \"nearest-neighbor\">Nearest neighbour</option>\n\t\t\t\t\t<option value = \"bilinear-interpolation\">Bilinear</option>\n\t\t\t\t</select>\n\t\t\t</span>\n\t\t\t</br>\n\t\t\t<button id=\"resize-sprite-confirm\">Scale sprite</button>\n\t\t</div>\n\t</span> \n</div>",
|
||||
"filePath": "./views/popups/sprite-resize.hbs",
|
||||
"dblCurlsArr": [
|
||||
"svg \"x.svg\" width=\"20\" height=\"20\"",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if",
|
||||
"#if border",
|
||||
"border",
|
||||
"else",
|
||||
"/if"
|
||||
],
|
||||
"partialArr": []
|
||||
}
|
||||
}
|
@ -38,12 +38,12 @@ const History = (() => {
|
||||
}
|
||||
|
||||
function undo () {
|
||||
console.log("undoing");
|
||||
////console.log("undoing");
|
||||
undoOrRedo('undo');
|
||||
}
|
||||
|
||||
function redo () {
|
||||
console.log("redoing");
|
||||
////console.log("redoing");
|
||||
undoOrRedo('redo');
|
||||
}
|
||||
|
||||
@ -186,13 +186,13 @@ class HistoryState {
|
||||
this.nFlattened = nFlattened;
|
||||
|
||||
this.undo = function() {
|
||||
for (let i=0; i<this.nFlattened - nAppLayers; i++) {
|
||||
for (let i=0; i<this.nFlattened; i++) {
|
||||
undo();
|
||||
}
|
||||
};
|
||||
|
||||
this.redo = function() {
|
||||
for (let i=0; i<this.nFlattened - nAppLayers; i++) {
|
||||
for (let i=0; i<this.nFlattened; i++) {
|
||||
redo();
|
||||
}
|
||||
};
|
||||
@ -241,7 +241,9 @@ class HistoryState {
|
||||
|
||||
this.undo = function() {
|
||||
addedLayer.selectLayer();
|
||||
LayerList.deleteLayer(false);
|
||||
if (currFile.layers.length != 4) {//TODO: repent and rebirth lol
|
||||
LayerList.deleteLayer(false);
|
||||
}
|
||||
};
|
||||
|
||||
this.redo = function() {
|
||||
@ -268,7 +270,9 @@ class HistoryState {
|
||||
|
||||
this.redo = function() {
|
||||
this.deleted.selectLayer();
|
||||
LayerList.deleteLayer(false);
|
||||
if (currFile.layers.length != 4) {//TODO: repent and rebirth lol
|
||||
LayerList.deleteLayer(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -315,7 +319,7 @@ class HistoryState {
|
||||
this.index = index;
|
||||
|
||||
this.undo = function() {
|
||||
if (currFile.layers.length - nAppLayers > this.index + 1) {
|
||||
if (currFile.layers.length > this.index + 1) {
|
||||
currFile.layers[this.index + 1].selectLayer();
|
||||
}
|
||||
else {
|
||||
@ -412,7 +416,7 @@ class HistoryState {
|
||||
//find new color in palette and change it back to old color
|
||||
let colors = document.getElementsByClassName('color-button');
|
||||
for (let i = 0; i < colors.length; i++) {
|
||||
//console.log(newColorValue, '==', colors[i].jscolor.toString());
|
||||
//////console.log(newColorValue, '==', colors[i].jscolor.toString());
|
||||
if (newColorValue == colors[i].jscolor.toString()) {
|
||||
colors[i].jscolor.fromString(oldColorValue);
|
||||
break;
|
||||
@ -429,7 +433,7 @@ class HistoryState {
|
||||
//find old color in palette and change it back to new color
|
||||
let colors = document.getElementsByClassName('color-button');
|
||||
for (let i = 0; i < colors.length; i++) {
|
||||
//console.log(oldColorValue, '==', colors[i].jscolor.toString());
|
||||
//////console.log(oldColorValue, '==', colors[i].jscolor.toString());
|
||||
if (oldColorValue == colors[i].jscolor.toString()) {
|
||||
colors[i].jscolor.fromString(newColorValue);
|
||||
break;
|
||||
|
@ -163,7 +163,7 @@ const Input = (() => {
|
||||
spacePressed = true;
|
||||
break;
|
||||
case 46:
|
||||
console.log("Pressed del");
|
||||
////console.log("Pressed del");
|
||||
Events.emit("del");
|
||||
break;
|
||||
}
|
||||
|
244
js/LayerList.js
@ -1,60 +1,60 @@
|
||||
const LayerList = (() => {
|
||||
|
||||
let layerList = document.getElementById("layers-menu");
|
||||
let layerListEntry = layerList.firstElementChild;
|
||||
// let layerListEntry = layerList.firstElementChild;
|
||||
let layerListEntry = document.getElementById("default-layer-list-item");
|
||||
let renamingLayer = false;
|
||||
let dragStartLayer;
|
||||
|
||||
// Binding the right click menu
|
||||
Events.on("mousedown", layerList, openOptionsMenu);
|
||||
// Binding the add layer button to the right function
|
||||
Events.on('click',"add-layer-button", addLayer, false);
|
||||
// Listening to the switch mode event so I can change the layout
|
||||
Events.onCustom("switchedToAdvanced", showMenu);
|
||||
Events.onCustom("switchedToBasic", hideMenu);
|
||||
Events.on('click',"add-layer-button", addLayerClick, false);
|
||||
Events.on('click',"add-reference-button", addReferenceClick, false);
|
||||
Events.onCustom("switchedToAdvanced", showLayerList);
|
||||
Events.onCustom("switchedToBasic", hideLayerList);
|
||||
|
||||
// Making the layers list sortable
|
||||
new Sortable(layerList, {
|
||||
animation: 100,
|
||||
filter: ".layer-button",
|
||||
draggable: ".layers-menu-entry",
|
||||
onStart: layerDragStart,
|
||||
onEnd: layerDragDrop
|
||||
onEnd: layerDragEnd
|
||||
});
|
||||
|
||||
function showMenu() {
|
||||
function showLayerList() {
|
||||
layerList.style.display = "inline-block";
|
||||
document.getElementById('layer-button').style.display = 'inline-block';
|
||||
}
|
||||
function hideMenu() {
|
||||
function hideLayerList() {
|
||||
if (EditorState.documentCreated()) {
|
||||
// Selecting the current layer
|
||||
currFile.currentLayer.selectLayer();
|
||||
// Flatten the layers
|
||||
flatten(true);
|
||||
}
|
||||
|
||||
layerList.style.display = "none";
|
||||
document.getElementById('layer-button').style.display = 'none';
|
||||
}
|
||||
|
||||
function addLayer(id, saveHistory = true) {
|
||||
// layers.length - 3
|
||||
let index = currFile.layers.length - 3;
|
||||
function addReferenceClick(id, saveHistory = true, layerName) {
|
||||
addLayer(...arguments);
|
||||
currFile.layers[currFile.layers.length-1].selectLayer();
|
||||
}
|
||||
function addLayerClick(id, saveHistory = true, layerName) {
|
||||
addLayer(...arguments);
|
||||
currFile.layers[currFile.layers.length-1].selectLayer();
|
||||
}
|
||||
function addLayer(id, saveHistory = true, layerName) {
|
||||
let index = currFile.layers.length;
|
||||
// Creating a new canvas
|
||||
let newCanvas = document.createElement("canvas");
|
||||
// Setting up the new canvas
|
||||
currFile.canvasView.append(newCanvas);
|
||||
Layer.maxZIndex+=2;
|
||||
newCanvas.style.zIndex = Layer.maxZIndex;
|
||||
newCanvas.classList.add("drawingCanvas");
|
||||
|
||||
if (!layerListEntry) return console.warn('skipping adding layer because no document');
|
||||
if (!layerListEntry) return //console.warn('skipping adding layer because no document');
|
||||
|
||||
// Clone the default layer
|
||||
let toAppend = layerListEntry.cloneNode(true);
|
||||
toAppend.style.display = "flex";
|
||||
//console.log('toAppend === ',toAppend);
|
||||
// Setting the default name for the layer
|
||||
toAppend.getElementsByTagName('p')[0].innerHTML = "Layer " + Layer.layerCount;
|
||||
const _layerName = layerName ?? "Layer " + currFile.layers.length;
|
||||
//console.log('_layerName === ',_layerName);
|
||||
toAppend.getElementsByTagName('p')[0].innerHTML = _layerName;
|
||||
// Removing the selected class
|
||||
toAppend.classList.remove("selected-layer");
|
||||
// Adding the layer to the list
|
||||
@ -63,12 +63,14 @@ const LayerList = (() => {
|
||||
// Creating a layer object
|
||||
let newLayer = new Layer(currFile.canvasSize[0], currFile.canvasSize[1], newCanvas, toAppend);
|
||||
newLayer.context.fillStyle = currFile.currentLayer.context.fillStyle;
|
||||
newLayer.context.willReadFrequently = true;
|
||||
newLayer.copyData(currFile.currentLayer);
|
||||
|
||||
currFile.layers.splice(index, 0, newLayer);
|
||||
// currFile.layers.splice(index, 0, newLayer);
|
||||
currFile.layers.push(newLayer);
|
||||
|
||||
// Insert it before the Add layer button
|
||||
layerList.insertBefore(toAppend, layerList.childNodes[0]);
|
||||
layerList.insertBefore(toAppend, document.getElementById("add-layer-li"));
|
||||
|
||||
if (id != null && typeof(id) == "string") {
|
||||
newLayer.setID(id);
|
||||
@ -76,11 +78,16 @@ const LayerList = (() => {
|
||||
// Basically "if I'm not adding a layer because redo() is telling meto do so", then I can save the history
|
||||
if (saveHistory) {
|
||||
new HistoryState().AddLayer(newLayer, index);
|
||||
if(FileManager.cacheEnabled)FileManager.localStorageSave();
|
||||
}
|
||||
|
||||
currFile.layers.forEach((layer, i) => {
|
||||
const _i = currFile.layers.length - i;
|
||||
layer.canvas.style.zIndex = (_i+1) * 10;
|
||||
})
|
||||
|
||||
return newLayer;
|
||||
}
|
||||
|
||||
/** Merges topLayer onto belowLayer
|
||||
*
|
||||
* @param {*} belowLayer The layer on the bottom of the layer stack
|
||||
@ -97,7 +104,7 @@ const LayerList = (() => {
|
||||
toMergeImageData.data[i+2], toMergeImageData.data[i+3]
|
||||
];
|
||||
|
||||
let currentUnderlyingPixel = [
|
||||
let currentUnderlyingPixel = [ //TODO: I'd be curious to know if this array slows this function down
|
||||
belowImageData.data[i], belowImageData.data[i+1],
|
||||
belowImageData.data[i+2], belowImageData.data[i+3]
|
||||
];
|
||||
@ -115,34 +122,38 @@ const LayerList = (() => {
|
||||
// Putting the top data into the belowdata
|
||||
belowLayer.putImageData(toMergeImageData, 0, 0);
|
||||
}
|
||||
|
||||
/** Sets the z indexes of the layers when the user drops the layer in the menu
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function layerDragDrop(event) {
|
||||
let oldIndex = event.oldDraggableIndex;
|
||||
let newIndex = event.newDraggableIndex;
|
||||
|
||||
let movedZIndex = dragStartLayer.canvas.style.zIndex;
|
||||
|
||||
if (oldIndex > newIndex)
|
||||
{
|
||||
for (let i=newIndex; i<oldIndex; i++) {
|
||||
getLayerByID(layerList.children[i].id).canvas.style.zIndex = getLayerByID(layerList.children[i + 1].id).canvas.style.zIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (let i=newIndex; i>oldIndex; i--) {
|
||||
getLayerByID(layerList.children[i].id).canvas.style.zIndex = getLayerByID(layerList.children[i - 1].id).canvas.style.zIndex;
|
||||
}
|
||||
}
|
||||
|
||||
getLayerByID(layerList.children[oldIndex].id).canvas.style.zIndex = movedZIndex;
|
||||
function layerDragEnd(event) {
|
||||
Events.simulateMouseEvent(window, "mouseup");
|
||||
}
|
||||
|
||||
const tempLayerCache = currFile.layers.reduce((r,n,i) => {
|
||||
r[n.id] = n;
|
||||
return r;
|
||||
},{});
|
||||
|
||||
let selectedId;
|
||||
const idArr = [...document.querySelectorAll(".layers-menu-entry")].map(elm => {
|
||||
if([...elm.classList].includes("selected-layer")) {
|
||||
selectedId = elm.id;
|
||||
}
|
||||
return elm.id;
|
||||
});
|
||||
let selectedIdx = idArr.indexOf(selectedId);
|
||||
idArr.forEach((id,i)=>{
|
||||
currFile.layers[i] = tempLayerCache[id];
|
||||
currFile.layers[i].isSelected = i===selectedIdx;
|
||||
});
|
||||
|
||||
currFile.layers.forEach((layer, i) => {
|
||||
const _i = currFile.layers.length - i;
|
||||
layer.canvas.style.zIndex = (_i+1) * 10;
|
||||
});
|
||||
if(FileManager.cacheEnabled)FileManager.localStorageSave();
|
||||
|
||||
}
|
||||
/** Saves the layer that is being moved when the dragging starts
|
||||
*
|
||||
* @param {*} event
|
||||
@ -150,21 +161,17 @@ const LayerList = (() => {
|
||||
function layerDragStart(event) {
|
||||
dragStartLayer = getLayerByID(layerList.children[event.oldIndex].id);
|
||||
}
|
||||
|
||||
// Finds a layer given its id
|
||||
function getLayerByID(id) {
|
||||
let ret;
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i].hasCanvas()) {
|
||||
if (currFile.layers[i].menuEntry.id == id) {
|
||||
return currFile.layers[i];
|
||||
ret = currFile.layers[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return ret ?? null;
|
||||
}
|
||||
|
||||
// Finds a layer given its name
|
||||
function getLayerByName(name) {
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i].hasCanvas()) {
|
||||
@ -176,7 +183,6 @@ const LayerList = (() => {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function startRenamingLayer(event) {
|
||||
let p = currFile.currentLayer.menuEntry.getElementsByTagName("p")[0];
|
||||
|
||||
@ -189,36 +195,17 @@ const LayerList = (() => {
|
||||
|
||||
renamingLayer = true;
|
||||
}
|
||||
|
||||
function duplicateLayer(event, saveHistory = true) {
|
||||
function getMenuEntryIndex(list, entry) {
|
||||
for (let i=0; i<list.length; i++) {
|
||||
if (list[i] === entry) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
let layerIndex = currFile.layers.indexOf(currFile.currentLayer);
|
||||
let toDuplicate = currFile.currentLayer;
|
||||
let menuEntries = layerList.children;
|
||||
|
||||
// Increasing z-indexes of the layers above
|
||||
for (let i=getMenuEntryIndex(menuEntries, toDuplicate.menuEntry) - 1; i>=0; i--) {
|
||||
LayerList.getLayerByID(menuEntries[i].id).canvas.style.zIndex++;
|
||||
}
|
||||
Layer.maxZIndex+=2;
|
||||
|
||||
// Creating a new canvas
|
||||
let newCanvas = document.createElement("canvas");
|
||||
// Setting up the new canvas
|
||||
currFile.canvasView.append(newCanvas);
|
||||
newCanvas.style.zIndex = parseInt(currFile.currentLayer.canvas.style.zIndex) + 2;
|
||||
newCanvas.classList.add("drawingCanvas");
|
||||
|
||||
if (!layerListEntry) return console.warn('skipping adding layer because no document');
|
||||
if (!layerListEntry) return //console.warn('skipping adding layer because no document');
|
||||
|
||||
// Clone the default layer
|
||||
let toAppend = currFile.currentLayer.menuEntry.cloneNode(true);
|
||||
@ -243,46 +230,59 @@ const LayerList = (() => {
|
||||
newLayer.context.putImageData(currFile.currentLayer.context.getImageData(
|
||||
0, 0, currFile.canvasSize[0], currFile.canvasSize[1]), 0, 0);
|
||||
newLayer.updateLayerPreview();
|
||||
|
||||
LayerList.refreshZ();
|
||||
|
||||
// Basically "if I'm not adding a layer because redo() is telling meto do so", then I can save the history
|
||||
if (saveHistory) {
|
||||
new HistoryState().DuplicateLayer(newLayer, currFile.currentLayer);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteLayer(saveHistory = true) {
|
||||
// Cannot delete all the layers
|
||||
if (currFile.layers.length != 4) {
|
||||
let layerIndex = currFile.layers.indexOf(currFile.currentLayer);
|
||||
let toDelete = currFile.layers[layerIndex];
|
||||
let previousSibling = toDelete.menuEntry.previousElementSibling;
|
||||
// Adding the ids to the unused ones
|
||||
Layer.unusedIDs.push(toDelete.id);
|
||||
|
||||
// Selecting the next layer
|
||||
if (layerIndex != (currFile.layers.length - 4)) {
|
||||
currFile.layers[layerIndex + 1].selectLayer();
|
||||
}
|
||||
// or the previous one if the next one doesn't exist
|
||||
else {
|
||||
currFile.layers[layerIndex - 1].selectLayer();
|
||||
}
|
||||
|
||||
// Deleting canvas and entry
|
||||
toDelete.canvas.remove();
|
||||
toDelete.menuEntry.remove();
|
||||
|
||||
// Removing the layer from the list
|
||||
currFile.layers.splice(layerIndex, 1);
|
||||
|
||||
if (saveHistory) {
|
||||
new HistoryState().DeleteLayer(toDelete, previousSibling, layerIndex);
|
||||
}
|
||||
function clearLayers() {
|
||||
|
||||
currFile.layers.forEach(()=>deleteLayer());
|
||||
//console.log('currFile.layers.length === ',currFile.layers.length);
|
||||
for(let i = 0; i < currFile.layers.length;i++){
|
||||
const layer = currFile.layers[i];
|
||||
//console.log('i === ', i);
|
||||
//console.log('layer === ',layer);
|
||||
}
|
||||
}
|
||||
function deleteLayer(saveHistory = true) {
|
||||
//console.log('deleting layer: ', currFile.currentLayer.name, currFile.currentLayer);
|
||||
//console.trace();
|
||||
deleteLayerDirectly(currFile.currentLayer, saveHistory);
|
||||
|
||||
// Closing the menu
|
||||
closeOptionsMenu();
|
||||
}
|
||||
function deleteLayerDirectly(layer, saveHistory = true) {
|
||||
let layerIndex = currFile.layers.indexOf(layer);
|
||||
let toDelete = currFile.layers[layerIndex];
|
||||
let previousSibling = toDelete.menuEntry.previousElementSibling;
|
||||
// Adding the ids to the unused ones
|
||||
Layer.unusedIDs.push(toDelete.id);
|
||||
|
||||
if(layer.isSelected) {
|
||||
// Selecting the nearest layer
|
||||
const nearestLayer = (currFile.layers[layerIndex + 1] ?? currFile.layers[layerIndex - 1]);
|
||||
if(nearestLayer){
|
||||
nearestLayer.selectLayer();
|
||||
//console.log('changing to nearest layer');
|
||||
}
|
||||
}
|
||||
|
||||
// Deleting canvas and entry
|
||||
toDelete.canvas.remove();
|
||||
toDelete.menuEntry.remove();
|
||||
|
||||
// Removing the layer from the list
|
||||
currFile.layers.splice(layerIndex, 1);
|
||||
|
||||
if (saveHistory) {
|
||||
new HistoryState().DeleteLayer(toDelete, previousSibling, layerIndex);
|
||||
}
|
||||
}
|
||||
function merge(saveHistory = true) {
|
||||
// Saving the layer that should be merged
|
||||
let toMerge = currFile.currentLayer;
|
||||
@ -312,7 +312,6 @@ const LayerList = (() => {
|
||||
currFile.currentLayer.updateLayerPreview();
|
||||
}
|
||||
}
|
||||
|
||||
function flatten(onlyVisible) {
|
||||
if (!onlyVisible) {
|
||||
// Selecting the first layer
|
||||
@ -366,7 +365,6 @@ const LayerList = (() => {
|
||||
currFile.currentLayer.updateLayerPreview();
|
||||
}
|
||||
}
|
||||
|
||||
function openOptionsMenu(event) {
|
||||
if (event.which == 3) {
|
||||
let selectedId;
|
||||
@ -385,29 +383,47 @@ const LayerList = (() => {
|
||||
getLayerByID(selectedId).selectLayer(false);
|
||||
}
|
||||
}
|
||||
|
||||
function closeOptionsMenu(event) {
|
||||
Layer.layerOptions.style.visibility = "hidden";
|
||||
currFile.currentLayer.rename();
|
||||
renamingLayer = false;
|
||||
}
|
||||
|
||||
function getLayerListEntries() {
|
||||
return layerList;
|
||||
}
|
||||
|
||||
function isRenamingLayer() {
|
||||
return renamingLayer;
|
||||
}
|
||||
|
||||
function refreshZ() {
|
||||
try{
|
||||
let selectedZIndex = 0;
|
||||
let maxZ = 0;
|
||||
currFile.layers.forEach((layer, i) => {
|
||||
const _i = currFile.layers.length - i;
|
||||
let z = (_i+1) * 10;
|
||||
if(maxZ < z)maxZ = z;
|
||||
layer.canvas.style.zIndex = z;
|
||||
if(layer.isSelected){
|
||||
selectedZIndex = z;
|
||||
}
|
||||
});
|
||||
currFile.checkerBoard.canvas.style.zIndex = 1;
|
||||
currFile.pixelGrid.canvas.style.zIndex = 2;
|
||||
currFile.TMPLayer.canvas.style.zIndex = selectedZIndex + 1;
|
||||
currFile.VFXLayer.canvas.style.zIndex = maxZ + 10;
|
||||
}catch(e){}
|
||||
}
|
||||
return {
|
||||
refreshZ,
|
||||
addLayer,
|
||||
mergeLayers,
|
||||
getLayerByID,
|
||||
getLayerByName,
|
||||
renameLayer: startRenamingLayer,
|
||||
duplicateLayer,
|
||||
clearLayers,
|
||||
deleteLayer,
|
||||
deleteLayerDirectly,
|
||||
merge,
|
||||
flatten,
|
||||
closeOptionsMenu,
|
||||
|
@ -6,7 +6,7 @@ const PresetModule = (() => {
|
||||
};
|
||||
|
||||
function instrumentPresetMenu() {
|
||||
console.info("Initializing presets..");
|
||||
//console.info("Initializing presets..");
|
||||
// Add a button for all the presets available
|
||||
const presetsMenu = document.getElementById('preset-menu');
|
||||
Object.keys(presets).forEach((presetName,) => {
|
||||
@ -17,7 +17,7 @@ const PresetModule = (() => {
|
||||
presetsMenu.appendChild(button);
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
console.log("Preset: " + presetName);
|
||||
////console.log("Preset: " + presetName);
|
||||
//change dimentions on new pixel form
|
||||
Util.setValue('size-width', presets[presetName].width);
|
||||
Util.setValue('size-height', presets[presetName].height);
|
||||
|
@ -14,7 +14,7 @@ const Settings = (() => {
|
||||
settingsFromCookie = Cookies.get('pixelEditorSettings');
|
||||
|
||||
if(!settingsFromCookie) {
|
||||
console.log('settings cookie not found');
|
||||
////console.log('settings cookie not found');
|
||||
|
||||
settings = {
|
||||
switchToChangedColor: true,
|
||||
@ -27,8 +27,8 @@ const Settings = (() => {
|
||||
};
|
||||
}
|
||||
else{
|
||||
console.log('settings cookie found');
|
||||
console.log(settingsFromCookie);
|
||||
////console.log('settings cookie found');
|
||||
////console.log(settingsFromCookie);
|
||||
|
||||
settings = JSON.parse(settingsFromCookie);
|
||||
}
|
||||
|
194
js/Startup.js
@ -1,6 +1,9 @@
|
||||
let DEBUG_ARR = [];
|
||||
const Startup = (() => {
|
||||
let splashPostfix = '';
|
||||
|
||||
let cacheIntervalIdx;
|
||||
|
||||
Events.on('click', 'create-button', create, false);
|
||||
Events.on('click', 'create-button-splash', create, true);
|
||||
|
||||
@ -15,7 +18,7 @@ const Startup = (() => {
|
||||
var height = Util.getValue('size-height' + splashPostfix);
|
||||
var selectedPalette = Util.getText('palette-button' + splashPostfix);
|
||||
|
||||
newPixel(width, height);
|
||||
newPixel(FileManager.defaultLPE(width,height));
|
||||
resetInput();
|
||||
|
||||
//track google event
|
||||
@ -25,15 +28,18 @@ const Startup = (() => {
|
||||
|
||||
/** Creates a new, empty file
|
||||
*
|
||||
* @param {*} width Start width of the canvas
|
||||
* @param {*} height Start height of the canvas
|
||||
* @param {*} fileContent If fileContent != null, then the newPixel is being called from the open menu
|
||||
* @param {*} lpe If lpe != null, then the newPixel is being called from the open menu
|
||||
* @param {*} skipModeConfirm If skipModeConfirm == true, then the mode switching confirmation will be skipped
|
||||
*/
|
||||
function newPixel (width, height, fileContent = null) {
|
||||
function newPixel (lpe = null, skipModeConfirm = false) {
|
||||
DEBUG_ARR.push('called Startup -> newPixel');
|
||||
console.trace();
|
||||
// The palette is empty, at the beginning
|
||||
ColorModule.resetPalette();
|
||||
|
||||
initLayers(width, height);
|
||||
lpe = FileManager.upgradeLPE(lpe);
|
||||
|
||||
initLayers(lpe);
|
||||
initPalette();
|
||||
|
||||
// Closing the "New Pixel dialogue"
|
||||
@ -44,66 +50,95 @@ const Startup = (() => {
|
||||
// The user is now able to export the Pixel
|
||||
document.getElementById('export-button').classList.remove('disabled');
|
||||
|
||||
// Now, if I opened an LPE file
|
||||
if (fileContent != null) {
|
||||
loadFromLPE(fileContent);
|
||||
// Deleting the default layer
|
||||
LayerList.deleteLayer(false);
|
||||
// Selecting the new one
|
||||
currFile.layers[1].selectLayer();
|
||||
if (lpe != null) {
|
||||
FileManager.loadFromLPE(lpe);
|
||||
}
|
||||
|
||||
EditorState.switchMode(EditorState.getCurrentMode());
|
||||
////console.log('ColorModule.getCurrentPalette() === ',ColorModule.getCurrentPalette());
|
||||
|
||||
EditorState.switchMode(EditorState.getCurrentMode(), skipModeConfirm);
|
||||
// This is not the first Pixel anymore
|
||||
EditorState.created();
|
||||
|
||||
////console.log('ColorModule.getCurrentPalette() === ',ColorModule.getCurrentPalette());
|
||||
////console.trace();
|
||||
}
|
||||
|
||||
function initLayers(width, height) {
|
||||
// Setting the general canvasSize
|
||||
currFile.canvasSize = [width, height];
|
||||
|
||||
// If this is the first pixel I'm creating since the app has started
|
||||
if (EditorState.firstPixel()) {
|
||||
// Creating the first layer
|
||||
currFile.currentLayer = new Layer(width, height, 'pixel-canvas', "");
|
||||
currFile.currentLayer.canvas.style.zIndex = 2;
|
||||
function clearLayers() {
|
||||
DEBUG_ARR.push('called Startup -> clearLayers');
|
||||
console.dir(currFile.layers);
|
||||
for(let i = currFile.layers.length-1; i >= 0;i--) {
|
||||
currFile.layers[i].delete(i);
|
||||
}
|
||||
else {
|
||||
// Deleting all the extra layers and canvases, leaving only one
|
||||
let nLayers = currFile.layers.length;
|
||||
for (let i=2; i < currFile.layers.length - nAppLayers; i++) {
|
||||
let currentEntry = currFile.layers[i].menuEntry;
|
||||
let associatedLayer;
|
||||
console.dir(currFile.layers);
|
||||
for(let i = currFile.sublayers.length-1; i >= 0;i--) {
|
||||
currFile.sublayers[i].delete(i);
|
||||
}
|
||||
}
|
||||
function initLayers(lpe) {
|
||||
DEBUG_ARR.push('called Startup -> initLayers');
|
||||
//console.group('called initLayers');
|
||||
//console.log('currFile.layers === ',currFile.layers);
|
||||
|
||||
if (currentEntry != null) {
|
||||
// Getting the associated layer
|
||||
associatedLayer = LayerList.getLayerByID(currentEntry.id);
|
||||
const width = lpe.canvasWidth = Number(lpe.canvasWidth);
|
||||
const height = lpe.canvasHeight = Number(lpe.canvasHeight);
|
||||
clearLayers();
|
||||
|
||||
// Deleting its canvas
|
||||
associatedLayer.canvas.remove();
|
||||
// debugger;
|
||||
//
|
||||
currFile.canvasSize = [width, height];
|
||||
console.log('lpe === ',lpe);
|
||||
if( lpe.layers && lpe.layers.length ) {
|
||||
currFile.currentLayer = new Layer(width, height, `pixel-canvas`,"","layer-li-template");
|
||||
currFile.sublayers.push(currFile.currentLayer);
|
||||
|
||||
// Adding the id to the unused ones
|
||||
Layer.unusedIDs.push(currentEntry.id);
|
||||
// Removing the entry from the menu
|
||||
currentEntry.remove();
|
||||
let selectedIdx = lpe.selectedLayer ?? 0;
|
||||
|
||||
lpe.layers.forEach((layerData, i) => {
|
||||
//console.log('lpe.layers[i] === ', i);
|
||||
const _i = lpe.layers.length - i;
|
||||
let layerImage = layerData.src;
|
||||
if (layerData != null) {
|
||||
// Setting id
|
||||
let createdLayer = LayerList.addLayer(layerData.id, false, layerData.name);
|
||||
if(i===selectedIdx)createdLayer.selectLayer();
|
||||
// Setting name
|
||||
createdLayer.menuEntry.getElementsByTagName("p")[0].innerHTML = layerData.name;
|
||||
|
||||
// Adding the image (I can do that because they're sorted by increasing z-index)
|
||||
let img = new Image();
|
||||
img.onload = function() {
|
||||
createdLayer.context.drawImage(img, 0, 0);
|
||||
createdLayer.updateLayerPreview();
|
||||
};
|
||||
|
||||
img.src = layerImage;
|
||||
|
||||
// Setting visibility and lock options
|
||||
if (!layerData.isVisible) {
|
||||
createdLayer.hide();
|
||||
}
|
||||
if (layerData.isLocked) {
|
||||
createdLayer.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Removing the old layers from the list
|
||||
for (let i=2; i<nLayers - nAppLayers; i++) {
|
||||
currFile.layers.splice(2, 1);
|
||||
}
|
||||
|
||||
// Setting up the current layer
|
||||
currFile.layers[1] = new Layer(width, height, currFile.layers[1].canvas, currFile.layers[1].menuEntry);
|
||||
currFile.currentLayer = currFile.layers[1];
|
||||
currFile.currentLayer.canvas.style.zIndex = 2;
|
||||
} else {
|
||||
currFile.currentLayer = new Layer(width, height, `pixel-canvas`,"");
|
||||
currFile.sublayers.push(currFile.currentLayer);
|
||||
|
||||
const defaultLayerId = "layer0";
|
||||
const defaultLayerName = "Layer 0";
|
||||
|
||||
let createdLayer = LayerList.addLayer(defaultLayerId, false, defaultLayerName);
|
||||
createdLayer.selectLayer();
|
||||
// Setting name
|
||||
createdLayer.menuEntry.getElementsByTagName("p")[0].innerHTML = defaultLayerName;
|
||||
}
|
||||
|
||||
// Adding the checkerboard behind it
|
||||
currFile.checkerBoard = new Checkerboard(width, height, null);
|
||||
// Pixel grid
|
||||
console.log("CREATED GRID");
|
||||
////console.log("CREATED GRID");
|
||||
currFile.pixelGrid = new PixelGrid(width, height, "pixel-grid");
|
||||
|
||||
// Creating the vfx layer on top of everything
|
||||
@ -111,17 +146,16 @@ const Startup = (() => {
|
||||
// Tmp layer to draw previews on
|
||||
currFile.TMPLayer = new Layer(width, height, 'tmp-canvas');
|
||||
|
||||
if (EditorState.firstPixel()) {
|
||||
// Adding the first layer and the checkerboard to the list of layers
|
||||
currFile.layers.push(currFile.checkerBoard);
|
||||
currFile.layers.push(currFile.currentLayer);
|
||||
currFile.layers.push(currFile.TMPLayer);
|
||||
currFile.layers.push(currFile.pixelGrid);
|
||||
currFile.layers.push(currFile.VFXLayer);
|
||||
}
|
||||
currFile.sublayers.push(currFile.checkerBoard);
|
||||
currFile.sublayers.push(currFile.TMPLayer);
|
||||
currFile.sublayers.push(currFile.pixelGrid);
|
||||
currFile.sublayers.push(currFile.VFXLayer);
|
||||
|
||||
LayerList.refreshZ();
|
||||
}
|
||||
|
||||
function initPalette() {
|
||||
DEBUG_ARR.push('called Startup -> initPalette');
|
||||
// Get selected palette
|
||||
let selectedPalette = Util.getText('palette-button' + splashPostfix);
|
||||
|
||||
@ -168,43 +202,8 @@ const Startup = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromLPE(fileContent) {
|
||||
// I add every layer the file had in it
|
||||
for (let i=0; i<fileContent['nLayers']; i++) {
|
||||
let layerData = fileContent['layer' + i];
|
||||
let layerImage = fileContent['layer' + i + 'ImageData'];
|
||||
|
||||
if (layerData != null) {
|
||||
// Setting id
|
||||
let createdLayer = LayerList.addLayer(layerData.id, false);
|
||||
// Setting name
|
||||
createdLayer.menuEntry.getElementsByTagName("p")[0].innerHTML = layerData.name;
|
||||
|
||||
// Adding the image (I can do that because they're sorted by increasing z-index)
|
||||
let img = new Image();
|
||||
img.onload = function() {
|
||||
createdLayer.context.drawImage(img, 0, 0);
|
||||
createdLayer.updateLayerPreview();
|
||||
|
||||
if (i == (fileContent['nLayers'] - 1)) {
|
||||
ColorModule.createPaletteFromLayers();
|
||||
}
|
||||
};
|
||||
|
||||
img.src = layerImage;
|
||||
|
||||
// Setting visibility and lock options
|
||||
if (!layerData.isVisible) {
|
||||
createdLayer.hide();
|
||||
}
|
||||
if (layerData.isLocked) {
|
||||
createdLayer.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetInput() {
|
||||
DEBUG_ARR.push('called Startup -> resetInput');
|
||||
//reset new form
|
||||
Util.setValue('size-width', 64);
|
||||
Util.setValue('size-height', 64);
|
||||
@ -214,6 +213,7 @@ const Startup = (() => {
|
||||
}
|
||||
|
||||
function newFromTemplate(preset, x, y) {
|
||||
DEBUG_ARR.push('called Startup -> newFromTemplate');
|
||||
if (preset != '') {
|
||||
const presetProperties = PresetModule.propertiesOf(preset);
|
||||
Util.setText('palette-button-splash', presetProperties.palette);
|
||||
@ -222,10 +222,12 @@ const Startup = (() => {
|
||||
x = presetProperties.width;
|
||||
y = presetProperties.height;
|
||||
}
|
||||
newPixel(x, y);
|
||||
|
||||
newPixel(FileManager.defaultLPE(x,y));
|
||||
}
|
||||
|
||||
function splashEditorMode(mode) {
|
||||
DEBUG_ARR.push('called Startup -> splashEditorMode');
|
||||
editorMode = mode;
|
||||
}
|
||||
|
||||
|
11
js/Tool.js
@ -62,12 +62,10 @@ class Tool {
|
||||
else {
|
||||
this.toolTutorial.style.top = this.mainButton.getBoundingClientRect().top - 48 + "px";
|
||||
}
|
||||
this.toolTutorial.style.display = "inline-block";
|
||||
this.toolTutorial.className = "fade-in";
|
||||
}
|
||||
hideTutorial() {
|
||||
this.toolTutorial.className = "fade-out";
|
||||
setTimeout(function(){this.toolTutorial.style.display = "none"}.bind(this), 200);
|
||||
}
|
||||
|
||||
resetTutorial() {
|
||||
@ -158,11 +156,12 @@ class Tool {
|
||||
onStart(mousePos, mouseTarget) {
|
||||
this.startMousePos = mousePos;
|
||||
}
|
||||
|
||||
|
||||
onDrag(mousePos, mouseTarget) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onEnd(mousePos, mouseTarget) {
|
||||
this.endMousePos = mousePos;
|
||||
this.endMousePos = mousePos;
|
||||
if(FileManager.cacheEnabled)FileManager.localStorageSave();
|
||||
}
|
||||
}
|
@ -47,6 +47,9 @@ const ToolManager = (() => {
|
||||
if (!EditorState.documentCreated || Dialogue.isOpen())
|
||||
return;
|
||||
|
||||
const isHoveringMenuElement = !!mouseEvent.path.find(n=>n.id && n.id.includes("-menu"));
|
||||
if(isHoveringMenuElement)return;
|
||||
|
||||
let mousePos = Input.getCursorPosition(mouseEvent);
|
||||
tools["zoom"].onMouseWheel(mousePos, mouseEvent.deltaY < 0 ? 'in' : 'out');
|
||||
}
|
||||
@ -109,7 +112,7 @@ const ToolManager = (() => {
|
||||
currTool.onRightDrag(mousePos, mouseEvent.target);
|
||||
break;
|
||||
default:
|
||||
console.log("wtf");
|
||||
////console.log("wtf");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
const TopMenuModule = (() => {
|
||||
|
||||
const mainMenuItems = document.getElementById('main-menu').children;
|
||||
let infoList = document.getElementById('editor-info');
|
||||
const mainMenuItems = document.getElementById('main-menu')?.children ?? [];
|
||||
let infoList = document.getElementById('editor-info') ?? document.createElement("div");
|
||||
let infoElements = {};
|
||||
|
||||
initMenu();
|
||||
@ -116,7 +116,12 @@ const TopMenuModule = (() => {
|
||||
}
|
||||
|
||||
function updateField(fieldId, value) {
|
||||
document.getElementById(fieldId).value = value;
|
||||
const elm = document.getElementById(fieldId);
|
||||
if(elm) {
|
||||
elm.value = value;
|
||||
} else {
|
||||
//console.warn('elm === ', elm);
|
||||
}
|
||||
}
|
||||
|
||||
function addInfoElement(fieldId, field) {
|
||||
|
@ -83,7 +83,7 @@ class Util {
|
||||
return document.getElementById(elementOrElementId);
|
||||
}
|
||||
else {
|
||||
console.log("Type not supported: " + typeof(elementOrElementId));
|
||||
////console.log("Type not supported: " + typeof(elementOrElementId));
|
||||
}
|
||||
}
|
||||
|
||||
|
211
js/canvas_util.js
Normal file
@ -0,0 +1,211 @@
|
||||
function drawTinyNumber(ctx,str,x,y) {
|
||||
const CHARS = [
|
||||
`0111110111011111`,
|
||||
`0110111001101111`,
|
||||
`1111001111001111`,
|
||||
`1111011100111111`,
|
||||
`1001100111110011`,
|
||||
`1111110000111111`,
|
||||
`1100111110111111`,
|
||||
`1111001101100110`,
|
||||
`1110101111010111`,
|
||||
`1111101111110011`,
|
||||
]
|
||||
.map(n=>n.split("").map(Number))
|
||||
;
|
||||
str.split("").reduce((xo,n)=>{
|
||||
let bitArr = CHARS[n];
|
||||
let cw = bitArr.length / 4;
|
||||
bitArr.forEach((bit,i)=>{
|
||||
const _x = x + xo + (i%cw);
|
||||
const _y = y + Math.floor(i/cw);
|
||||
if(bit)ctx.fillRect(_x,_y,1,1);
|
||||
});
|
||||
xo+=cw+1;
|
||||
return xo;
|
||||
},0);
|
||||
}
|
||||
function drawTinyText( ctx, str, x = 0, y = 0, font = "Verdana", w = 16, h = 16, xo = 0, yo = 0 ) {
|
||||
for(let i = 0; i < 4;i++){
|
||||
drawTinyTextOne( ctx, str, x, y, font, w, h, xo+0, yo+i );
|
||||
}
|
||||
}
|
||||
function drawTinyTextOne( ctx, str, x = 0, y = 0, font = "Verdana", w = 16, h = 16, xo = 0, yo = 0 ) {
|
||||
const CHARS = generateCharsFromFont(font, w, h, 8, 8, undefined, undefined, xo, yo)
|
||||
.map(n=>n.split("").map(Number))
|
||||
;
|
||||
////console.log('CHARS === ',CHARS);
|
||||
str.split("").reduce((_xo,n)=>{
|
||||
const code = n.charCodeAt(0) - 33;
|
||||
// ////console.log('n,code === ',n,code);
|
||||
let charWidth = CHARS[code].length / w;
|
||||
CHARS[code].forEach((bit,i)=>{
|
||||
const _x = x + _xo + (i%charWidth);
|
||||
const _y = y + Math.floor(i/charWidth);
|
||||
|
||||
// ////console.log('bit === ',bit);
|
||||
if(bit)ctx.fillRect(_x,_y,1,1);
|
||||
});
|
||||
_xo+=charWidth+1;
|
||||
return _xo;
|
||||
},0);
|
||||
}
|
||||
function generateCharsFromFont(font, charW = 7, charH = 7, sampleScale = 8, scale = 8, previewDiv, debugDiv, xo = 0, yo = 0) {
|
||||
return [...Array(94)].map((_,i)=>{
|
||||
const char = String.fromCharCode(i+33);
|
||||
const canvas = document.createElement('canvas');
|
||||
if(debugDiv)debugDiv.appendChild(canvas);
|
||||
const sz = sampleScale;
|
||||
const w = charW * sz;
|
||||
const h = charH * sz;
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.font = `${h}px ` + font;
|
||||
ctx.shadowColor="black";
|
||||
ctx.shadowBlur=sz*2;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "bottom";
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillText(char,w/2,h);
|
||||
ctx.strokeStyle = "black";
|
||||
ctx.strokeText(char,w/2,h);
|
||||
const imageData = ctx.getImageData(0,0,w,h);
|
||||
// ////console.log('imageData === ',imageData);
|
||||
let ret = '';
|
||||
ctx.fillStyle = "red";
|
||||
const previewCanvas = document.createElement('canvas');
|
||||
previewCanvas.width = charW;
|
||||
previewCanvas.height = charH;
|
||||
const ctx2 = previewCanvas.getContext('2d');
|
||||
if(previewDiv)previewDiv.appendChild(previewCanvas);
|
||||
|
||||
for(let y = scale/2; y < h;y+=scale) {
|
||||
for(let x = scale/2; x < w;x+=scale) {
|
||||
const _x = (x-(scale/2))/scale;
|
||||
const _y = (y-(scale/2))/scale;
|
||||
const _imageData = ctx.getImageData(x+xo,y+yo,1,1);
|
||||
let specResult = _imageData.data[3] > 128;
|
||||
ctx2.fillStyle = "black";
|
||||
if(specResult) {
|
||||
ctx2.fillRect(_x,_y,1,1);
|
||||
ret += "1";
|
||||
} else {
|
||||
ret += "0";
|
||||
}
|
||||
ctx.fillStyle = specResult ? "#00ff00" : "#ff0000";
|
||||
ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
})
|
||||
}
|
||||
function pixelButtonMeta(x, y, img, options) {
|
||||
return Object.entries(options).reduce((r,n,i)=>{
|
||||
const [k,v] = n;
|
||||
|
||||
})
|
||||
}
|
||||
function pixelButton(x,y,xo,yo,img,colors=["#112","#334","#556","#778","#99A","#BBC"]) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const w = img.width+4;
|
||||
const h = img.height+5;
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = colors[0];
|
||||
ctx.fillRect(x+0+xo,y+yo,img.width+4,img.height+5);
|
||||
ctx.fillStyle = colors[1];
|
||||
ctx.fillRect(x+0+xo,y+yo,img.width+4,img.height+4);
|
||||
ctx.fillStyle = colors[3];
|
||||
ctx.fillRect(x+1+xo,y+yo,img.width+2,img.height+4);
|
||||
ctx.fillStyle = colors[3];
|
||||
ctx.fillRect(x+0+xo,y+yo,img.width+4,img.height+2);
|
||||
ctx.fillStyle = colors[2];
|
||||
ctx.fillRect(x+1+xo,y+yo,img.width+2,img.height+2);
|
||||
ctx.drawImage(img,x+2,y+2);
|
||||
return canvas;
|
||||
}
|
||||
function scaleImageData(imageData, scale) {
|
||||
if (scale === 1) return imageData;
|
||||
var scaledImageData = document.createElement("canvas").getContext("2d").createImageData(imageData.width * scale, imageData.height * scale);
|
||||
for (var row = 0; row < imageData.height; row++) {
|
||||
for (var col = 0; col < imageData.width; col++) {
|
||||
var sourcePixel = [
|
||||
imageData.data[(row * imageData.width + col) * 4 + 0],
|
||||
imageData.data[(row * imageData.width + col) * 4 + 1],
|
||||
imageData.data[(row * imageData.width + col) * 4 + 2],
|
||||
imageData.data[(row * imageData.width + col) * 4 + 3]
|
||||
];
|
||||
for (var y = 0; y < scale; y++) {
|
||||
var destRow = row * scale + y;
|
||||
for (var x = 0; x < scale; x++) {
|
||||
var destCol = col * scale + x;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
scaledImageData.data[(destRow * scaledImageData.width + destCol) * 4 + i] =
|
||||
sourcePixel[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return scaledImageData;
|
||||
}
|
||||
|
||||
function imageChopper(img,tileHeight,tileWidth) {
|
||||
const c = document.createElement('canvas');
|
||||
const w = c.width = img.width;
|
||||
const h = c.height = img.height;
|
||||
const ctx = c.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const arr = [];
|
||||
for (let y = 0; y < h; y += tileHeight) {
|
||||
for (let x = 0; x < w; x += tileWidth) {
|
||||
const imageData = ctx.getImageData(x, y, tileWidth, tileHeight);
|
||||
const tileCanvas = document.createElement('canvas');
|
||||
tileCanvas.width = tileWidth;
|
||||
tileCanvas.height = tileHeight;
|
||||
const tileCtx = tileCanvas.getContext('2d');
|
||||
tileCtx.putImageData(imageData,0,0);
|
||||
arr.push(tileCanvas);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
function imageDataToCanvas(imageData, x = 0, y = 0) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = imageData.width;
|
||||
canvas.height = imageData.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.putImageData(imageData, x, y);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function tilesToCanvas(arr,columns,tilesData) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const rows = Math.floor(arr.length / columns);
|
||||
if(rows !== (arr.length / columns)){
|
||||
debugger;
|
||||
//console.error("wtf this should never happen...");
|
||||
}
|
||||
const w = tilesData[0].width * columns;
|
||||
const h = tilesData[0].height * rows;
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
/* first draw the tiles... */
|
||||
arr.forEach((tileIdx,i) => {
|
||||
if(tileIdx >= 0) {
|
||||
const c = tilesData[tileIdx];
|
||||
const x = i%columns;
|
||||
const y = Math.floor(i/columns);
|
||||
document.body.appendChild(c);
|
||||
ctx.drawImage(c, x * c.width, y * c.width);
|
||||
}
|
||||
});
|
||||
|
||||
/* then draw tile fringe? */ // TODO
|
||||
|
||||
return canvas;
|
||||
}
|
385
js/color_utils.js
Normal file
@ -0,0 +1,385 @@
|
||||
// UTILITY
|
||||
|
||||
let firstColor = "#000000", secondColor = "#000000";
|
||||
let log = document.getElementById("log");
|
||||
|
||||
// CONSTS
|
||||
|
||||
// Degrees to radiants
|
||||
let degreesToRad = Math.PI / 180;
|
||||
// I'm pretty sure that precision is necessary
|
||||
let referenceWhite = {x: 95.05, y: 100, z: 108.89999999999999};
|
||||
|
||||
// COLOUR SIMILARITY
|
||||
|
||||
// Min distance under which 2 colours are considered similar
|
||||
let distanceThreshold = 10;
|
||||
|
||||
// Threshold used to consider a colour "dark"
|
||||
let darkColoursThreshold = 50;
|
||||
// Threshold used to tell if 2 dark colours are similar
|
||||
let darkColoursSimilarityThreshold = 40;
|
||||
|
||||
// Threshold used to consider a colour "light"
|
||||
let lightColoursThreshold = 190;
|
||||
// Threshold used to tell if 2 light colours are similar
|
||||
let lightColoursSimilarityThreshold = 30;
|
||||
|
||||
|
||||
// document.getElementById("color1").addEventListener("change", updateColor);
|
||||
// document.getElementById("color2").addEventListener("change", updateColor);
|
||||
|
||||
function updateColor(e) {
|
||||
////console.log(e);
|
||||
|
||||
switch (e.target.id) {
|
||||
case "color1":
|
||||
firstColor = e.target.value;
|
||||
break;
|
||||
case "color2":
|
||||
secondColor = e.target.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
updateWarnings();
|
||||
}
|
||||
|
||||
function updateWarnings() {
|
||||
let toSet = "";
|
||||
////console.log("colors: " + firstColor + ", " + secondColor);
|
||||
toSet += similarColours(firstColor, secondColor) ? 'Colours are similar!' + '\n' : "";
|
||||
|
||||
log.innerHTML = toSet;
|
||||
}
|
||||
|
||||
/**********************SECTION: COLOUR SIMILARITY*********************************/
|
||||
|
||||
function similarColours(rgb1, rgb2) {
|
||||
let ret = differenceCiede2000(rgb1, rgb2);
|
||||
const lightInRange = lightColoursCheck(rgb1, rgb2);
|
||||
const darkInRange = darkColoursCheck(rgb1, rgb2);
|
||||
|
||||
// if((ret < distanceThreshold && lightColoursCheck(rgb1, rgb2)) || darkColoursCheck(rgb1, rgb2)){
|
||||
// return ret;
|
||||
// }
|
||||
// return 100;
|
||||
if((ret < distanceThreshold && lightInRange) || darkInRange) {
|
||||
// ////console.log('GOOD ret === ',ret);
|
||||
return ret;
|
||||
} else {
|
||||
// ////console.log('BAD ret === ',ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function lightColoursCheck(c1, c2) {
|
||||
let rDelta = Math.abs(c1.r - c2.r);
|
||||
let gDelta = Math.abs(c1.g - c2.g);
|
||||
let bDelta = Math.abs(c1.b - c2.b);
|
||||
|
||||
// Checking only if the colours are dark enough
|
||||
if (c1.r > lightColoursThreshold && c1.g > lightColoursThreshold && c1.b > lightColoursThreshold &&
|
||||
c2.r > lightColoursThreshold && c2.g > lightColoursThreshold && c2.b > lightColoursThreshold) {
|
||||
return rDelta < lightColoursSimilarityThreshold && gDelta < lightColoursSimilarityThreshold &&
|
||||
bDelta < lightColoursSimilarityThreshold;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function darkColoursCheck(c1, c2) {
|
||||
let rDelta = Math.abs(c1.r - c2.r);
|
||||
let gDelta = Math.abs(c1.g - c2.g);
|
||||
let bDelta = Math.abs(c1.b - c2.b);
|
||||
|
||||
// Checking only if the colours are dark enough
|
||||
if (c1.r < darkColoursThreshold && c1.g < darkColoursThreshold && c1.b < darkColoursThreshold &&
|
||||
c2.r < darkColoursThreshold && c2.g < darkColoursThreshold && c2.b < darkColoursThreshold) {
|
||||
return rDelta < darkColoursSimilarityThreshold && gDelta < darkColoursSimilarityThreshold &&
|
||||
bDelta < darkColoursSimilarityThreshold;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Distance based on CIEDE2000 (https://en.wikipedia.org/wiki/Color_difference#CIEDE2000)
|
||||
function differenceCiede2000(c1, c2) {
|
||||
var kL = 1, kC = 1, kH = 0.9;
|
||||
var LabStd = RGBtoCIELAB(c1);
|
||||
var LabSmp = RGBtoCIELAB(c2);
|
||||
|
||||
var lStd = LabStd.l;
|
||||
var aStd = LabStd.a;
|
||||
var bStd = LabStd.b;
|
||||
var cStd = Math.sqrt(aStd * aStd + bStd * bStd);
|
||||
|
||||
var lSmp = LabSmp.l;
|
||||
var aSmp = LabSmp.a;
|
||||
var bSmp = LabSmp.b;
|
||||
var cSmp = Math.sqrt(aSmp * aSmp + bSmp * bSmp);
|
||||
|
||||
var cAvg = (cStd + cSmp) / 2;
|
||||
|
||||
var G = 0.5 * (1 - Math.sqrt(Math.pow(cAvg, 7) / (Math.pow(cAvg, 7) + Math.pow(25, 7))));
|
||||
|
||||
var apStd = aStd * (1 + G);
|
||||
var apSmp = aSmp * (1 + G);
|
||||
|
||||
var cpStd = Math.sqrt(apStd * apStd + bStd * bStd);
|
||||
var cpSmp = Math.sqrt(apSmp * apSmp + bSmp * bSmp);
|
||||
|
||||
var hpStd = Math.abs(apStd) + Math.abs(bStd) === 0 ? 0 : Math.atan2(bStd, apStd);
|
||||
hpStd += (hpStd < 0) * 2 * Math.PI;
|
||||
|
||||
var hpSmp = Math.abs(apSmp) + Math.abs(bSmp) === 0 ? 0 : Math.atan2(bSmp, apSmp);
|
||||
hpSmp += (hpSmp < 0) * 2 * Math.PI;
|
||||
|
||||
var dL = lSmp - lStd;
|
||||
var dC = cpSmp - cpStd;
|
||||
|
||||
var dhp = cpStd * cpSmp === 0 ? 0 : hpSmp - hpStd;
|
||||
dhp -= (dhp > Math.PI) * 2 * Math.PI;
|
||||
dhp += (dhp < -Math.PI) * 2 * Math.PI;
|
||||
|
||||
var dH = 2 * Math.sqrt(cpStd * cpSmp) * Math.sin(dhp / 2);
|
||||
|
||||
var Lp = (lStd + lSmp) / 2;
|
||||
var Cp = (cpStd + cpSmp) / 2;
|
||||
|
||||
var hp;
|
||||
if (cpStd * cpSmp === 0) {
|
||||
hp = hpStd + hpSmp;
|
||||
} else {
|
||||
hp = (hpStd + hpSmp) / 2;
|
||||
hp -= (Math.abs(hpStd - hpSmp) > Math.PI) * Math.PI;
|
||||
hp += (hp < 0) * 2 * Math.PI;
|
||||
}
|
||||
|
||||
var Lpm50 = Math.pow(Lp - 50, 2);
|
||||
var T = 1 -
|
||||
0.17 * Math.cos(hp - Math.PI / 6) +
|
||||
0.24 * Math.cos(2 * hp) +
|
||||
0.32 * Math.cos(3 * hp + Math.PI / 30) -
|
||||
0.20 * Math.cos(4 * hp - 63 * Math.PI / 180);
|
||||
|
||||
var Sl = 1 + (0.015 * Lpm50) / Math.sqrt(20 + Lpm50);
|
||||
var Sc = 1 + 0.045 * Cp;
|
||||
var Sh = 1 + 0.015 * Cp * T;
|
||||
|
||||
var deltaTheta = 30 * Math.PI / 180 * Math.exp(-1 * Math.pow((180 / Math.PI * hp - 275)/25, 2));
|
||||
var Rc = 2 * Math.sqrt(
|
||||
Math.pow(Cp, 7) / (Math.pow(Cp, 7) + Math.pow(25, 7))
|
||||
);
|
||||
|
||||
var Rt = -1 * Math.sin(2 * deltaTheta) * Rc;
|
||||
|
||||
return Math.sqrt(
|
||||
Math.pow(dL / (kL * Sl), 2) +
|
||||
Math.pow(dC / (kC * Sc), 2) +
|
||||
Math.pow(dH / (kH * Sh), 2) +
|
||||
Rt * dC / (kC * Sc) * dH / (kH * Sh)
|
||||
);
|
||||
}
|
||||
|
||||
/**********************SECTION: COLOUR CONVERSIONS****************************** */
|
||||
|
||||
/**
|
||||
* Converts an HSL color value to RGB. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*
|
||||
* @param {number} h The hue
|
||||
* @param {number} s The saturation
|
||||
* @param {number} l The lightness
|
||||
* @return {Array} The RGB representation
|
||||
*/
|
||||
function hslToRgb(h, s, l){
|
||||
var r, g, b;
|
||||
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
|
||||
if(s == 0){
|
||||
r = g = b = l; // achromatic
|
||||
}else{
|
||||
var hue2rgb = function hue2rgb(p, q, t){
|
||||
if(t < 0) t += 1;
|
||||
if(t > 1) t -= 1;
|
||||
if(t < 1/6) return p + (q - p) * 6 * t;
|
||||
if(t < 1/2) return q;
|
||||
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
var p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1/3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1/3);
|
||||
}
|
||||
|
||||
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||||
}
|
||||
|
||||
function hsvToRgb(h, s, v) {
|
||||
var r, g, b;
|
||||
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
v /= 100;
|
||||
var i = Math.floor(h * 6);
|
||||
var f = h * 6 - i;
|
||||
var p = v * (1 - s);
|
||||
var q = v * (1 - f * s);
|
||||
var t = v * (1 - (1 - f) * s);
|
||||
|
||||
switch (i % 6) {
|
||||
case 0: r = v, g = t, b = p; break;
|
||||
case 1: r = q, g = v, b = p; break;
|
||||
case 2: r = p, g = v, b = t; break;
|
||||
case 3: r = p, g = q, b = v; break;
|
||||
case 4: r = t, g = p, b = v; break;
|
||||
case 5: r = v, g = p, b = q; break;
|
||||
}
|
||||
|
||||
return [ r * 255, g * 255, b * 255 ];
|
||||
}
|
||||
|
||||
function hslToHex(h, s, l) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
let r, g, b;
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const hue2rgb = (p, q, t) => {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
};
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
const toHex = x => {
|
||||
const hex = Math.round(x * 255).toString(16);
|
||||
return hex.length === 1 ? '0' + hex : hex;
|
||||
};
|
||||
|
||||
return `${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
}
|
||||
|
||||
function rgbToHsl(col) {
|
||||
let r = col.r;
|
||||
let g = col.g;
|
||||
let b = col.b;
|
||||
|
||||
r /= 255, g /= 255, b /= 255;
|
||||
|
||||
let max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
let myH, myS, myL = (max + min) / 2;
|
||||
|
||||
if (max == min) {
|
||||
myH = myS = 0; // achromatic
|
||||
}
|
||||
else {
|
||||
let d = max - min;
|
||||
myS = myL > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: myH = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: myH = (b - r) / d + 2; break;
|
||||
case b: myH = (r - g) / d + 4; break;
|
||||
}
|
||||
|
||||
myH /= 6;
|
||||
}
|
||||
|
||||
return {h: myH, s: myS, l: myL };
|
||||
}
|
||||
|
||||
function rgbToHsv(col) {
|
||||
let r = col.r;
|
||||
let g = col.g;
|
||||
let b = col.b;
|
||||
|
||||
r /= 255, g /= 255, b /= 255;
|
||||
|
||||
let max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
let myH, myS, myV = max;
|
||||
|
||||
let d = max - min;
|
||||
myS = max == 0 ? 0 : d / max;
|
||||
|
||||
if (max == min) {
|
||||
myH = 0; // achromatic
|
||||
}
|
||||
else {
|
||||
switch (max) {
|
||||
case r: myH = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: myH = (b - r) / d + 2; break;
|
||||
case b: myH = (r - g) / d + 4; break;
|
||||
}
|
||||
|
||||
myH /= 6;
|
||||
}
|
||||
|
||||
return {h: myH, s: myS, v: myV};
|
||||
}
|
||||
|
||||
function RGBtoCIELAB(rgbColour) {
|
||||
// Convert to XYZ first via matrix transformation
|
||||
let x = 0.412453 * rgbColour.r + 0.357580 * rgbColour.g + 0.180423 * rgbColour.b;
|
||||
let y = 0.212671 * rgbColour.r + 0.715160 * rgbColour.g + 0.072169 * rgbColour.b;
|
||||
let z = 0.019334 * rgbColour.r + 0.119193 * rgbColour.g + 0.950227 * rgbColour.b;
|
||||
|
||||
let xFunc = CIELABconvF(x / referenceWhite.x);
|
||||
let yFunc = CIELABconvF(y / referenceWhite.y);
|
||||
let zFunc = CIELABconvF(z / referenceWhite.z);
|
||||
|
||||
let myL = 116 * yFunc - 16;
|
||||
let myA = 500 * (xFunc - yFunc);
|
||||
let myB = 200 * (yFunc - zFunc);
|
||||
|
||||
return {l: myL, a: myA, b: myB};
|
||||
|
||||
}
|
||||
function CIELABconvF(value) {
|
||||
if (value > Math.pow(6/29, 3)) {
|
||||
return Math.cbrt(value);
|
||||
}
|
||||
|
||||
return 1/3 * Math.pow(6/29, 2) * value + 4/29;
|
||||
}
|
||||
|
||||
function colorToRGB(color) {
|
||||
if(window.colorCache && window.colorCache[color]){
|
||||
return window.colorCache[color];
|
||||
}
|
||||
if (!window.cachedCtx) {
|
||||
window.cachedCtx = document.createElement("canvas").getContext("2d");
|
||||
window.colorCache = {};
|
||||
}
|
||||
let ctx = window.cachedCtx;
|
||||
ctx.fillStyle = color;
|
||||
return hexToRgb(ctx.fillStyle);
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16)
|
||||
};
|
||||
}
|
||||
}
|
338
js/color_utils3.js
Normal file
@ -0,0 +1,338 @@
|
||||
// Min distance under which 2 colours are considered similar
|
||||
let distanceThreshold = 10;
|
||||
|
||||
// Threshold used to consider a colour "dark"
|
||||
let darkColoursThreshold = 50;
|
||||
// Threshold used to tell if 2 dark colours are similar
|
||||
let darkColoursSimilarityThreshold = 40;
|
||||
|
||||
// Threshold used to consider a colour "light"
|
||||
let lightColoursThreshold = 190;
|
||||
// Threshold used to tell if 2 light colours are similar
|
||||
let lightColoursSimilarityThreshold = 30;
|
||||
|
||||
let referenceWhite = {
|
||||
x: 95.05,
|
||||
y: 100,
|
||||
z: 108.89999999999999
|
||||
};
|
||||
const example = {
|
||||
"red": [
|
||||
"#bf6f4a",
|
||||
"#e07438",
|
||||
"#c64524",
|
||||
"#ff5000"
|
||||
],
|
||||
"green": [
|
||||
"#99e65f",
|
||||
"#5ac54f",
|
||||
"#33984b"
|
||||
],
|
||||
"blue": [
|
||||
"#0069aa",
|
||||
"#0098dc",
|
||||
"#00cdf9"
|
||||
],
|
||||
"cyan": [
|
||||
"#0069aa",
|
||||
"#0098dc",
|
||||
"#00cdf9",
|
||||
"#0cf1ff"
|
||||
],
|
||||
"yellow": [
|
||||
"#ffa214",
|
||||
"#ffc825",
|
||||
"#ffeb57"
|
||||
],
|
||||
"magenta": [
|
||||
"#db3ffd"
|
||||
],
|
||||
"light": [
|
||||
"#ffffff",
|
||||
"#f9e6cf",
|
||||
"#fdd2ed"
|
||||
],
|
||||
"dark": [
|
||||
"#131313",
|
||||
"#1b1b1b",
|
||||
"#272727",
|
||||
"#3d3d3d",
|
||||
"#5d5d5d"
|
||||
],
|
||||
"brown": [
|
||||
"#e69c69",
|
||||
"#f6ca9f",
|
||||
"#f9e6cf",
|
||||
"#edab50",
|
||||
"#e07438",
|
||||
"#ed7614",
|
||||
"#ffa214",
|
||||
"#ffc825",
|
||||
"#ffeb57"
|
||||
],
|
||||
"neon": [
|
||||
"#ff0040",
|
||||
"#ff5000",
|
||||
"#ed7614",
|
||||
"#ffa214",
|
||||
"#ffc825",
|
||||
"#0098dc",
|
||||
"#00cdf9",
|
||||
"#0cf1ff",
|
||||
"#7a09fa",
|
||||
"#3003d9"
|
||||
]
|
||||
};
|
||||
const COLOR_META = {
|
||||
red: { color: "#ff0000", flux:{ h:25, v:40, s:40} },
|
||||
green: { color: "#00ff00", flux:{ h:35} },
|
||||
blue: { color: "#0077dd", flux:{ h:25, v:30, s:30} },
|
||||
cyan: { color: "#00ffff", flux:{ h:25, v:40, s:40} },
|
||||
yellow: { color: "#ffff00", flux:{ h:25, v:40, s:40} },
|
||||
magenta: { color: "#ff00ff", flux:{ h:15, v:40, s:40} },
|
||||
light: { color: "#ffffff", flux:{ v:10, s:30} },
|
||||
dark: { color: "#000000", flux:{ v:30, v:40, s:20} },
|
||||
brown: { color: "#ffaa00", flux:{ h:20} },
|
||||
neon: { color: "#00ffff", flux:{ s:20, v:20} },
|
||||
};
|
||||
Object.keys(COLOR_META).forEach(metaName=>{
|
||||
COLOR_META[metaName].colorMeta = colorMeta(COLOR_META[metaName].color);
|
||||
});
|
||||
function paletteMeta(colorArr) {
|
||||
const colorMetaArr = colorArr.map(colorMeta);
|
||||
//////console.log('colorMetaArr === ',colorMetaArr);
|
||||
|
||||
const ret = {};
|
||||
Object.keys(COLOR_META).forEach(metaName=>{
|
||||
const {color,colorMeta,flux} = COLOR_META[metaName];
|
||||
|
||||
const fluxKeys = Object.keys(flux);
|
||||
|
||||
ret[metaName] = colorArr.filter((c,i)=>{
|
||||
const colorMeta2 = colorMetaArr[i];
|
||||
return fluxKeys.filter(k=>{
|
||||
return (colorMeta[k] + flux[k]) > colorMeta2[k]
|
||||
&&
|
||||
(colorMeta[k] - flux[k]) < colorMeta2[k]
|
||||
;
|
||||
}).length === fluxKeys.length;
|
||||
});
|
||||
|
||||
});
|
||||
//////console.log(JSON.stringify(ret,null,4));
|
||||
return ret;
|
||||
}
|
||||
function colorMeta(colorStr) {
|
||||
const rgb = colorToRGB(colorStr);
|
||||
const hsv = rgb2hsv(rgb.r, rgb.g, rgb.b);
|
||||
const lab = rgb2lab(rgb.r, rgb.g, rgb.b);
|
||||
const cie = {c:lab.l,i:lab.a,e:lab.b};
|
||||
return {
|
||||
...rgb,
|
||||
...hsv,
|
||||
...cie
|
||||
};
|
||||
}
|
||||
function rgb2hex(r, g, b) {
|
||||
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
}
|
||||
function rgb2hsv(r, g, b) {
|
||||
let rabs, gabs, babs, rr, gg, bb, h, s, v, diff, diffc, percentRoundFn;
|
||||
rabs = r / 255;
|
||||
gabs = g / 255;
|
||||
babs = b / 255;
|
||||
v = Math.max(rabs, gabs, babs),
|
||||
diff = v - Math.min(rabs, gabs, babs);
|
||||
diffc = c => (v - c) / 6 / diff + 1 / 2;
|
||||
percentRoundFn = num => Math.round(num * 100) / 100;
|
||||
if (diff == 0) {
|
||||
h = s = 0;
|
||||
} else {
|
||||
s = diff / v;
|
||||
rr = diffc(rabs);
|
||||
gg = diffc(gabs);
|
||||
bb = diffc(babs);
|
||||
|
||||
if (rabs === v) {
|
||||
h = bb - gg;
|
||||
} else if (gabs === v) {
|
||||
h = (1 / 3) + rr - bb;
|
||||
} else if (babs === v) {
|
||||
h = (2 / 3) + gg - rr;
|
||||
}
|
||||
if (h < 0) {
|
||||
h += 1;
|
||||
}else if (h > 1) {
|
||||
h -= 1;
|
||||
}
|
||||
}
|
||||
return {
|
||||
h: Math.round(h * 360),
|
||||
s: percentRoundFn(s * 100),
|
||||
v: percentRoundFn(v * 100)
|
||||
};
|
||||
}
|
||||
|
||||
function similarColors(rgb1, rgb2) {
|
||||
let ret = differenceCiede2000(rgb1, rgb2)
|
||||
//////console.log(ret);
|
||||
return (ret < distanceThreshold && lightColoursCheck(rgb1, rgb2)) || darkColoursCheck(rgb1, rgb2);
|
||||
}
|
||||
function lightColoursCheck(rgb1, rgb2) {
|
||||
let rDelta = Math.abs(rgb1.r - rgb2.r);
|
||||
let gDelta = Math.abs(rgb1.g - rgb2.g);
|
||||
let bDelta = Math.abs(rgb1.b - rgb2.b);
|
||||
|
||||
// Checking only if the colours are dark enough
|
||||
if (rgb1.r > lightColoursThreshold && rgb1.g > lightColoursThreshold && rgb1.b > lightColoursThreshold &&
|
||||
rgb2.r > lightColoursThreshold && rgb2.g > lightColoursThreshold && rgb2.b > lightColoursThreshold) {
|
||||
return rDelta < lightColoursSimilarityThreshold && gDelta < lightColoursSimilarityThreshold &&
|
||||
bDelta < lightColoursSimilarityThreshold;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
function darkColoursCheck(rgb1, rgb2) {
|
||||
let rDelta = Math.abs(rgb1.r - rgb2.r);
|
||||
let gDelta = Math.abs(rgb1.g - rgb2.g);
|
||||
let bDelta = Math.abs(rgb1.b - rgb2.b);
|
||||
|
||||
// Checking only if the colours are dark enough
|
||||
if (rgb1.r < darkColoursThreshold && rgb1.g < darkColoursThreshold && rgb1.b < darkColoursThreshold &&
|
||||
rgb2.r < darkColoursThreshold && rgb2.g < darkColoursThreshold && rgb2.b < darkColoursThreshold) {
|
||||
return rDelta < darkColoursSimilarityThreshold && gDelta < darkColoursSimilarityThreshold &&
|
||||
bDelta < darkColoursSimilarityThreshold;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// Distance based on CIEDE2000 (https://en.wikipedia.org/wiki/Color_difference#CIEDE2000)
|
||||
function differenceCiede2000(rgb1, rgb2) {
|
||||
var kL = 1,
|
||||
kC = 1,
|
||||
kH = 0.9;
|
||||
var LabStd = rgb2lab(rgb1);
|
||||
var LabSmp = rgb2lab(rgb2);
|
||||
|
||||
var lStd = LabStd.l;
|
||||
var aStd = LabStd.a;
|
||||
var bStd = LabStd.b;
|
||||
var cStd = Math.sqrt(aStd * aStd + bStd * bStd);
|
||||
|
||||
var lSmp = LabSmp.l;
|
||||
var aSmp = LabSmp.a;
|
||||
var bSmp = LabSmp.b;
|
||||
var cSmp = Math.sqrt(aSmp * aSmp + bSmp * bSmp);
|
||||
|
||||
var cAvg = (cStd + cSmp) / 2;
|
||||
|
||||
var G = 0.5 * (1 - Math.sqrt(Math.pow(cAvg, 7) / (Math.pow(cAvg, 7) + Math.pow(25, 7))));
|
||||
|
||||
var apStd = aStd * (1 + G);
|
||||
var apSmp = aSmp * (1 + G);
|
||||
|
||||
var cpStd = Math.sqrt(apStd * apStd + bStd * bStd);
|
||||
var cpSmp = Math.sqrt(apSmp * apSmp + bSmp * bSmp);
|
||||
|
||||
var hpStd = Math.abs(apStd) + Math.abs(bStd) === 0 ? 0 : Math.atan2(bStd, apStd);
|
||||
hpStd += (hpStd < 0) * 2 * Math.PI;
|
||||
|
||||
var hpSmp = Math.abs(apSmp) + Math.abs(bSmp) === 0 ? 0 : Math.atan2(bSmp, apSmp);
|
||||
hpSmp += (hpSmp < 0) * 2 * Math.PI;
|
||||
|
||||
var dL = lSmp - lStd;
|
||||
var dC = cpSmp - cpStd;
|
||||
|
||||
var dhp = cpStd * cpSmp === 0 ? 0 : hpSmp - hpStd;
|
||||
dhp -= (dhp > Math.PI) * 2 * Math.PI;
|
||||
dhp += (dhp < -Math.PI) * 2 * Math.PI;
|
||||
|
||||
var dH = 2 * Math.sqrt(cpStd * cpSmp) * Math.sin(dhp / 2);
|
||||
|
||||
var Lp = (lStd + lSmp) / 2;
|
||||
var Cp = (cpStd + cpSmp) / 2;
|
||||
|
||||
var hp;
|
||||
if (cpStd * cpSmp === 0) {
|
||||
hp = hpStd + hpSmp;
|
||||
} else {
|
||||
hp = (hpStd + hpSmp) / 2;
|
||||
hp -= (Math.abs(hpStd - hpSmp) > Math.PI) * Math.PI;
|
||||
hp += (hp < 0) * 2 * Math.PI;
|
||||
}
|
||||
|
||||
var Lpm50 = Math.pow(Lp - 50, 2);
|
||||
var T = 1 -
|
||||
0.17 * Math.cos(hp - Math.PI / 6) +
|
||||
0.24 * Math.cos(2 * hp) +
|
||||
0.32 * Math.cos(3 * hp + Math.PI / 30) -
|
||||
0.20 * Math.cos(4 * hp - 63 * Math.PI / 180);
|
||||
|
||||
var Sl = 1 + (0.015 * Lpm50) / Math.sqrt(20 + Lpm50);
|
||||
var Sc = 1 + 0.045 * Cp;
|
||||
var Sh = 1 + 0.015 * Cp * T;
|
||||
|
||||
var deltaTheta = 30 * Math.PI / 180 * Math.exp(-1 * Math.pow((180 / Math.PI * hp - 275) / 25, 2));
|
||||
var Rc = 2 * Math.sqrt(
|
||||
Math.pow(Cp, 7) / (Math.pow(Cp, 7) + Math.pow(25, 7))
|
||||
);
|
||||
|
||||
var Rt = -1 * Math.sin(2 * deltaTheta) * Rc;
|
||||
|
||||
return Math.sqrt(
|
||||
Math.pow(dL / (kL * Sl), 2) +
|
||||
Math.pow(dC / (kC * Sc), 2) +
|
||||
Math.pow(dH / (kH * Sh), 2) +
|
||||
Rt * dC / (kC * Sc) * dH / (kH * Sh)
|
||||
);
|
||||
}
|
||||
function rgb2lab(r, g, b) {
|
||||
// Convert to XYZ first via matrix transformation
|
||||
let x = 0.412453 * r + 0.357580 * g + 0.180423 * b;
|
||||
let y = 0.212671 * r + 0.715160 * g + 0.072169 * b;
|
||||
let z = 0.019334 * r + 0.119193 * g + 0.950227 * b;
|
||||
|
||||
let xFunc = CIELABconvF(x / referenceWhite.x);
|
||||
let yFunc = CIELABconvF(y / referenceWhite.y);
|
||||
let zFunc = CIELABconvF(z / referenceWhite.z);
|
||||
|
||||
let myL = 116 * yFunc - 16;
|
||||
let myA = 500 * (xFunc - yFunc);
|
||||
let myB = 200 * (yFunc - zFunc);
|
||||
|
||||
return {
|
||||
l: myL,
|
||||
a: myA,
|
||||
b: myB
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function CIELABconvF(value) {
|
||||
if (value > Math.pow(6 / 29, 3)) {
|
||||
return Math.cbrt(value);
|
||||
}
|
||||
return 1 / 3 * Math.pow(6 / 29, 2) * value + 4 / 29;
|
||||
}
|
||||
|
||||
function colorToRGB(color) {
|
||||
if(window.colorCache && window.colorCache[color]){
|
||||
return window.colorCache[color];
|
||||
}
|
||||
if (!window.cachedCtx) {
|
||||
window.cachedCtx = document.createElement("canvas").getContext("2d");
|
||||
window.colorCache = {};
|
||||
}
|
||||
let ctx = window.cachedCtx;
|
||||
ctx.fillStyle = color;
|
||||
return hexToRgb(ctx.fillStyle);
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16)
|
||||
};
|
||||
}
|
||||
}
|
1
js/data/LOSPEC_PALETTES.js
Normal file
@ -3,7 +3,5 @@ const MAX_Z_INDEX = 5000;
|
||||
|
||||
// Index of the first layer the user can use in the layers array
|
||||
const firstUserLayerIndex = 2;
|
||||
// Number of layers that are only used by the editor
|
||||
const nAppLayers = 3;
|
||||
|
||||
const MIN_ZOOM_LEVEL = 0.5;
|
@ -18,7 +18,7 @@ class Checkerboard extends Layer {
|
||||
|
||||
initialize() {
|
||||
super.initialize();
|
||||
console.log("Square size: " + this.checkerBoardSquareSize);
|
||||
////console.log("Square size: " + this.checkerBoardSquareSize);
|
||||
this.currentColor = this.firstCheckerBoardColor;
|
||||
this.fillCheckerboard();
|
||||
}
|
||||
|
@ -8,7 +8,6 @@
|
||||
*/
|
||||
class Layer {
|
||||
static layerCount = 1;
|
||||
static maxZIndex = 3;
|
||||
|
||||
static unusedIDs = [];
|
||||
static currentID = 1;
|
||||
@ -18,7 +17,7 @@ class Layer {
|
||||
// TODO: this is simply terrible. menuEntry can either be an HTML element, a string or undefined.
|
||||
// If it's an HTML element it is added, if it's a string nothing happens, if it's undefined the
|
||||
// first menuEntry is used (the one that appears on load)
|
||||
constructor(width, height, canvas, menuEntry) {
|
||||
constructor(width, height, canvas, menuEntry, id) {
|
||||
// REFACTOR: the canvas should actually be a Canvas instance
|
||||
this.canvas = Util.getElement(canvas);
|
||||
this.canvas.width = width;
|
||||
@ -37,18 +36,24 @@ class Layer {
|
||||
else if (menuEntry !== undefined)
|
||||
this.menuEntry = menuEntry;
|
||||
|
||||
let id = Layer.unusedIDs.pop();
|
||||
let hadId = false;
|
||||
if(typeof id !== "undefined"){
|
||||
hadId = true;
|
||||
} else {
|
||||
id = Layer.unusedIDs.pop();
|
||||
}
|
||||
|
||||
if (id == null) {
|
||||
id = Layer.currentID;
|
||||
Layer.currentID++;
|
||||
}
|
||||
|
||||
this.id = "layer" + id;
|
||||
this.id = hadId ? id : "layer" + id;
|
||||
|
||||
// Binding the events
|
||||
if (this.menuEntry !== undefined) {
|
||||
this.name = this.menuEntry.getElementsByTagName("p")[0].innerHTML;
|
||||
|
||||
this.menuEntry.id = "layer" + id;
|
||||
|
||||
this.menuEntry.onmouseover = () => this.hover();
|
||||
@ -69,16 +74,58 @@ class Layer {
|
||||
this.menuEntry.getElementsByTagName("canvas")[0].getContext('2d').imageSmoothingEnabled = false;
|
||||
}
|
||||
|
||||
if(hadId){
|
||||
this.menuEntry.classList.remove("layers-menu-entry");
|
||||
} else {
|
||||
if(this.menuEntry)this.menuEntry.classList.add("layers-menu-entry");
|
||||
}
|
||||
|
||||
this.initialize();
|
||||
|
||||
}
|
||||
|
||||
hasCanvas() {
|
||||
return this.menuEntry != null;
|
||||
}
|
||||
|
||||
tryDelete() {
|
||||
delete(layerIndex) {
|
||||
//console.log('layerIndex === ',layerIndex);
|
||||
let toDelete = currFile.layers[layerIndex];
|
||||
let previousSibling;
|
||||
if(toDelete){
|
||||
//console.log('toDelete === ',toDelete);
|
||||
previousSibling = toDelete.menuEntry.previousElementSibling;
|
||||
//console.log('previousSibling === ',previousSibling);
|
||||
// Adding the ids to the unused ones
|
||||
// Deleting canvas and entry
|
||||
toDelete.canvas.remove();
|
||||
toDelete.menuEntry.remove();
|
||||
}
|
||||
|
||||
Layer.unusedIDs.push(this.id);
|
||||
|
||||
if(this.isSelected) {
|
||||
// Selecting the nearest layer
|
||||
const nearestLayer = (currFile.layers[layerIndex + 1] ?? currFile.layers[layerIndex - 1]);
|
||||
if(nearestLayer){
|
||||
nearestLayer.selectLayer();
|
||||
//console.log('changing to nearest layer');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Removing the layer from the list
|
||||
currFile.layers.splice(layerIndex, 1);
|
||||
|
||||
if(toDelete){
|
||||
new HistoryState().DeleteLayer(toDelete, previousSibling, layerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
tryDelete() { //TODO: quote yoda
|
||||
if (Input.getLastTarget() != this.menuEntry && Input.getLastTarget().parentElement != this.menuEntry)
|
||||
return;
|
||||
|
||||
LayerList.deleteLayer();
|
||||
}
|
||||
|
||||
@ -121,7 +168,7 @@ class Layer {
|
||||
|
||||
hover() {
|
||||
// Hides all the layers but the current one
|
||||
for (let i=1; i<currFile.layers.length - nAppLayers; i++) {
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i] !== this) {
|
||||
currFile.layers[i].canvas.style.opacity = 0.3;
|
||||
}
|
||||
@ -130,7 +177,7 @@ class Layer {
|
||||
|
||||
unhover() {
|
||||
// Shows all the layers again
|
||||
for (let i=1; i<currFile.layers.length - nAppLayers; i++) {
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if (currFile.layers[i] !== this) {
|
||||
currFile.layers[i].canvas.style.opacity = 1;
|
||||
}
|
||||
@ -187,6 +234,8 @@ class Layer {
|
||||
}
|
||||
|
||||
selectLayer(hideOptions = true) {
|
||||
//console.log('called selectLayer');
|
||||
////console.trace();
|
||||
if (hideOptions)
|
||||
LayerList.closeOptionsMenu();
|
||||
// Deselecting the old layer
|
||||
@ -196,6 +245,12 @@ class Layer {
|
||||
this.isSelected = true;
|
||||
this.menuEntry.classList.add("selected-layer");
|
||||
currFile.currentLayer = this;
|
||||
|
||||
if(currFile.VFXLayer) { // only refresh z after init
|
||||
LayerList.refreshZ();
|
||||
}
|
||||
|
||||
if(FileManager.cacheEnabled)FileManager.localStorageSave();
|
||||
}
|
||||
|
||||
toggleLock() {
|
||||
|
@ -127,7 +127,7 @@
|
||||
decodedKey = decodeURIComponent(key);
|
||||
} catch (e) {
|
||||
if (console && typeof console.error === 'function') {
|
||||
console.error('Could not decode cookie with key "' + key + '"', e);
|
||||
//console.error('Could not decode cookie with key "' + key + '"', e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ if (!window.jscolor) { window.jscolor = (function () {
|
||||
|
||||
|
||||
init : function () {
|
||||
//console.log('init()')
|
||||
//////console.log('init()')
|
||||
if (jsc.jscolor.lookupClass) {
|
||||
jsc.jscolor.installByClassName(jsc.jscolor.lookupClass);
|
||||
}
|
||||
@ -533,7 +533,7 @@ if (!window.jscolor) { window.jscolor = (function () {
|
||||
|
||||
|
||||
onDocumentMouseDown : function (e) {
|
||||
//console.log(e)
|
||||
//////console.log(e)
|
||||
|
||||
if (!e) { e = window.event; }
|
||||
var target = e.target || e.srcElement;
|
||||
@ -547,7 +547,7 @@ if (!window.jscolor) { window.jscolor = (function () {
|
||||
} else {
|
||||
// Mouse is outside the picker controls -> hide the color picker!
|
||||
if (jsc.picker && jsc.picker.owner) {
|
||||
//console.log(e.target,'=====================================')
|
||||
//////console.log(e.target,'=====================================')
|
||||
//if they clicked on the delete button [lospec]
|
||||
if (e.target.className == 'delete-color-button') {
|
||||
new HistoryState().DeleteColor(jsc.picker.owner.toString());
|
||||
@ -555,13 +555,13 @@ if (!window.jscolor) { window.jscolor = (function () {
|
||||
ColorModule.deleteColor(jsc.picker.owner.styleElement);
|
||||
}
|
||||
else if (e.target.className == 'jscolor-picker-bottom') {
|
||||
//console.log('clicked color picker bottom')
|
||||
//////console.log('clicked color picker bottom')
|
||||
}
|
||||
else if (e.target.parentElement.classList.contains('jscolor-picker-bottom')) {
|
||||
//console.log('clicked element in color picker bottom')
|
||||
//////console.log('clicked element in color picker bottom')
|
||||
}
|
||||
else {
|
||||
//console.log('clicked outside of color picker')
|
||||
//////console.log('clicked outside of color picker')
|
||||
//unhide hidden edit button [lospec]
|
||||
var hiddenButton = document.querySelector('.color-edit-button.hidden');
|
||||
if (hiddenButton) hiddenButton.classList.remove('hidden');
|
||||
@ -1068,7 +1068,7 @@ if (!window.jscolor) { window.jscolor = (function () {
|
||||
|
||||
|
||||
this.hide = function () {
|
||||
///console.log(this.styleElement)
|
||||
///////console.log(this.styleElement)
|
||||
if (isPickerOwner()) {
|
||||
//set the color to old color, in case the color is a duplicate that hasn't been resolved yet [lospec]
|
||||
var hexInput = document.getElementById('jscolor-hex-input');
|
||||
@ -1164,7 +1164,7 @@ if (!window.jscolor) { window.jscolor = (function () {
|
||||
if (this.hash) { value = '#' + value; }
|
||||
|
||||
if (jsc.isElementType(this.valueElement, 'input')) {
|
||||
//console.log('SETTING VALUE')
|
||||
//////console.log('SETTING VALUE')
|
||||
//this sets the value element's value
|
||||
this.valueElement.value = value;
|
||||
} else {
|
||||
@ -1439,7 +1439,7 @@ function detachPicker () {
|
||||
|
||||
function drawPicker () {
|
||||
|
||||
//console.log('drawPicker ()')
|
||||
//////console.log('drawPicker ()')
|
||||
// At this point, when drawing the picker, we know what the parent elements are
|
||||
// and we can do all related DOM operations, such as registering events on them
|
||||
// or checking their positioning
|
||||
@ -1811,7 +1811,7 @@ function isPickerOwner () {
|
||||
|
||||
|
||||
function blurValue () {
|
||||
//console.log('blurValue()')
|
||||
//////console.log('blurValue()')
|
||||
THIS.importColor();
|
||||
}
|
||||
|
||||
@ -1842,7 +1842,7 @@ this.valueElement = jsc.fetchElement(this.valueElement);
|
||||
// Find the style element
|
||||
this.styleElement = jsc.fetchElement(this.styleElement);
|
||||
|
||||
//console.log('VALUE ELEMENT: ', this.valueElement)
|
||||
//////console.log('VALUE ELEMENT: ', this.valueElement)
|
||||
|
||||
var THIS = this;
|
||||
var container =
|
||||
@ -1894,7 +1894,7 @@ do {
|
||||
|
||||
if (jsc.isElementType(this.valueElement, 'input')) {
|
||||
var updateField = function () {
|
||||
//console.log('updateField()')
|
||||
//////console.log('updateField()')
|
||||
THIS.fromString(THIS.valueElement.value, jsc.leaveValue);
|
||||
jsc.dispatchFineChange(THIS);
|
||||
};
|
||||
|
@ -5,7 +5,9 @@
|
||||
|
||||
//=include data/consts.js
|
||||
//=include data/palettes.js
|
||||
|
||||
// str.split(`//=include `).slice(1).map(n=>{
|
||||
// return `<script src="${jsPath}/${n.split('\n')[0]}"></script>`;
|
||||
// });
|
||||
/** UTILITY AND INPUT **/
|
||||
//=include Util.js
|
||||
//=include Events.js
|
||||
@ -77,59 +79,83 @@ window.onload = function () {
|
||||
ToolManager.currentTool().updateCursor();
|
||||
// Apply checkboxes
|
||||
|
||||
|
||||
//check if there are any url parameters
|
||||
if (window.location.pathname.replace('/pixel-editor/','').length <= 1) {
|
||||
//show splash screen
|
||||
Dialogue.showDialogue('splash', false);
|
||||
}
|
||||
//url parameters were specified
|
||||
else {
|
||||
let args = window.location.pathname.split('/');
|
||||
let paletteSlug = args[2];
|
||||
let dimentions = args[3];
|
||||
////console.log('window.location.pathname === ',window.location.pathname);
|
||||
|
||||
//fetch palette via lospec palette API
|
||||
fetch('https://lospec.com/palette-list/'+paletteSlug+'.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
//palette loaded successfully
|
||||
palettes[paletteSlug] = data;
|
||||
palettes[paletteSlug].specified = true;
|
||||
////console.log('window.location === ',window.location);
|
||||
let args = window.location.pathname.split('/');
|
||||
let paletteSlug = args[2];
|
||||
let dimensions = args[3];
|
||||
// let prefillWidth = args[4] ?? 9; // TODO
|
||||
// let prefill = args[5] ?? "110101111110100110111100110110101111";
|
||||
// let customColors = args[6] ?? ""; // ex: "#ffffff,#000000"
|
||||
// console.log('prefill === ',prefill);
|
||||
if(paletteSlug && dimensions) {
|
||||
|
||||
//refresh list of palettes
|
||||
document.getElementById('palette-menu-splash').refresh();
|
||||
|
||||
//if the dimentions were specified
|
||||
if (dimentions && dimentions.length >= 3 && dimentions.includes('x')) {
|
||||
let width = dimentions.split('x')[0];
|
||||
let height = dimentions.split('x')[1];
|
||||
|
||||
//create new document
|
||||
Startup.newPixel(width, height);
|
||||
}
|
||||
//dimentions were not specified -- show splash screen with palette preselected
|
||||
else {
|
||||
//show splash
|
||||
Dialogue.showDialogue('new-pixel', false);
|
||||
}
|
||||
})
|
||||
//error fetching url (either palette doesn't exist, or lospec is down)
|
||||
.catch((error) => {
|
||||
console.warn('failed to load palette "'+paletteSlug+'"', error);
|
||||
//proceed to splash screen
|
||||
Dialogue.showDialogue('splash', false);
|
||||
});
|
||||
}
|
||||
};
|
||||
//fetch palette via lospec palette API
|
||||
fetch('https://lospec.com/palette-list/'+paletteSlug+'.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
//palette loaded successfully
|
||||
palettes[paletteSlug] = data;
|
||||
palettes[paletteSlug].specified = true;
|
||||
////console.log('palettes[paletteSlug] === ',palettes[paletteSlug]);
|
||||
//refresh list of palettes
|
||||
document.getElementById('palette-menu-splash').refresh();
|
||||
console.log('paletteSlug === ',paletteSlug);
|
||||
console.log('dimensions === ',dimensions);
|
||||
//if the dimensions were specified
|
||||
if (dimensions && dimensions.length >= 3 && dimensions.includes('x')) {
|
||||
let width = dimensions.split('x')[0];
|
||||
let height = dimensions.split('x')[1];
|
||||
const layers = [];
|
||||
let selectedLayer = 0;
|
||||
// if(prefill && prefillWidth){ // TODO
|
||||
// layers.push({
|
||||
// id: "layer0",
|
||||
// name: "Layer 0",
|
||||
// prefillWidth,
|
||||
// prefill
|
||||
// });
|
||||
// selectedLayer = 0;
|
||||
// }
|
||||
let _lpe = FileManager.defaultLPE(width, height, (data.colors ?? []).map(n=>"#"+n));
|
||||
console.log('_lpe === ',_lpe);
|
||||
Startup.newPixel(_lpe);
|
||||
}
|
||||
//dimensions were not specified -- show splash screen with palette preselected
|
||||
else {
|
||||
//show splash
|
||||
Dialogue.showDialogue('new-pixel', false);
|
||||
}
|
||||
})
|
||||
//error fetching url (either palette doesn't exist, or lospec is down)
|
||||
.catch((error) => {
|
||||
//console.warn('failed to load palette "'+paletteSlug+'"', error);
|
||||
//proceed to splash screen
|
||||
Dialogue.showDialogue('splash', false);
|
||||
});
|
||||
} else {
|
||||
if(FileManager.cacheEnabled && FileManager.localStorageCheck()) {
|
||||
//load cached document
|
||||
const lpe = FileManager.localStorageLoad();
|
||||
|
||||
Startup.newPixel(lpe);
|
||||
}
|
||||
//check if there are any url parameters
|
||||
else if (window.location.pathname.replace('/pixel-editor/','').length <= 1) {
|
||||
//show splash screen
|
||||
Dialogue.showDialogue('splash', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//prevent user from leaving page with unsaved data
|
||||
window.onbeforeunload = function() {
|
||||
if (EditorState.documentCreated)
|
||||
return 'You will lose your pixel if it\'s not saved!';
|
||||
// window.onbeforeunload = function() {
|
||||
// if (EditorState.documentCreated)
|
||||
// return 'You will lose your pixel if it\'s not saved!';
|
||||
|
||||
else return;
|
||||
};
|
||||
// else return;
|
||||
// };
|
||||
|
||||
// Compatibility functions
|
||||
function closeCompatibilityWarning() {
|
||||
|
@ -45,11 +45,11 @@ class EllipseTool extends ResizableTool {
|
||||
}
|
||||
|
||||
onStart(mousePos, mouseTarget) {
|
||||
super.onStart(mousePos);
|
||||
|
||||
if (mouseTarget.className != "drawingCanvas")
|
||||
if (mouseTarget.className != "drawingCanvas") {
|
||||
return;
|
||||
}
|
||||
|
||||
super.onStart(mousePos);
|
||||
// Putting the tmp layer on top of everything
|
||||
currFile.TMPLayer.canvas.style.zIndex = parseInt(currFile.currentLayer.canvas.style.zIndex, 10) + 1;
|
||||
|
||||
@ -65,7 +65,7 @@ class EllipseTool extends ResizableTool {
|
||||
currFile.TMPLayer.context);
|
||||
}
|
||||
|
||||
/** Finishes drawing the rect, decides the end coordinates and moves the preview rectangle to the
|
||||
/** Finishes drawing the ellipse, decides the end coordinates and moves the preview ellipse to the
|
||||
* current layer
|
||||
*
|
||||
* @param {*} mousePos The position of the mouse when the user stopped dragging
|
||||
|
@ -86,7 +86,9 @@ class EyeDropperTool extends Tool {
|
||||
// Returned colour
|
||||
let selectedColor;
|
||||
|
||||
for (let i=1; i<currFile.layers.length-3; i++) {
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
if(!currFile.layers[i].isVisible)continue;
|
||||
|
||||
// Getting the colour of the pixel in the cursorLocation
|
||||
tmpColour = currFile.layers[i].context.getImageData(Math.floor(cursorLocation[0]/currFile.zoom),Math.floor(cursorLocation[1]/currFile.zoom),1,1).data;
|
||||
|
||||
|
@ -25,7 +25,7 @@ class FillTool extends DrawingTool {
|
||||
static fill(cursorLocation, context) {
|
||||
//changes a pixels color
|
||||
function colorPixel(tempImage, pixelPos, fillColor) {
|
||||
//console.log('colorPixel:',pixelPos);
|
||||
//////console.log('colorPixel:',pixelPos);
|
||||
tempImage.data[pixelPos] = fillColor.r;
|
||||
tempImage.data[pixelPos + 1] = fillColor.g;
|
||||
tempImage.data[pixelPos + 2] = fillColor.b;
|
||||
@ -34,13 +34,13 @@ class FillTool extends DrawingTool {
|
||||
|
||||
//change x y to color value passed from the function and use that as the original color
|
||||
function matchStartColor(tempImage, pixelPos, color) {
|
||||
//console.log('matchPixel:',x,y)
|
||||
//////console.log('matchPixel:',x,y)
|
||||
|
||||
let r = tempImage.data[pixelPos];
|
||||
let g = tempImage.data[pixelPos + 1];
|
||||
let b = tempImage.data[pixelPos + 2];
|
||||
let a = tempImage.data[pixelPos + 3];
|
||||
//console.log(r == color[0] && g == color[1] && b == color[2]);
|
||||
//////console.log(r == color[0] && g == color[1] && b == color[2]);
|
||||
return (r == color[0] && g == color[1] && b == color[2] && a == color[3]);
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ class FillTool extends DrawingTool {
|
||||
|
||||
//this is an array that holds all of the pixels at the top of the cluster
|
||||
let topmostPixelsArray = [[Math.floor(cursorLocation[0]/currFile.zoom), Math.floor(cursorLocation[1]/currFile.zoom)]];
|
||||
//console.log('topmostPixelsArray:',topmostPixelsArray)
|
||||
//////console.log('topmostPixelsArray:',topmostPixelsArray)
|
||||
|
||||
//the offset of the pixel in the temp image data to start with
|
||||
let startingPosition = (topmostPixelsArray[0][1] * currFile.canvasSize[0] + topmostPixelsArray[0][0]) * 4;
|
||||
|
@ -22,7 +22,7 @@ class MagicWandTool extends SelectionTool {
|
||||
!Util.cursorInCanvas(currFile.canvasSize, [mousePos[0]/currFile.zoom, mousePos[1]/currFile.zoom]))
|
||||
return;
|
||||
|
||||
|
||||
////console.log('this.moveTool === ',this.moveTool);
|
||||
this.switchFunc(this.moveTool);
|
||||
this.moveTool.setSelectionData(this.getSelection(), this);
|
||||
}
|
||||
@ -52,8 +52,10 @@ class MagicWandTool extends SelectionTool {
|
||||
this.outlineData = new ImageData(currFile.canvasSize[0], currFile.canvasSize[1]);
|
||||
this.previewData = selectedData;
|
||||
this.drawSelectedArea();
|
||||
this.boundingBoxCenter = [this.boundingBox.minX + (this.boundingBox.maxX - this.boundingBox.minX) / 2,
|
||||
this.boundingBox.minY + (this.boundingBox.maxY - this.boundingBox.minY) / 2];
|
||||
this.boundingBoxCenter = [
|
||||
this.boundingBox.minX + (this.boundingBox.maxX - this.boundingBox.minX) / 2,
|
||||
this.boundingBox.minY + (this.boundingBox.maxY - this.boundingBox.minY) / 2
|
||||
];
|
||||
|
||||
// Cut the selection
|
||||
this.cutSelection();
|
||||
@ -61,7 +63,7 @@ class MagicWandTool extends SelectionTool {
|
||||
currFile.TMPLayer.context.putImageData(this.previewData, 0, 0);
|
||||
|
||||
// Draw the bounding box
|
||||
this.drawBoundingBox();
|
||||
this.drawBoundingBox(1, 1);
|
||||
|
||||
return selectedData;
|
||||
}
|
||||
|
@ -31,6 +31,9 @@ class PanTool extends Tool {
|
||||
for (let i=1; i<currFile.layers.length; i++) {
|
||||
currFile.layers[i].copyData(currFile.layers[0]);
|
||||
}
|
||||
for (let i=0; i<currFile.sublayers.length; i++) {
|
||||
currFile.sublayers[i].copyData(currFile.layers[0]);
|
||||
}
|
||||
}
|
||||
|
||||
onEnd(mousePos, target) {
|
||||
|
@ -44,8 +44,10 @@ class SelectionTool extends Tool {
|
||||
this.currSelection = {};
|
||||
this.moveOffset = [0, 0];
|
||||
|
||||
this.updateBoundingBox(Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1),
|
||||
Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1));
|
||||
this.updateBoundingBox(
|
||||
Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1),
|
||||
Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1)
|
||||
);
|
||||
}
|
||||
|
||||
onDrag(mousePos) {
|
||||
@ -66,8 +68,10 @@ class SelectionTool extends Tool {
|
||||
|
||||
|
||||
if (Util.cursorInCanvas(currFile.canvasSize, [mousePos[0]/currFile.zoom, mousePos[1]/currFile.zoom])) {
|
||||
this.updateBoundingBox(Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1),
|
||||
Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1));
|
||||
this.updateBoundingBox(
|
||||
Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1),
|
||||
Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,8 +85,10 @@ class SelectionTool extends Tool {
|
||||
let mouseY = mousePos[1] / currFile.zoom;
|
||||
|
||||
if (Util.cursorInCanvas(currFile.canvasSize, [mousePos[0]/currFile.zoom, mousePos[1]/currFile.zoom])) {
|
||||
this.updateBoundingBox(Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1),
|
||||
Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1));
|
||||
this.updateBoundingBox(
|
||||
Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1),
|
||||
Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1)
|
||||
);
|
||||
}
|
||||
|
||||
this.boundingBoxCenter = [this.boundingBox.minX + (this.boundingBox.maxX - this.boundingBox.minX) / 2,
|
||||
@ -211,6 +217,7 @@ class SelectionTool extends Tool {
|
||||
}
|
||||
}
|
||||
}
|
||||
////console.log('this.currSelection === ',this.currSelection);
|
||||
|
||||
// Save the selection outline
|
||||
this.outlineData = currFile.VFXLayer.context.getImageData(this.boundingBox.minX,
|
||||
@ -246,16 +253,25 @@ class SelectionTool extends Tool {
|
||||
this.boundingBox.minY + this.moveOffset[1]);
|
||||
}
|
||||
|
||||
drawBoundingBox() {
|
||||
drawBoundingBox(xo = 0, yo = 0) {
|
||||
currFile.VFXLayer.context.fillStyle = "red";
|
||||
currFile.VFXLayer.context.fillRect(this.boundingBox.minX + this.moveOffset[0],
|
||||
this.boundingBox.minY + this.moveOffset[1], 1, 1);
|
||||
currFile.VFXLayer.context.fillRect(this.boundingBox.minX+ this.moveOffset[0],
|
||||
this.boundingBox.maxY + this.moveOffset[1], 1, 1);
|
||||
currFile.VFXLayer.context.fillRect(this.boundingBox.maxX+ this.moveOffset[0],
|
||||
this.boundingBox.minY + this.moveOffset[1], 1, 1);
|
||||
currFile.VFXLayer.context.fillRect(this.boundingBox.maxX+ this.moveOffset[0],
|
||||
this.boundingBox.maxY + this.moveOffset[1], 1, 1);
|
||||
|
||||
currFile.VFXLayer.context.fillRect(
|
||||
this.boundingBox.minX + this.moveOffset[0] - xo,
|
||||
this.boundingBox.minY + this.moveOffset[1] - yo,
|
||||
1, 1);
|
||||
currFile.VFXLayer.context.fillRect(
|
||||
this.boundingBox.minX + this.moveOffset[0] - xo,
|
||||
this.boundingBox.maxY + this.moveOffset[1] + yo,
|
||||
1, 1);
|
||||
currFile.VFXLayer.context.fillRect(
|
||||
this.boundingBox.maxX + this.moveOffset[0] + xo,
|
||||
this.boundingBox.minY + this.moveOffset[1] - yo,
|
||||
1, 1);
|
||||
currFile.VFXLayer.context.fillRect(
|
||||
this.boundingBox.maxX + this.moveOffset[0] + xo,
|
||||
this.boundingBox.maxY + this.moveOffset[1] + yo,
|
||||
1, 1);
|
||||
}
|
||||
|
||||
isBorderOfBox(pixel) {
|
||||
|
@ -75,8 +75,11 @@ class ZoomTool extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
for (let i=1; i<currFile.layers.length; i++) {
|
||||
for (let i=0; i<currFile.layers.length; i++) {
|
||||
currFile.layers[i].copyData(currFile.layers[0]);
|
||||
}
|
||||
for (let i=0; i<currFile.sublayers.length; i++) {
|
||||
currFile.sublayers[i].copyData(currFile.layers[0]);
|
||||
}
|
||||
}
|
||||
}
|
53
old_lpe1.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"canvasWidth": 128,
|
||||
"canvasHeight": 128,
|
||||
"editorMode": "Advanced",
|
||||
"color0": "#798040",
|
||||
"color1": "#33ff69",
|
||||
"color2": "#2f748a",
|
||||
"nLayers": 7,
|
||||
"layer4": {
|
||||
"canvas": {},
|
||||
"context": {
|
||||
"mozImageSmoothingEnabled": false
|
||||
},
|
||||
"isSelected": false,
|
||||
"isVisible": true,
|
||||
"isLocked": false,
|
||||
"oldLayerName": null,
|
||||
"menuEntry": {},
|
||||
"id": "layer1",
|
||||
"name": "Layer 0"
|
||||
},
|
||||
"layer4ImageData": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAABCRJREFUeF7tmrFx3DAQRXXlOFYDjtyLS3BkOXIJ7sWRG1DscuShZjADcwgSBD4ofO5TouCIvd3 / H3ZB8h5P / IVW4BG6eop / AoDgEAAAAARXIHj5dAAACK5A8PLpAAAQXIHg5dMBACC4AsHLpwMAQHAFgpdPBwCA4AoEL58OAADBFQhePh0AAIIrELx8OgAABFcgePl0AAAIrkDw8ukAABBcgeDl0wEAILgCwcunAwBAcAWCl08HAIDgCgQvnw4AAPEU + Pby + W2v6t / fP21 + /Pr4dbsNc7uCjnDeMz8Z/ + XH380wP1 / +3E6 vqQp6fvv6vjNH7bSS + UfGJxoA4Gh7dX4 + CoBe4wGg09ja5SMAWJufz / dSqy / lSweodbLxOjUAufm1bX4vdQBoNLZm2WL + MvvT / 5 o1e9ck8xXGMwJ63ahYrwYgdZOzbZ4OUGHWiEuUACyxlMbTAUY4voqpAmCU + Uu6nAEGgtALwIiWvy4XAAYBkB / 8 Wg6Bac3RI97e9AGgV8HC + h4ArjKfETDI / CVsS / vPnxmM3vkcAgea3wLAekxcBUCrDDOPjileBp3pAG7mzz46rABwNH / 28 TENAItQpdfApXcEs7f + fGTMOgamB6B0W + hk / sxjYBoAtnb / 3 jMBAGg9kv6 / bloAjh4IAcBNANia70fmL6UDwE0BqDFfAYDydwI1VnAI3FBpff9fa34PAFcbz23gzvbIAdi7DdwKcXYEfJTxAHDwAqj1d4C1AHy08QCwA0Da9Wdafwp3BMAsxgNAYfbnLV8JwGzGA8DO4S991AtAz2 / 9 a07wimu4C6g4DJ4ROp0dljUjfgB6JpeaawFAAICb6XnJANAIQG56 / r7g6BBYsyuvvAYADtTOjc4vLb0iBgANvlO8DGop5WoA0g7u + d4ZuwAAFOgrmQUALdv1RmsA4EZmtpTSA8D6 + 2 YYCbYjoMU8xRoAUKhoHAMAjM1TpA4AChWNYwCAsXmK1AFAoaJxDAAwNk + ROgAoVDSOAQDG5ilSBwCFiuYxVBDwJNAUBAAwNU6VNgColDSNAwCmxinT7oVghvm / 6 MHbwA4qeiAAgA7hWapTgA6g09IyEgBY2qZLGgB0WlpGAgBL23RJA4BOS8tIAGBpmy5pANBpaRkJACxt0yUNADotLSMBgKVtuqQBQKelZSQAsLRNlzQA6LS0jAQAlrbpkgYAnZaWkQDA0jZd0gCg09IyEgBY2qZLGgB0WlpGAgBL23RJA4BOS8tIAGBpmy5pANBpaRkJACxt0yUNADotLSMBgKVtuqQBQKelZSQAsLRNlzQA6LS0jAQAlrbpkgYAnZaWkQDA0jZd0gCg09IyEgBY2qZLGgB0WlpGAgBL23RJA4BOS8tIAGBpmy5pANBpaRkJACxt0yUNADotLSMBgKVtuqQBQKelZaR / PIgEn3reoTUAAAAASUVORK5CYII = ",
|
||||
"layer5": {
|
||||
"canvas": {},
|
||||
"context": {
|
||||
"mozImageSmoothingEnabled": false
|
||||
},
|
||||
"isSelected": false,
|
||||
"isVisible": true,
|
||||
"isLocked": false,
|
||||
"oldLayerName": null,
|
||||
"menuEntry": {},
|
||||
"id": "layer6",
|
||||
"name": "Layer 1"
|
||||
|
||||
},
|
||||
"layer5ImageData": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAA7ZJREFUeF7tnFFyo0AQQ + Mz7an2RDnVnilbqYqrHMo2aNBgLL380gMj6XUDdpLLBz / VDlyq1SP + AwDKIQAAACh3oFw + EwAAyh0ol88EAIByB8rlMwEAoNyBcvlMAAAod6BcPhMAAModKJfPBACAcgfK5TMBAKDcgXL5TAAAKHegXD4TAADKHSiXzwQAgHIHyuUzAQCg3IFy + UwAACh3oFw + EwAAyh0ol88EAIByB8rlMwEAoNyBcvlMAAAod6BcPhMAAModKJfPBPgB4M / X3y + FhX + XzwjvIkQowS1r1eCv6wFgj + snWTsa / vf2AeAkIY5uY0 / 4 ADDq + knW7Q0fAE4S5Mg2HOEDwIjzJ1jjCh8AThCmugVn + G4ARvfmeBCteA0cNfgZZHvNd + xp7x6 + 9 QGAOkp + 6 lXzHYEvt6ru4Z7UeABmGL / 1 FjDr2s4Po6IBmBnAo + 6 beU0mgDiuZ4WxDH / Wddbkcgt44tCMUK6Gzzj3Wtj3jgPAA9fOEtBIqMoaALjjVkv4Wx9E14CKewgEgLXIfx + PAqApfCbAAvS28AEAACy / lBJzC2ACaPf + azUAjPn28lWOV8CoL4PaJgAAlD8DAEAxAK7wuQUccCef8cURAJz8I + DZXxUDwAkB2BKK40F1y3WUwcZroOLWTa0ahCN816d / t5IBQABADf321A4A9lz / kUwAeAKAy3BH + DO6n7eARfiuwJdMOQCYtbeYCSBM8kNLHeHP6v6oCXBoqsLFHADM6n4AEIIcKXWEP7P7AWAkVWGNA4CZ3Q8AQphqqSP82d0PAGqqQr0DgNndDwBCoEqpI / wjuh8AlFSFWgcAR3Q / AAihbi11hH9U9wPA1lSFOgcAR3U / AAjBbil1hH9k9wPAllSFGgcAR3Y / AAjhrpU6wj + 6 + wFgLVXhuAOAo7sfAISA10oBYM2h8ON7AXhF9zMBjFACgNHMdzzVHgBe1f1MACNpAGA08x1PNQrAK7ufCWAkDQCMZr7jqQDgHVMz7nkEgFePf24BANDx7 + KNOcedij8MiYtUEwQAml9x1QAQF6kmCAA0v + KqASAuUk0QAGh + xVUDQFykmiAA0PyKqwaAuEg1QQCg + RVXDQBxkWqCAEDzK64aAOIi1QQBgOZXXDUAxEWqCQIAza + 4 agCIi1QTBACaX3HVABAXqSYIADS / 4 qoBIC5STRAAaH7FVQNAXKSaIADQ / IqrBoC4SDVBAKD5FVcNAHGRaoIAQPMrrhoA4iLVBAGA5ldcNQDERaoJAgDNr7jq / yPDGJDPZGMlAAAAAElFTkSuQmCC ",
|
||||
"layer6": {
|
||||
"canvas": {},
|
||||
"context": {
|
||||
"mozImageSmoothingEnabled": false
|
||||
},
|
||||
"isSelected": true,
|
||||
"isVisible": true,
|
||||
"isLocked": false,
|
||||
"oldLayerName": null,
|
||||
"menuEntry": {},
|
||||
"id": "layer7",
|
||||
"name": "Layer 2"
|
||||
|
||||
},
|
||||
"layer6ImageData": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAA9VJREFUeF7tnFuO1TAQRGdWxOYQi0BsjhWBrsQVmSgPt92VVNxnfse4y1UnZQshPj / 4 Ke3AZ + nTc / gPACgOAQAAQHEHih + fBgCA4g4UPz4NAADFHSh + fBoAAIo7UPz4NAAAFHeg + PFpAAAo7kDx49MAAFDcgeLHpwEAoLgDxY9PAwBAcQeKH58GAIDiDhQ / fqkG + Pb9559o3r9 //Zjao6kPtw67B4DlHjPCUAaA0fC3mmMGIAAgeiccrH8iECUAUHz9R9w8CQQASGyA9VZPAGF6AK7++p/2VgAAYQNEGiEKala7AMCFALxG7QUHAIIgoqYKJOxuuQYhqpUGaEgramrDlqlLliFGtQJAQxRRUxu2TF/yDrJHawYEU78BekxNT1i4IQAcmDt7+EcPyghz0zYAALRhAAAbPo08ztpsz1nFFSC6AraMdWwUANgBYCSsM1NH9s757v/vcqa1Zd6UV8BISK2mjsxoCaZlTavWo70AYOVO1NQ7QYhq3QIBABau9Bp6FwS9epcgAEACAO8trgYBAJIfgRmGXglBht7pGmAkgAxDX0yOaGh5/L3XZOgFgH9uZpi5Dk8NQoZmABACoG4DANjoy96vLsPMvfru1XR2HWRopgHEDaBsAQBIaoAMI8++VkULZOimAQ7+oeZZqNHfZ0MAAA9qAMVfFgEAAAw3+PAG0RpUr++p2YwvKXKuHo1b+2foBoAL3wDLEEchyAj/pac8AFlGRhog4z2QpRsAbv4vYHqaICt8GuCm+u9pC9WfoQFubgBVsK37AgAAtLLyjHXROzXzPn2GQ19V0gA0wBO53ddMA8TypAFogBgx7qtpgFhCNAANECPGfTUNEEuIBqABYsS4r6YBYgnRADRAjBj31TRALCEagAaIEeO+mgaIJUQD0AAxYtxX0wCxhGgAGiBGDKvncmC6BpgrHv1pAEDvsfUEALCORy8OAPQeW08AAOt49OIAQO+x9QQAsI5HLw4A9B5bTwAA63j04gBA77H1BACwjkcvDgD0HltPAADrePTiAEDvsfUEALCORy8OAPQeW08AAOt49OIAQO+x9QQAsI5HLw4A9B5bTwAA63j04gBA77H1BACwjkcvDgD0HltPAADrePTiAEDvsfUEALCORy8OAPQeW08AAOt49OIAQO+x9QQAsI5HLw4A9B5bTwAA63j04gBA77H1BACwjkcvDgD0HltPAADrePTiAEDvsfUEALCORy8OAPQeW08AAOt49OIAQO+x9QQAsI5HLw4A9B5bTwAA63j04gBA77H1BACwjkcvDgD0HltPAADrePTi/gKPB0SQMNaI5gAAAABJRU5ErkJggg=="
|
||||
}
|
35
package-lock.json
generated
@ -21,7 +21,7 @@
|
||||
"nodemon": "^2.0.7",
|
||||
"open": "^8.0.6",
|
||||
"open-cli": "^6.0.1",
|
||||
"sass": "^1.17.3"
|
||||
"sass": "^1.49.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
@ -6069,18 +6069,19 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.44.0.tgz",
|
||||
"integrity": "sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw==",
|
||||
"version": "1.49.7",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.49.7.tgz",
|
||||
"integrity": "sha512-13dml55EMIR2rS4d/RDHHP0sXMY3+30e1TKsyXaSz3iLWVoDWEoboY8WzJd5JMnxrRHffKO3wq2mpJ0jxRJiEQ==",
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0"
|
||||
"immutable": "^4.0.0",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"sass": "sass.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9.0"
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sass/node_modules/anymatch": {
|
||||
@ -6523,6 +6524,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-resolve": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
|
||||
@ -12614,12 +12623,13 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.44.0.tgz",
|
||||
"integrity": "sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw==",
|
||||
"version": "1.49.7",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.49.7.tgz",
|
||||
"integrity": "sha512-13dml55EMIR2rS4d/RDHHP0sXMY3+30e1TKsyXaSz3iLWVoDWEoboY8WzJd5JMnxrRHffKO3wq2mpJ0jxRJiEQ==",
|
||||
"requires": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0"
|
||||
"immutable": "^4.0.0",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"anymatch": {
|
||||
@ -12959,6 +12969,11 @@
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
|
||||
},
|
||||
"source-map-resolve": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
|
||||
|
@ -28,7 +28,7 @@
|
||||
"nodemon": "^2.0.7",
|
||||
"open": "^8.0.6",
|
||||
"open-cli": "^6.0.1",
|
||||
"sass": "^1.17.3"
|
||||
"sass": "^1.49.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
|
73
poc_pages/pixelate_font.html
Normal file
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<img src="/images/test_8x8.png" id="test_image" style="display:none;">
|
||||
<script src="/js/canvas_util.js"></script>
|
||||
<script src="/js/color_utils.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.40.2/codemirror.min.js"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.40.2/codemirror.css" rel="stylesheet" />
|
||||
<div id="target" style="position:fixed;top:0;left:0;right:50%;bottom:0;">
|
||||
<textarea id="editorTextArea">
|
||||
const word = Math.random() > 0.5 ? "Hello" : "Goodbye cruel";
|
||||
alert(`${word} world`);
|
||||
</textarea>
|
||||
</div>
|
||||
<div id="preview_div" style="position:fixed;top:0;left:50%;right:0;bottom:90%;">
|
||||
|
||||
</div>
|
||||
<div id="debug_div" style="position:fixed;top:10%;left:50%;right:0;bottom:0;overflow:scroll;">
|
||||
|
||||
</div>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script>
|
||||
const charW = 8;
|
||||
const charH = 8;
|
||||
const charArr = generateCharsFromFont("monospace", charW, charH, 8, 8, preview_div, debug_div);
|
||||
////console.log('charArr === ',charArr);
|
||||
|
||||
const editorTextArea = document.getElementById('editorTextArea');
|
||||
const cm = new CodeMirror.fromTextArea(editorTextArea, {
|
||||
lineNumbers: true
|
||||
});
|
||||
cm.setSize("100%", "100%");
|
||||
|
||||
CodeMirror.on(cm, "cursorActivity", function() {
|
||||
const isSomethingSelected = cm.somethingSelected();
|
||||
////console.log(isSomethingSelected);
|
||||
const cursor = cm.getCursor();
|
||||
////console.log(cursor);
|
||||
if (isSomethingSelected) {
|
||||
const text = cm.getSelection();
|
||||
const cursor = cm.getCursor();
|
||||
const selectionData = {text,cursor};
|
||||
////console.log("selectionData:",selectionData);
|
||||
}
|
||||
});
|
||||
let count = 0;
|
||||
cm.setValue(`\
|
||||
const CHAR = [
|
||||
${charArr.map((n,i)=>` \`${linebreakify(n,charW)}\`, //${String.fromCharCode(33+i)} `).join("\n")}
|
||||
];
|
||||
`);
|
||||
// setInterval(function(){
|
||||
// count++;
|
||||
// cm.replaceRange(count%2 ? "Hi" : "Hello",{line:0,ch:36},{line:0,ch:(count%2 ? 41 : 38)})
|
||||
// },1000);
|
||||
function linebreakify(str,cols = 4) {
|
||||
const arr = str.split("");
|
||||
const arr2 = [];
|
||||
for(let i = 0; i < arr.length;i++){
|
||||
if((i%cols)===0)arr2.push("");
|
||||
arr2[arr2.length-1] += arr[i];
|
||||
}
|
||||
return "\\\n"+arr2.join("\n");
|
||||
}
|
||||
</script>
|
269
poc_pages/rotation_POC_latest.html
Normal file
@ -0,0 +1,269 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<img src="/images/test_8x8.png" id="test_image" style="display:none;">
|
||||
<script src="/js/canvas_util.js"></script>
|
||||
<script src="/js/color_utils.js"></script>
|
||||
<style>
|
||||
#content_wrapper{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content_wrapper">
|
||||
<div id="orig_colors_wrapper"></div>
|
||||
<div id="color_compare_wrapper"></div>
|
||||
<div id="preview_wrapper"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script>
|
||||
window.ROTATION_CACHE = {};
|
||||
window.SCORE_CACHE = {};
|
||||
window.onload = () => {
|
||||
const p0 = 30;
|
||||
const p1 = 40;
|
||||
const p2 = 50;
|
||||
const p3 = 60;
|
||||
const p4 = 70;
|
||||
const offset = 50;
|
||||
const to = -240;
|
||||
const testArr = [
|
||||
[Math.PI * 0.000, test_image, `calc(${p2}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
[Math.PI * 0.825, test_image, `calc(${p1}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
[Math.PI * 0.750, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
[Math.PI * 0.625, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p1}% - ${offset + to}px)`],
|
||||
[Math.PI * 0.500, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p2}% - ${offset + to}px)`],
|
||||
[Math.PI * 0.375, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p3}% - ${offset + to}px)`],
|
||||
[Math.PI * 0.250, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
[Math.PI * 0.125, test_image, `calc(${p1}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
[Math.PI *-0.125, test_image, `calc(${p3}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
[Math.PI *-0.250, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
[Math.PI *-0.375, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p3}% - ${offset + to}px)`],
|
||||
[Math.PI *-0.500, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p2}% - ${offset + to}px)`],
|
||||
[Math.PI *-0.625, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p1}% - ${offset + to}px)`],
|
||||
[Math.PI *-0.750, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
[Math.PI *-0.825, test_image, `calc(${p3}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
[Math.PI *-1.000, test_image, `calc(${p2}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
];
|
||||
const canvasArr = testArr.map(n=>canvasRotateTest(...n));
|
||||
canvasArr.forEach(canvas=>{
|
||||
preview_wrapper.appendChild(canvas);
|
||||
});
|
||||
|
||||
insertOrigColors();
|
||||
}
|
||||
function insertOrigColors(){
|
||||
////console.log('window.ORIG_IMAGE_META.colorCounts === ',window.ORIG_IMAGE_META.colorCounts);
|
||||
Object.keys(window.ORIG_IMAGE_META.colorCounts).forEach(colorStr=>{
|
||||
////console.log('colorStr === ',colorStr);
|
||||
const div = document.createElement('div');
|
||||
const size = 42;
|
||||
div.innerHTML = `\
|
||||
<div style="float:left;margin:1px;">
|
||||
<div style="float:left;width:${size}px;height:${size}px;background-color:${colorStr}"></div>
|
||||
</div>`;
|
||||
div.onclick = () => {
|
||||
//////console.log('scores === ',scores);
|
||||
}
|
||||
orig_colors_wrapper.appendChild(div);
|
||||
});
|
||||
}
|
||||
function canvasRotateTest(radians = 0, testImage, left, top) {
|
||||
if(radians === 0)window.ORIG_IMAGE_META = imageMeta(testImage);
|
||||
const canvas = document.createElement('canvas');
|
||||
// const w = 1000;
|
||||
// const h = 1000;
|
||||
// const p = 100;
|
||||
const rotationCacheKey = `${testImage.src}_${radians}`;
|
||||
const rotationCacheKey0 = `${testImage.src}_${0}`;
|
||||
//////console.log('rotationCacheKey, rotationCacheKey0 === ',rotationCacheKey, rotationCacheKey0);
|
||||
const p = 1;
|
||||
const x = p;
|
||||
const y = p;
|
||||
const w = (testImage.width + (p*2)) ?? 100;
|
||||
const h = (testImage.height + (p*2)) ?? 100;
|
||||
const sw = testImage.width;
|
||||
const sh = testImage.height;
|
||||
const sw2 = sw/2;
|
||||
const sh2 = sh/2;
|
||||
const sr = [x,y,sw,sh];
|
||||
const center = [sr[0] + sw2, sr[1] + sh2];
|
||||
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(center[0],center[1]);
|
||||
ctx.fillStyle = "#fff";
|
||||
ctx.rotate(radians);
|
||||
ctx.translate(-center[0],-center[1]);
|
||||
ctx.lineWidth = 10;
|
||||
ctx.strokeStyle = "#000";
|
||||
if(testImage) {
|
||||
ctx.drawImage(testImage, sr[0], sr[1]);
|
||||
} else {
|
||||
ctx.strokeRect(...sr);
|
||||
}
|
||||
canvas.style.imageRendering = "pixelated";
|
||||
canvas.style.transform = `scale(8)`;
|
||||
canvas.style.position = "fixed";
|
||||
|
||||
canvas.style.top = top;
|
||||
canvas.style.left = left;
|
||||
|
||||
if(radians==0)return canvas;
|
||||
const imageData = ctx.getImageData(0,0,w,h);
|
||||
const colorCounts = countImageDataColors(imageData);
|
||||
const uniqueColors = Object.keys(colorCounts);
|
||||
const uniqueFullColors = Object.keys(colorCounts).filter(n=>n.includes(",255)"));
|
||||
//////console.log('uniqueFullColors === ',uniqueFullColors);
|
||||
|
||||
const bestMatches = {};
|
||||
uniqueFullColors.forEach(colorStr=>{
|
||||
const rgb = colorToRGB(colorStr);
|
||||
const orig = window.ROTATION_CACHE[rotationCacheKey0] ?? {uniqueFullColors:[...uniqueFullColors]};
|
||||
let minScore = Infinity;
|
||||
let maxScore = -Infinity;
|
||||
let minScoreChampion;
|
||||
let maxScoreChampion;
|
||||
const scores = orig.uniqueFullColors.map(origColor=>{
|
||||
const origRgb = colorToRGB(origColor);
|
||||
const scoreKey = `${rgb.r},${rgb.g},${rgb.b}_${origRgb.r},${origRgb.g},${origRgb.b}`;
|
||||
// //////console.log('rgb, origRgb === ',rgb, origRgb);
|
||||
const score = window.SCORE_CACHE[scoreKey] ?? similarColours(rgb, origRgb);//TODO: find a fire wizard to optimize this
|
||||
window.SCORE_CACHE[scoreKey] = score;
|
||||
const betterMinScore = minScore > score;
|
||||
const betterMaxScore = maxScore < score;
|
||||
if(betterMinScore) {
|
||||
minScore = score;
|
||||
minScoreChampion = origRgb;
|
||||
}
|
||||
if(betterMaxScore) {
|
||||
maxScore = score;
|
||||
maxScoreChampion = origRgb;
|
||||
}
|
||||
return score;
|
||||
});
|
||||
bestMatches[colorStr] = {
|
||||
min:minScoreChampion,
|
||||
max:maxScoreChampion,
|
||||
scores
|
||||
};
|
||||
});
|
||||
for(let i = 0; i < imageData.data.length;i += 4) {// <-- NOTE the 4 here
|
||||
const r = imageData.data[i];
|
||||
const g = imageData.data[i+1];
|
||||
const b = imageData.data[i+2];
|
||||
const a = imageData.data[i+3];//TODO: perf diffs between overwriting 'a' vs ignoring it
|
||||
const rgbStr = `rgba(${r},${g},${b},${a})`;
|
||||
const meta = bestMatches[rgbStr];
|
||||
if(meta) {
|
||||
// const bestRgb = meta.max;
|
||||
const bestRgb = meta.min;
|
||||
if(a === 255){
|
||||
imageData.data[i] = bestRgb.r;
|
||||
imageData.data[i+1] = bestRgb.g;
|
||||
imageData.data[i+2] = bestRgb.b;
|
||||
} else { // probably not needed since a<255 colors are not currently added to bestMatches
|
||||
//////console.log('alpha < 255');
|
||||
imageData.data[i] = 0;
|
||||
imageData.data[i+1] = 0;
|
||||
imageData.data[i+2] = 0;
|
||||
imageData.data[i+3] = 0;
|
||||
}
|
||||
} else if (a < 255) {
|
||||
imageData.data[i] = 0;
|
||||
imageData.data[i+1] = 0;
|
||||
imageData.data[i+2] = 0;
|
||||
imageData.data[i+3] = 0;
|
||||
}
|
||||
// //////console.log('meta === ',meta);
|
||||
}
|
||||
ctx.putImageData(imageData,0,0);
|
||||
//////console.log('bestMatches === ',bestMatches);
|
||||
|
||||
//////console.log('uniqueColors.length === ', uniqueColors.length);
|
||||
//////console.log('uniqueFullColors.length === ', uniqueFullColors.length);
|
||||
|
||||
window.ROTATION_CACHE[(!window.ROTATION_CACHE[rotationCacheKey0]) ? rotationCacheKey0 : rotationCacheKey] = {
|
||||
canvas,
|
||||
ctx,
|
||||
imageData,
|
||||
uniqueColors,
|
||||
uniqueFullColors,
|
||||
bestMatches
|
||||
};
|
||||
const color_compare_row = document.createElement('div');
|
||||
color_compare_row.style.display = "flex";
|
||||
color_compare_row.style.borderBottom = "2px solid black";
|
||||
color_compare_row.style.marginBottom = "2x";
|
||||
color_compare_row.setAttribute("class","color-compare-row");
|
||||
Object.keys(bestMatches).forEach(colorStr=>{
|
||||
const div = document.createElement('div');
|
||||
const {min,max,scores} = bestMatches[colorStr];
|
||||
const minMatchColor = `rgba(${min.r},${min.g},${min.b},255)`;
|
||||
const maxMatchColor = `rgba(${max.r},${max.g},${max.b},255)`;
|
||||
div.innerHTML = `\
|
||||
<div style="display:flex;margin:1px;">
|
||||
<div style="width:10px;height:20px;background-color:${colorStr}"></div>
|
||||
<div style="width:10px;height:20px;background-color:${minMatchColor}"></div>
|
||||
</div>`;
|
||||
div.onclick = () => {
|
||||
////console.log('scores === ',scores);
|
||||
}
|
||||
color_compare_row.appendChild(div);
|
||||
});
|
||||
color_compare_wrapper.appendChild(color_compare_row);
|
||||
color_compare_wrapper.setAttribute("class","color-compare-wrapper");
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function countImageDataColors(imageData) {
|
||||
const w = imageData.width;
|
||||
const h = imageData.height;
|
||||
const colorCounts = {};
|
||||
const startTime = window.performance.now();
|
||||
for (let x = 0; x < w; x++) {
|
||||
for (let y = 0; y < h; y++) {
|
||||
const i = (x + y * w) * 4;
|
||||
const r = imageData.data[i];
|
||||
const g = imageData.data[i + 1];
|
||||
const b = imageData.data[i + 2];
|
||||
const a = imageData.data[i + 3];
|
||||
const key = `rgba(${r},${g},${b},${a})`;
|
||||
if (!colorCounts[key]) colorCounts[key] = 0;
|
||||
colorCounts[key]++;
|
||||
}
|
||||
}
|
||||
const endTime = window.performance.now();
|
||||
//////console.log('Count ImageData Colors duration (ms) === ',endTime - startTime);
|
||||
return colorCounts;
|
||||
}
|
||||
function imageMeta(img) {
|
||||
const canvas = imageToCanvas(img);
|
||||
const ctx = canvas.getContext('2d');
|
||||
const imageData = ctx.getImageData(0,0,img.width,img.height);
|
||||
return {
|
||||
img,
|
||||
colorCounts: countImageDataColors(imageData),
|
||||
};
|
||||
}
|
||||
function imageToCanvas(img) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
return canvas;
|
||||
}
|
||||
</script>
|
268
poc_pages/rotation_POC_old.html
Normal file
@ -0,0 +1,268 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<!-- <img src="/images/test_8x8.png" id="test_image" style="display:none;"> -->
|
||||
<img src="/images/sked_tree_32x32.png" id="test_image" style="display:none;">
|
||||
<script src="/js/canvas_util.js"></script>
|
||||
<script src="/js/color_utils.js"></script>
|
||||
<style>
|
||||
#content_wrapper{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content_wrapper">
|
||||
<div id="orig_colors_wrapper"></div>
|
||||
<div id="color_compare_wrapper"></div>
|
||||
<div id="preview_wrapper"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script>
|
||||
window.ROTATION_CACHE = {};
|
||||
window.SCORE_CACHE = {};
|
||||
window.onload = () => {
|
||||
const p0 = 30;
|
||||
const p1 = 40;
|
||||
const p2 = 50;
|
||||
const p3 = 60;
|
||||
const p4 = 70;
|
||||
const offset = 50;
|
||||
const to = -40;
|
||||
const testArr = [
|
||||
[Math.PI * 0.000, test_image, `calc(${p2}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
[Math.PI * 0.825, test_image, `calc(${p1}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
// [Math.PI * 0.750, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
// [Math.PI * 0.625, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p1}% - ${offset + to}px)`],
|
||||
// [Math.PI * 0.500, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p2}% - ${offset + to}px)`],
|
||||
// [Math.PI * 0.375, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p3}% - ${offset + to}px)`],
|
||||
// [Math.PI * 0.250, test_image, `calc(${p0}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
// [Math.PI * 0.125, test_image, `calc(${p1}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
// [Math.PI *-0.125, test_image, `calc(${p3}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
// [Math.PI *-0.250, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p4}% - ${offset + to}px)`],
|
||||
// [Math.PI *-0.375, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p3}% - ${offset + to}px)`],
|
||||
// [Math.PI *-0.500, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p2}% - ${offset + to}px)`],
|
||||
// [Math.PI *-0.625, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p1}% - ${offset + to}px)`],
|
||||
// [Math.PI *-0.750, test_image, `calc(${p4}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
// [Math.PI *-0.825, test_image, `calc(${p3}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
// [Math.PI *-1.000, test_image, `calc(${p2}% - ${offset}px)`, `calc(${p0}% - ${offset + to}px)`],
|
||||
];
|
||||
const canvasArr = testArr.map(n=>canvasRotateTest(...n));
|
||||
canvasArr.forEach(canvas=>{
|
||||
preview_wrapper.appendChild(canvas);
|
||||
});
|
||||
|
||||
insertOrigColors();
|
||||
}
|
||||
function insertOrigColors(){
|
||||
Object.keys(window.ORIG_IMAGE_META.colorCounts).forEach(colorStr=>{
|
||||
const div = document.createElement('div');
|
||||
const size = 42;
|
||||
div.innerHTML = `\
|
||||
<div style="float:left;margin:1px;">
|
||||
<div style="float:left;width:${size}px;height:${size}px;background-color:${colorStr}"></div>
|
||||
</div>`;
|
||||
div.onclick = () => {
|
||||
//////console.log('scores === ',scores);
|
||||
}
|
||||
orig_colors_wrapper.appendChild(div);
|
||||
});
|
||||
}
|
||||
function canvasRotateTest(radians = 0, testImage, left, top) {
|
||||
if(radians === 0)window.ORIG_IMAGE_META = imageMeta(testImage);
|
||||
const canvas = document.createElement('canvas');
|
||||
// const w = 1000;
|
||||
// const h = 1000;
|
||||
// const p = 100;
|
||||
const rotationCacheKey = `${testImage.src}_${radians}`;
|
||||
const rotationCacheKey0 = `${testImage.src}_${0}`;
|
||||
//////console.log('rotationCacheKey, rotationCacheKey0 === ',rotationCacheKey, rotationCacheKey0);
|
||||
const p = 1;
|
||||
const x = p;
|
||||
const y = p;
|
||||
const w = (testImage.width + (p*2)) ?? 100;
|
||||
const h = (testImage.height + (p*2)) ?? 100;
|
||||
const sw = testImage.width;
|
||||
const sh = testImage.height;
|
||||
const sw2 = sw/2;
|
||||
const sh2 = sh/2;
|
||||
const sr = [x,y,sw,sh];
|
||||
const center = [sr[0] + sw2, sr[1] + sh2];
|
||||
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(center[0],center[1]);
|
||||
ctx.fillStyle = "#fff";
|
||||
ctx.rotate(radians);
|
||||
ctx.translate(-center[0],-center[1]);
|
||||
ctx.lineWidth = 10;
|
||||
ctx.strokeStyle = "#000";
|
||||
if(testImage) {
|
||||
ctx.drawImage(testImage, sr[0], sr[1]);
|
||||
} else {
|
||||
ctx.strokeRect(...sr);
|
||||
}
|
||||
canvas.style.imageRendering = "pixelated";
|
||||
canvas.style.transform = `scale(4)`;
|
||||
canvas.style.position = "fixed";
|
||||
|
||||
canvas.style.top = top;
|
||||
canvas.style.left = left;
|
||||
|
||||
if(radians==0)return canvas;
|
||||
const imageData = ctx.getImageData(0,0,w,h);
|
||||
const colorCounts = countImageDataColors(imageData);
|
||||
const uniqueColors = Object.keys(colorCounts);
|
||||
const uniqueFullColors = Object.keys(colorCounts).filter(n=>n.includes(",255)"));
|
||||
//////console.log('uniqueFullColors === ',uniqueFullColors);
|
||||
|
||||
const bestMatches = {};
|
||||
uniqueFullColors.forEach(colorStr=>{
|
||||
const rgb = colorToRGB(colorStr);
|
||||
const orig = window.ROTATION_CACHE[rotationCacheKey0] ?? {uniqueFullColors:[...uniqueFullColors]};
|
||||
let minScore = Infinity;
|
||||
let maxScore = -Infinity;
|
||||
let minScoreChampion;
|
||||
let maxScoreChampion;
|
||||
const scores = orig.uniqueFullColors.map(origColor=>{
|
||||
const origRgb = colorToRGB(origColor);
|
||||
const scoreKey = `${rgb.r},${rgb.g},${rgb.b}_${origRgb.r},${origRgb.g},${origRgb.b}`;
|
||||
// //////console.log('rgb, origRgb === ',rgb, origRgb);
|
||||
const score = window.SCORE_CACHE[scoreKey] ?? similarColours(rgb, origRgb);//TODO: find a fire wizard to optimize this
|
||||
window.SCORE_CACHE[scoreKey] = score;
|
||||
const betterMinScore = minScore > score;
|
||||
const betterMaxScore = maxScore < score;
|
||||
if(betterMinScore) {
|
||||
minScore = score;
|
||||
minScoreChampion = origRgb;
|
||||
}
|
||||
if(betterMaxScore) {
|
||||
maxScore = score;
|
||||
maxScoreChampion = origRgb;
|
||||
}
|
||||
return score;
|
||||
});
|
||||
bestMatches[colorStr] = {
|
||||
min:minScoreChampion,
|
||||
max:maxScoreChampion,
|
||||
scores
|
||||
};
|
||||
});
|
||||
for(let i = 0; i < imageData.data.length;i += 4) {// <-- NOTE the 4 here
|
||||
const r = imageData.data[i];
|
||||
const g = imageData.data[i+1];
|
||||
const b = imageData.data[i+2];
|
||||
const a = imageData.data[i+3];//TODO: perf diffs between overwriting 'a' vs ignoring it
|
||||
const rgbStr = `rgba(${r},${g},${b},${a})`;
|
||||
const meta = bestMatches[rgbStr];
|
||||
if(meta) {
|
||||
// const bestRgb = meta.max;
|
||||
const bestRgb = meta.min;
|
||||
if(a === 255){
|
||||
imageData.data[i] = bestRgb.r;
|
||||
imageData.data[i+1] = bestRgb.g;
|
||||
imageData.data[i+2] = bestRgb.b;
|
||||
} else { // probably not needed since a<255 colors are not currently added to bestMatches
|
||||
//////console.log('alpha < 255');
|
||||
imageData.data[i] = 0;
|
||||
imageData.data[i+1] = 0;
|
||||
imageData.data[i+2] = 0;
|
||||
imageData.data[i+3] = 0;
|
||||
}
|
||||
} else if (a < 255) {
|
||||
imageData.data[i] = 0;
|
||||
imageData.data[i+1] = 0;
|
||||
imageData.data[i+2] = 0;
|
||||
imageData.data[i+3] = 0;
|
||||
}
|
||||
// //////console.log('meta === ',meta);
|
||||
}
|
||||
ctx.putImageData(imageData,0,0);
|
||||
//////console.log('bestMatches === ',bestMatches);
|
||||
|
||||
//////console.log('uniqueColors.length === ', uniqueColors.length);
|
||||
//////console.log('uniqueFullColors.length === ', uniqueFullColors.length);
|
||||
|
||||
window.ROTATION_CACHE[(!window.ROTATION_CACHE[rotationCacheKey0]) ? rotationCacheKey0 : rotationCacheKey] = {
|
||||
canvas,
|
||||
ctx,
|
||||
imageData,
|
||||
uniqueColors,
|
||||
uniqueFullColors,
|
||||
bestMatches
|
||||
};
|
||||
const color_compare_row = document.createElement('div');
|
||||
color_compare_row.style.display = "flex";
|
||||
color_compare_row.style.borderBottom = "2px solid black";
|
||||
color_compare_row.style.marginBottom = "2x";
|
||||
color_compare_row.setAttribute("class","color-compare-row");
|
||||
Object.keys(bestMatches).forEach(colorStr=>{
|
||||
const div = document.createElement('div');
|
||||
const {min,max,scores} = bestMatches[colorStr];
|
||||
const minMatchColor = `rgba(${min.r},${min.g},${min.b},255)`;
|
||||
const maxMatchColor = `rgba(${max.r},${max.g},${max.b},255)`;
|
||||
div.innerHTML = `\
|
||||
<div style="display:flex;margin:1px;">
|
||||
<div style="width:10px;height:20px;background-color:${colorStr}"></div>
|
||||
<div style="width:10px;height:20px;background-color:${minMatchColor}"></div>
|
||||
</div>`;
|
||||
div.onclick = () => {
|
||||
////console.log('scores === ',scores);
|
||||
}
|
||||
color_compare_row.appendChild(div);
|
||||
});
|
||||
color_compare_wrapper.appendChild(color_compare_row);
|
||||
color_compare_wrapper.setAttribute("class","color-compare-wrapper");
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function countImageDataColors(imageData) {
|
||||
const w = imageData.width;
|
||||
const h = imageData.height;
|
||||
const colorCounts = {};
|
||||
const startTime = window.performance.now();
|
||||
for (let x = 0; x < w; x++) {
|
||||
for (let y = 0; y < h; y++) {
|
||||
const i = (x + y * w) * 4;
|
||||
const r = imageData.data[i];
|
||||
const g = imageData.data[i + 1];
|
||||
const b = imageData.data[i + 2];
|
||||
const a = imageData.data[i + 3];
|
||||
const key = `rgba(${r},${g},${b},${a})`;
|
||||
if (!colorCounts[key]) colorCounts[key] = 0;
|
||||
colorCounts[key]++;
|
||||
}
|
||||
}
|
||||
const endTime = window.performance.now();
|
||||
////console.log('Count ImageData Colors duration (ms) === ',endTime - startTime);
|
||||
return colorCounts;
|
||||
}
|
||||
function imageMeta(img) {
|
||||
const canvas = imageToCanvas(img);
|
||||
const ctx = canvas.getContext('2d');
|
||||
const imageData = ctx.getImageData(0,0,img.width,img.height);
|
||||
return {
|
||||
img,
|
||||
colorCounts: countImageDataColors(imageData),
|
||||
};
|
||||
}
|
||||
function imageToCanvas(img) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
return canvas;
|
||||
}
|
||||
</script>
|
45
poc_pages/tiny_text.html
Normal file
@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="chaotic_good"></canvas>
|
||||
<canvas id="neutral_good"></canvas>
|
||||
<canvas id="lawful_good"></canvas>
|
||||
<canvas id="chaotic_neutral"></canvas>
|
||||
<canvas id="true_neutral"></canvas>
|
||||
<canvas id="lawful_neutral"></canvas>
|
||||
<canvas id="chaotic_evil"></canvas>
|
||||
<canvas id="neutral_evil"></canvas>
|
||||
<canvas id="lawful_evil"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
<script src="/js/canvas_util.js"></script>
|
||||
<script src="/js/color_utils.js"></script>
|
||||
<script>
|
||||
const canvas = document.createElement('canvas');
|
||||
const w = 256;
|
||||
const h = 256;
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillRect(0,0,w,h);
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.fillRect(1,1,w-2,h-2);
|
||||
canvas.style.imageRendering = "pixelated";
|
||||
canvas.style.transform = `scale(4)`;
|
||||
canvas.style.position = "fixed";
|
||||
canvas.style.top = "calc(50% - "+(h/2)+"px)";
|
||||
canvas.style.left = "calc(50% - "+(w/2)+"px)";
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
ctx.fillStyle = 'black';
|
||||
drawTinyText(ctx, "HELLO", 7, 7, "monospace");
|
||||
|
||||
</script>
|
120
poc_pages/wang_tiles_16.html
Normal file
@ -0,0 +1,120 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Basic three.js template</title>
|
||||
<style>
|
||||
body { margin: 0; }
|
||||
canvas {
|
||||
/* position: fixed; */
|
||||
/* top: 450px;
|
||||
left: 450px; */
|
||||
/* transform: scale(4); */
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- import the three.js library -->
|
||||
<img src="/images/wang_tilesets_32x32.png" style="display:none;" id="tiles_image">
|
||||
<img src="/images/icons_14x14.png" data-tile-width="14" data-tile-height="14" style="display:none;" id="icons_img1">
|
||||
<script src="/js/canvas_util.js"></script>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/three@0.122.0/build/three.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/examples/js/controls/OrbitControls.js"></script> -->
|
||||
<script>
|
||||
// var scene = new THREE.Scene();
|
||||
// var camera = new THREE.PerspectiveCamera(
|
||||
// 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
|
||||
// var renderer = new THREE.WebGLRenderer();
|
||||
// renderer.setSize( window.innerWidth, window.innerHeight );
|
||||
// document.body.appendChild( renderer.domElement );
|
||||
// camera.position.z = 5;
|
||||
|
||||
// const controls = new THREE.OrbitControls( camera, renderer.domElement );
|
||||
|
||||
//var geometry = new THREE.BoxGeometry( 1, 1, 1 );
|
||||
//var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
|
||||
//var cube = new THREE.Mesh( geometry, material );
|
||||
//scene.add( cube );
|
||||
|
||||
const data = `
|
||||
10000000
|
||||
00100000
|
||||
01110010
|
||||
00100010
|
||||
00000010
|
||||
00111000
|
||||
00000010
|
||||
00000000
|
||||
`
|
||||
.replaceAll("\n","")
|
||||
.replaceAll(" ","")
|
||||
.replaceAll("\t","")
|
||||
;
|
||||
const ASSET_CACHE = {};
|
||||
const TOOL_ARR = [
|
||||
// {name:"Pencil",hotkey:"B",art:{img: icons_img1,idx:0}},
|
||||
{name:"Eraser",hotkey:"E",art:{img: icons_img1,idx:1}},
|
||||
{name:"Rectangle",hotkey:"R",art:{img: icons_img1,idx:2}},
|
||||
{name:"Ellipse",hotkey:"S",art:{img: icons_img1,idx:2}},
|
||||
{name:"Line",hotkey:"S",art:{img: icons_img1,idx:2}},
|
||||
{name:"Line",hotkey:"S",art:{img: icons_img1,idx:2}},
|
||||
];
|
||||
window.onload = () => {
|
||||
const columns = 8;
|
||||
const tilesetsArr = imageChopper(tiles_image,32,32);
|
||||
const tilesetsCanvasArrArr = tilesetsArr.map((canvas)=>{
|
||||
return imageChopper(canvas,8,8);
|
||||
});
|
||||
////console.log('tilesetsCanvasArrArr === ', tilesetsCanvasArrArr);
|
||||
|
||||
////console.log('data === ',data);
|
||||
let i = 0;
|
||||
const width = columns;
|
||||
const height = 8;
|
||||
const tileIndexArr = [];
|
||||
for(let i = 0; i < data.length;i++) {
|
||||
// debugger;
|
||||
const x = i%width;
|
||||
const y = Math.floor(i/width);
|
||||
const v = data[i];
|
||||
let binNum = -1;
|
||||
if(v === "1") {
|
||||
// if(i === 10)debugger;
|
||||
const n = y > 0 ? data[i-width] : "0";
|
||||
const e = x < (width-1) ? data[i+1] : "0";
|
||||
const s = data[i+width];
|
||||
const w = i > 0 ? data[i-1] : "0";
|
||||
const binStr = `${w}${s}${e}${n}`;
|
||||
binNum = parseInt(binStr,2);
|
||||
////console.log(v,`data[${i}]`,w,s,e,n,"= "+binNum);
|
||||
}
|
||||
tileIndexArr.push(binNum);
|
||||
}
|
||||
////console.log('tileIndexArr === ',tileIndexArr);
|
||||
const retCanvas = tilesToCanvas(tileIndexArr,columns,tilesetsCanvasArrArr[1]);
|
||||
////console.log('retCanvas === ',retCanvas);
|
||||
document.body.appendChild(retCanvas);
|
||||
}
|
||||
function insertTools(arr){
|
||||
arr.forEach((tool,i)=>{
|
||||
if(!ASSET_CACHE[tool.art.img.src]){
|
||||
ASSET_CACHE[tool.art.img.src] = imageChopper(
|
||||
tool.art.img,
|
||||
Number(tool.art.img.getAttribute("data-tile-width")),
|
||||
Number(tool.art.img.getAttribute("data-tile-height"))
|
||||
);
|
||||
}
|
||||
tool.art.canvas = ASSET_CACHE[tool.art.img.src][tool.art.idx];
|
||||
// const canvas = document.createElement('canvas');
|
||||
// const w = 64;
|
||||
// const h = 64;
|
||||
// canvas.width = w;
|
||||
// canvas.height = h;
|
||||
// const ctx = canvas.getContext('2d');
|
||||
const canvas = pixelButton(0,0,0,0,tool.art.canvas);
|
||||
document.body.appendChild(canvas);
|
||||
});
|
||||
////console.log('ASSET_CACHE === ',ASSET_CACHE);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
30
server.js
@ -9,7 +9,7 @@ const FULLBUILDPATH = path.join(__dirname, BUILDDIR)
|
||||
|
||||
//LOGGING
|
||||
app.use((req, res, next)=> {
|
||||
//console.log('REQUEST', req.method+' '+req.originalUrl, res.statusCode);
|
||||
//////console.log('REQUEST', req.method+' '+req.originalUrl, res.statusCode);
|
||||
next();
|
||||
});
|
||||
|
||||
@ -29,17 +29,29 @@ app.use((req, res, next) => {
|
||||
app.use('/', express.static(FULLBUILDPATH, {
|
||||
//custom function required for logging static files
|
||||
setHeaders: (res, filePath, fileStats) => {
|
||||
console.info('GET', '/'+path.relative(FULLBUILDPATH, filePath), res.statusCode);
|
||||
//console.info('GET', '/'+path.relative(FULLBUILDPATH, filePath), res.statusCode);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
|
||||
//ROUTE - match / or any route with just numbers letters and dashes, and return index.htm (all other routes should have been handled already)
|
||||
app.get('/', (req, res, next) => {
|
||||
console.log('root')
|
||||
////console.log('root')
|
||||
res.sendFile(path.join(__dirname, BUILDDIR, 'index.htm'), {}, function (err) {
|
||||
console.log('sent file');
|
||||
////console.log('sent file');
|
||||
return next();
|
||||
});
|
||||
});
|
||||
app.get('/pixel-editor', (req, res, next) => {
|
||||
////console.log('root')
|
||||
res.sendFile(path.join(__dirname, BUILDDIR, 'index.htm'), {}, function (err) {
|
||||
////console.log('sent file');
|
||||
return next();
|
||||
});
|
||||
});
|
||||
app.get('/pixel-editor/?:palette/?:resolution/?:patternWidth/?:patternBinStr', (req, res, next) => {
|
||||
////console.log('root')
|
||||
res.sendFile(path.join(__dirname, BUILDDIR, 'index.htm'), {}, function (err) {
|
||||
////console.log('sent file');
|
||||
return next();
|
||||
});
|
||||
});
|
||||
@ -52,13 +64,13 @@ if (process.env.RELOAD === "yes") {
|
||||
reload(app).then(() => {
|
||||
//start server
|
||||
app.server = app.listen(PORT, () => {
|
||||
console.log(`Web server listening on port ${PORT} (with reload module)`);
|
||||
////console.log(`Web server listening on port ${PORT} (with reload module)`);
|
||||
|
||||
})
|
||||
});
|
||||
} else {
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Web server listening on port ${PORT}`);
|
||||
////console.log(`Web server listening on port ${PORT}`);
|
||||
})
|
||||
}
|
||||
|
||||
@ -70,5 +82,5 @@ app.use(function(req, res, next) {
|
||||
|
||||
//LOGGING
|
||||
app.use((req, res, next)=> {
|
||||
console.log(req.method+' '+req.originalUrl, res.statusCode);
|
||||
////console.log(req.method+' '+req.originalUrl, res.statusCode);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!-- CANVASES -->
|
||||
<div id="canvas-view">
|
||||
<canvas id="vfx-canvas" class = "drawingCanvas"></canvas>
|
||||
<canvas id = "tmp-canvas" class = "drawingCanvas"></canvas>
|
||||
<canvas id="tmp-canvas" class = "drawingCanvas"></canvas>
|
||||
<canvas id="pixel-canvas" class = "drawingCanvas"></canvas>
|
||||
<canvas id="checkerboard" class = "drawingCanvas"></canvas>
|
||||
<canvas id="pixel-grid" class = "drawingCanvas"></canvas>
|
||||
|
@ -1,3 +1,18 @@
|
||||
<span id="colors-menu-settings">
|
||||
<button id="cm-add">
|
||||
Add color
|
||||
</button>
|
||||
<button id="cm-remove">
|
||||
Remove color
|
||||
</button>
|
||||
<button id="cm-zoomin">
|
||||
Zoom in
|
||||
</button>
|
||||
<button id="cm-zoomout">
|
||||
Zoom out
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<ul id="colors-menu">
|
||||
<li class="noshrink"><button title="Add Current Color To Palette" id="add-color-button">{{svg "./plus.svg" width="30" height="30"}}</button></li>
|
||||
</ul>
|
||||
|
@ -18,8 +18,11 @@
|
||||
|
||||
{{> main-menu}}
|
||||
{{> tools-menu}}
|
||||
{{> colors-menu}}
|
||||
{{> layers-menu}}
|
||||
|
||||
<div id="right-nav">
|
||||
{{> colors-menu}}
|
||||
{{> layers-menu}}
|
||||
</div>
|
||||
|
||||
{{> tool-previews}}
|
||||
{{> canvases}}
|
||||
|
@ -1,52 +1,20 @@
|
||||
Heyo! New pixel editor update, with some very requested changes. After the code refactoring, adding features
|
||||
is way easier: I introduced some more selection tools, the ellipse tool and an info bar in the top menu :)
|
||||
|
||||
<h2>Lasso tool</h2>
|
||||
Finally! With the lasso tool you're not forced to select rectangular areas anymore. Have fun selecting, cutting,
|
||||
copying and pasting any kind of selection with pixel-perfect precision.
|
||||
|
||||
</br><img src="lassoselect-tutorial.gif"/>
|
||||
|
||||
<h2>Magic wand</h2>
|
||||
In addition to the lasso tool, we added a new selection tool: the magic wand. You can use it to select
|
||||
contiguous areas of the same colour! If you need to exactly select the pixels of a certain colour, you're
|
||||
probably going to find the magic wand useful.
|
||||
|
||||
</br><img src="magicwand-tutorial.gif"/>
|
||||
|
||||
<h2>Ellipse tool</h2>
|
||||
I added a cute friend for the rectangle tool: with the ellipse tool you'll be able to draw circles and
|
||||
ellipses of all sizes. The tool works similarly to the rectangle tool: select it to draw empty ellipses,
|
||||
click on the ellipse button again to draw filled ellipses.
|
||||
|
||||
</br><img src="ellipse-tutorial.gif"/>
|
||||
|
||||
<h2>Tool tutorials</h2>
|
||||
I know what you're thinking, "wow those gifs are so cute, that guy must have put a lot of love in them".
|
||||
Well, I'm glad you like them, and I have good news for you: there are more! Move the cursor on a tool
|
||||
button: after a little a small tutorial explaining how to use the tool will appear. Hope it's useful for
|
||||
everyone who's new to the editor!
|
||||
|
||||
</br><img src="tool-tutorials.gif"/>
|
||||
|
||||
<h2>Top bar info</h2>
|
||||
Depending on the tool you're using, you'll notice that the top right part of the editor will slightly change.
|
||||
When using a resizable tool (eraser, brush, rectangle, ellipse, line), it's now possible to select a precise
|
||||
size by typing it in the input field that appears when you select it. More features that make use of the
|
||||
top bar are planned.
|
||||
|
||||
<h2>Bug fixes and minor details</h2>
|
||||
Hi! Been a while after the last update, the lack of contributors slowed down the support a bit. You may have seen
|
||||
it from the Lospec homepage, but this editor has been liberated and released under the GPL-3.0 license! This
|
||||
probably incentivized more contributors to help with development. I want to thank them for their work.
|
||||
<ul>
|
||||
<li>Squares in the splash page are now...well...actual squares</li>
|
||||
<li>Using the mouse when a dialogue popup is open will no longer edit the canvas</li>
|
||||
<li>For coders: the selection system has been uniformed</li>
|
||||
<li>Tool buttons have been shrinked to make room for more</li>
|
||||
<li><a href="https://github.com/NSSure">NSSure</a> added the ability to import images in the editor. Very
|
||||
useful if you need to have a reference layer!</li>
|
||||
<li><a href="https://github.com/blueprismo">Blueprismo</a> added a Dockerfile to the repository, making the
|
||||
editor even more accessible.</li>
|
||||
<li><a href="https://github.com/hacknorris-aka-penguin">hacknorris-aka-penguin</a> and
|
||||
<a href="https://github.com/matthewd673">matthewd673</a> fixed a few bugs we had.</li>
|
||||
</ul>
|
||||
In addition, we had a pretty big contribution by <a href="https://github.com/jbrundage">Jaman</a>. He did some
|
||||
needed refactoring that will help us with development. In addition, he kindly added a new palette layout so that
|
||||
big palettes aren't too stretched out, like it happened with the previous vertical layout.
|
||||
</br>
|
||||
<img src="palettegrid.gif"/>
|
||||
|
||||
<h2>End of log</h2>
|
||||
You've reached the end of this log, congrats. Special thanks to Jaman on Discord, who's helping us and
|
||||
who found a quite nasty bug in the selectiont tools. Hope to see you soon in a new log!</br>
|
||||
- <a href="https://github.com/unsettledgames">Unsettled</a>
|
||||
</br></br>
|
||||
P.S.: we're always looking for contributors! Join the <a href="https://discord.com/invite/QjsgTQM">Lospec discord</a> to get in touch
|
||||
or have a look at the <a href="https://github.com/lospec/pixel-edior">editor repository</a>!
|
||||
</br>
|
||||
Lastly, I would also like to thank all the people who submitted feature suggestions and bug reports on Github, they
|
||||
are very welcome and we will try to address as many as possible of them.
|
@ -1,6 +1,6 @@
|
||||
<!-- LAYER MENU -->
|
||||
<ul id = "layers-menu">
|
||||
<li class = "layers-menu-entry selected-layer">
|
||||
<ul id="layers-menu">
|
||||
<li id="default-layer-list-item" class="layers-menu-entry selected-layer" style="display:none;">
|
||||
<canvas class = "preview-canvas"></canvas>
|
||||
<ul class="layer-buttons">
|
||||
<li class = "layer-button">
|
||||
@ -17,17 +17,20 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Layer 0<div class = "gradient"></div></p>
|
||||
<p>Background<div class = "gradient"></div></p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<button id = "add-layer-button">
|
||||
<li id="add-layer-li">
|
||||
<button id="add-layer-button">
|
||||
{{svg "plus.svg" width="20" height="20"}} Add layer
|
||||
</button>
|
||||
<button id="add-reference-button">
|
||||
{{svg "plus.svg" width="20" height="20"}} Add reference
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id = "layer-properties-menu">
|
||||
<ul id="layer-properties-menu">
|
||||
<li>
|
||||
<button onclick = "LayerList.renameLayer()">Rename</button>
|
||||
</li>
|
||||
|
52
views/logs/00003-selection-tools.hbs
Normal file
@ -0,0 +1,52 @@
|
||||
Heyo! New pixel editor update, with some very requested changes. After the code refactoring, adding features
|
||||
is way easier: I introduced some more selection tools, the ellipse tool and an info bar in the top menu :)
|
||||
|
||||
<h2>Lasso tool</h2>
|
||||
Finally! With the lasso tool you're not forced to select rectangular areas anymore. Have fun selecting, cutting,
|
||||
copying and pasting any kind of selection with pixel-perfect precision.
|
||||
|
||||
</br><img src="/lassoselect-tutorial.gif"/>
|
||||
|
||||
<h2>Magic wand</h2>
|
||||
In addition to the lasso tool, we added a new selection tool: the magic wand. You can use it to select
|
||||
contiguous areas of the same colour! If you need to exactly select the pixels of a certain colour, you're
|
||||
probably going to find the magic wand useful.
|
||||
|
||||
</br><img src="/magicwand-tutorial.gif"/>
|
||||
|
||||
<h2>Ellipse tool</h2>
|
||||
I added a cute friend for the rectangle tool: with the ellipse tool you'll be able to draw circles and
|
||||
ellipses of all sizes. The tool works similarly to the rectangle tool: select it to draw empty ellipses,
|
||||
click on the ellipse button again to draw filled ellipses.
|
||||
|
||||
</br><img src="/ellipse-tutorial.gif"/>
|
||||
|
||||
<h2>Tool tutorials</h2>
|
||||
I know what you're thinking, "wow those gifs are so cute, that guy must have put a lot of love in them".
|
||||
Well, I'm glad you like them, and I have good news for you: there are more! Move the cursor on a tool
|
||||
button: after a little a small tutorial explaining how to use the tool will appear. Hope it's useful for
|
||||
everyone who's new to the editor!
|
||||
|
||||
</br><img src="/tool-tutorials.gif"/>
|
||||
|
||||
<h2>Top bar info</h2>
|
||||
Depending on the tool you're using, you'll notice that the top right part of the editor will slightly change.
|
||||
When using a resizable tool (eraser, brush, rectangle, ellipse, line), it's now possible to select a precise
|
||||
size by typing it in the input field that appears when you select it. More features that make use of the
|
||||
top bar are planned.
|
||||
|
||||
<h2>Bug fixes and minor details</h2>
|
||||
<ul>
|
||||
<li>Squares in the splash page are now...well...actual squares</li>
|
||||
<li>Using the mouse when a dialogue popup is open will no longer edit the canvas</li>
|
||||
<li>For coders: the selection system has been uniformed</li>
|
||||
<li>Tool buttons have been shrinked to make room for more</li>
|
||||
</ul>
|
||||
|
||||
<h2>End of log</h2>
|
||||
You've reached the end of this log, congrats. Special thanks to Jaman on Discord, who's helping us and
|
||||
who found a quite nasty bug in the selectiont tools. Hope to see you soon in a new log!</br>
|
||||
- <a href="https://github.com/unsettledgames">Unsettled</a>
|
||||
</br></br>
|
||||
P.S.: we're always looking for contributors! Join the <a href="https://discord.com/invite/QjsgTQM">Lospec discord</a> to get in touch
|
||||
or have a look at the <a href="https://github.com/lospec/pixel-edior">editor repository</a>!
|
@ -28,7 +28,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<button id = "layer-button">Layer</button>
|
||||
<button id="layer-button">Layer</button>
|
||||
<ul>
|
||||
<li><button onclick = "LayerList.addLayer()">New layer</button></li>
|
||||
<li><button onclick = "LayerList.duplicateLayer()">Duplicate</button></li>
|
||||
@ -54,6 +54,7 @@
|
||||
<ul>
|
||||
<li><button id="switch-mode-button">Switch to basic mode</button></li>
|
||||
<li><button onclick="Dialogue.showDialogue('splash', false)">Splash page</button></li>
|
||||
<li><button id="auto-cache-button" onclick="FileManager.toggleCache(this)">Enable auto-cache</button></li>
|
||||
<li><button>Settings</button></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!--CANVAS RESIZE-->
|
||||
<div class="update" id = "resize-canvas">
|
||||
<div class="update" id="resize-canvas">
|
||||
<button class="close-button">{{svg "x.svg" width="20" height="20"}}</button>
|
||||
<h1>Resize canvas</h1>
|
||||
|
||||
<!--PIVOTS-->
|
||||
<span id = "pivot-menu">
|
||||
<span id="pivot-menu">
|
||||
<button class="pivot-button" value="topleft">{{svg "arrows/topleft.svg" width="20" height="20"}}</button>
|
||||
<button class="pivot-button" value="top">{{svg "arrows/top.svg" width="20" height="20"}}</button>
|
||||
<button class="pivot-button" value="topright">{{svg "arrows/topright.svg" width="20" height="20"}}</button>
|
||||
@ -16,7 +16,7 @@
|
||||
<button class="pivot-button" value="bottomright">{{svg "arrows/bottomright.svg" width="20" height="20"}}</button>
|
||||
</span>
|
||||
<!-- SIZE-->
|
||||
<span id = "rc-size-menu">
|
||||
<span id="rc-size-menu">
|
||||
<h2>Size</h2>
|
||||
<div>
|
||||
<span>
|
||||
@ -31,7 +31,7 @@
|
||||
</div>
|
||||
</span>
|
||||
<!--BORDERS-->
|
||||
<span id = "borders-menu">
|
||||
<span id="borders-menu">
|
||||
<h2>Borders offsets</h2>
|
||||
<div>
|
||||
<span>
|
||||
@ -54,6 +54,6 @@
|
||||
value="{{#if border}}{{border}}{{else}}0{{/if}}" autocomplete="off"/>
|
||||
</span>
|
||||
</div>
|
||||
<button id = "resize-canvas-confirm">Resize canvas</button>
|
||||
<button id="resize-canvas-confirm">Resize canvas</button>
|
||||
</span>
|
||||
</div>
|
@ -1,11 +1,11 @@
|
||||
<!-- PALETTE -->
|
||||
<div id = "palette-block">
|
||||
<div id="palette-block">
|
||||
<button class="close-button">{{svg "x.svg" width="20" height="20"}}</button>
|
||||
|
||||
<h1>Edit palette</h1>
|
||||
|
||||
<div id = "colour-picker">
|
||||
<div id = "cp-modes">
|
||||
<div id="colour-picker">
|
||||
<div id="cp-modes">
|
||||
<button id="cp-rgb" class="cp-selected-mode">RGB</button>
|
||||
<button id="cp-hsv">HSV</button>
|
||||
<button id="cp-hsl">HSL</button>
|
||||
@ -14,7 +14,7 @@
|
||||
<input id="cp-hex" type="text" value="#123456"/>
|
||||
</div>
|
||||
|
||||
<div id = "sliders-container">
|
||||
<div id="sliders-container">
|
||||
<div class = "cp-slider-entry">
|
||||
<label for = "first-slider">R</label>
|
||||
<input type="range" min="0" max="255" class="colour-picker-slider" id="first-slider"/>
|
||||
@ -29,25 +29,25 @@
|
||||
|
||||
<div class = "cp-slider-entry">
|
||||
<label for = "third-slider">B</label>
|
||||
<input type="range" min = "0" max = "255" class = "colour-picker-slider" id = "third-slider"/>
|
||||
<input type="range" min = "0" max = "255" class = "colour-picker-slider" id="third-slider"/>
|
||||
<input type = "text" value = "128" id="cp-sliderText3"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id = "cp-minipicker">
|
||||
<input type = "range" min = "0" max = "100" id = "cp-minipicker-slider"/>
|
||||
<div id="cp-minipicker">
|
||||
<input type = "range" min = "0" max = "100" id="cp-minipicker-slider"/>
|
||||
<div id="cp-canvas-container">
|
||||
<canvas id = "cp-spectrum"></canvas>
|
||||
<canvas id="cp-spectrum"></canvas>
|
||||
<div id="cp-active-icon" class="cp-picker-icon"></div>
|
||||
</div>
|
||||
|
||||
<div id = "cp-colours-previews">
|
||||
<div id="cp-colours-previews">
|
||||
<div class = "cp-colour-preview">
|
||||
#123456
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id = "cp-colour-picking-modes">
|
||||
<div id="cp-colour-picking-modes">
|
||||
<button id="cp-mono" class="cp-selected-mode">Mono</button>
|
||||
<button id="cp-analog">Nlgs</button>
|
||||
<button id="cp-cmpt">Cmpt</button>
|
||||
@ -58,8 +58,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id = "palette-container">
|
||||
<ul id = "palette-list">
|
||||
<div id="palette-container">
|
||||
<ul id="palette-list">
|
||||
<li style = "background-color:rgb(255,0,0);width:40px;height:40px;" onmousedown="PaletteBlock.startRampSelection(event)"
|
||||
onmousemove="PaletteBlock.updateRampSelection(event)" onmouseup="PaletteBlock.endRampSelection(event)"></li>
|
||||
<li style = "background-color:rgb(0,255,0);width:40px;height:40px;"onmousedown="PaletteBlock.startRampSelection(event)"
|
||||
|
@ -9,6 +9,6 @@
|
||||
</div>
|
||||
|
||||
<div class="popup-actions">
|
||||
<button class="default" id = "export-confirm">Export</button>
|
||||
<button class="default" id="export-confirm">Export</button>
|
||||
</div>
|
||||
</div>
|
@ -9,6 +9,6 @@
|
||||
</div>
|
||||
|
||||
<div class="popup-actions">
|
||||
<button class="default" id = "save-project-confirm">Save</button>
|
||||
<button class="default" id="save-project-confirm">Save</button>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,5 @@
|
||||
<!-- Splash page -->
|
||||
<div id = "splash">
|
||||
<div id="splash">
|
||||
<div id="splash-news">
|
||||
<div id="latest-update">
|
||||
<h1>Latest updates</h1>
|
||||
@ -38,7 +38,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id = "sp-quickstart-container">
|
||||
<div id="sp-quickstart-container">
|
||||
<div id="sp-quickstart-title">
|
||||
Quickstart
|
||||
</div>
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!--SPRITE RESIZE-->
|
||||
<div class="update" id = "resize-sprite">
|
||||
<div class="update" id="resize-sprite">
|
||||
<button class="close-button">{{svg "x.svg" width="20" height="20"}}</button>
|
||||
<h1>Scale sprite</h1>
|
||||
<!-- SIZE-->
|
||||
<h2>New size</h2>
|
||||
<span id = "rs-size-menu">
|
||||
<span id="rs-size-menu">
|
||||
<div>
|
||||
<span>
|
||||
Width: <input id="rs-width" type="number" default="0" step="1"
|
||||
@ -19,7 +19,7 @@
|
||||
</span>
|
||||
<!--BORDERS-->
|
||||
<h2>Resize percentages</h2>
|
||||
<span id = "rs-percentage-menu">
|
||||
<span id="rs-percentage-menu">
|
||||
<div>
|
||||
<span>
|
||||
Width <input id="rs-width-percentage" type="number" default="0" step="1"
|
||||
@ -31,19 +31,19 @@
|
||||
value="{{#if border}}{{border}}{{else}}0{{/if}}" autocomplete="off"/> %
|
||||
</span>
|
||||
</div>
|
||||
<div id = "rs-ratio-div">
|
||||
<div id="rs-ratio-div">
|
||||
<span>
|
||||
Keep current ratio <input type = "checkbox" id = "rs-keep-ratio"/>
|
||||
Keep current ratio <input type = "checkbox" id="rs-keep-ratio"/>
|
||||
</span>
|
||||
<span>
|
||||
Scaling algorithm:
|
||||
<select name = "resize-algorithm" id = "resize-algorithm-combobox">
|
||||
<select name = "resize-algorithm" id="resize-algorithm-combobox">
|
||||
<option value = "nearest-neighbor">Nearest neighbour</option>
|
||||
<option value = "bilinear-interpolation">Bilinear</option>
|
||||
</select>
|
||||
</span>
|
||||
</br>
|
||||
<button id = "resize-sprite-confirm">Scale sprite</button>
|
||||
<button id="resize-sprite-confirm">Scale sprite</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
@ -16,8 +16,8 @@
|
||||
</li>
|
||||
|
||||
<li class="expanded">
|
||||
<button id="rectangle-button">{{svg "rectangle.svg" width="24" height="24" id = "rectangle-empty-button-svg"}}
|
||||
{{svg "fullrect.svg" width="24" height="24" id = "rectangle-full-button-svg" display = "none"}}</button>
|
||||
<button id="rectangle-button">{{svg "rectangle.svg" width="24" height="24" id="rectangle-empty-button-svg"}}
|
||||
{{svg "fullrect.svg" width="24" height="24" id="rectangle-full-button-svg" display = "none"}}</button>
|
||||
<ul class="size-buttons">
|
||||
<button title="Increase Rectangle Size" id="rectangle-bigger-button" class="tools-menu-sub-button">{{svg "plus.svg" width="12" height="12"}}</button>
|
||||
<button title="Decrease Rectangle Size" id="rectangle-smaller-button" class="tools-menu-sub-button">{{svg "minus.svg" width="12" height="12"}}</button>
|
||||
@ -26,8 +26,8 @@
|
||||
|
||||
<li class="expanded">
|
||||
<button id="ellipse-button">
|
||||
{{svg "ellipse.svg" width="24" height="24" id = "ellipse-empty-button-svg"}}
|
||||
{{svg "filledellipse.svg" width="24" height="24" id = "ellipse-full-button-svg" display = "none"}}
|
||||
{{svg "ellipse.svg" width="24" height="24" id="ellipse-empty-button-svg"}}
|
||||
{{svg "filledellipse.svg" width="24" height="24" id="ellipse-full-button-svg" display = "none"}}
|
||||
</button>
|
||||
<ul class="size-buttons">
|
||||
<button title="Increase Ellipse Size" id="ellipse-bigger-button" class="tools-menu-sub-button">{{svg "plus.svg" width="12" height="12"}}</button>
|
||||
@ -45,11 +45,11 @@
|
||||
|
||||
<li><button id="fill-button">{{svg "fill.svg" width="24" height="24"}}</button></li>
|
||||
|
||||
<li><button id = "rectselect-button">{{svg "rectselect.svg" width = "24" height = "24"}}</button><li>
|
||||
<li><button id="rectselect-button">{{svg "rectselect.svg" width = "24" height = "24"}}</button><li>
|
||||
|
||||
<li><button id = "lassoselect-button">{{svg "lasso.svg" width = "26" height = "26"}}</button></li>
|
||||
<li><button id="lassoselect-button">{{svg "lasso.svg" width = "26" height = "26"}}</button></li>
|
||||
|
||||
<li><button id = "magicwand-button">{{svg "magicwand.svg" width = "26" height = "26"}}</button></li>
|
||||
<li><button id="magicwand-button">{{svg "magicwand.svg" width = "26" height = "26"}}</button></li>
|
||||
|
||||
<li><button id="eyedropper-button">{{svg "eyedropper.svg" width="24" height="24"}}</button></li>
|
||||
|
||||
|