Lightningbeam/src/main.js

388 lines
9.8 KiB
JavaScript

// const { invoke } = window.__TAURI__.core;
let greetInputEl;
let greetMsgEl;
let rootPane;
let canvases = [];
let mode = "draw"
let minSegmentSize = 5;
let tools = {
select: {
icon: "/assets/select.svg",
},
transform: {
icon: "/assets/transform.svg",
},
draw: {
icon: "/assets/draw.svg"
},
rectangle: {
icon: "/assets/rectangle.svg"
},
polygon: {
icon: "assets/polygon.svg"
}
}
let mouseEvent;
let context = {
mouseDown: false,
swatches: [
"#000000",
"#FFFFFF",
"#FF0000",
"#FFFF00",
"#00FF00",
"#00FFFF",
"#0000FF",
"#FF00FF",
]
}
function uuidv4() {
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
(+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
);
}
function vectorDist(a, b) {
return Math.sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y))
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
class Curve {
constructor(cp1x, cp1y, cp2x, cp2y, x, y) {
this.cp1x = cp1x;
this.cp1y = cp1y;
this.cp2x = cp2x;
this.cp2y = cp2y;
this.x = x;
this.y = y;
}
}
class Frame {
constructor() {
this.keys = {}
}
}
class Shape {
constructor(startx, starty, fillStyle, strokeStyle, filled=true, stroked=true) {
this.startx = startx;
this.starty = starty;
this.curves = [];
this.fillStyle = fillStyle;
this.strokeStyle = strokeStyle;
this.filled = filled;
this.stroked = stroked;
}
addCurve(curve) {
this.curves.push(curve)
}
}
class GraphicsObject {
constructor() {
this.x = 0;
this.y = 0;
this.rotation = 0;
this.scale = 1;
this.idx = uuidv4()
this.frames = [new Frame()]
this.currentFrame = 0;
this.children = []
this.shapes = []
}
draw(context) {
let ctx = context.ctx;
if (this.currentFrame>=this.frames.length) {
this.currentFrame = 0;
}
for (let child of this.children) {
let idx = child.idx
child.x = this.frames[this.currentFrame][idx].x;
child.y = this.frames[this.currentFrame][idx].y;
child.rotation = this.frames[this.currentFrame][idx].rotation;
child.scale = this.frames[this.currentFrame][idx].scale;
child.draw(context)
}
for (let shape of this.shapes) {
ctx.beginPath()
ctx.moveTo(shape.startx, shape.starty)
for (let curve of shape.curves) {
ctx.bezierCurveTo(curve.cp1x, curve.cp1y, curve.cp2x, curve.cp2y, curve.x, curve.y)
}
if (shape.filled) {
ctx.fillStyle = shape.fillStyle
ctx.fill()
}
if (shape.stroked) {
ctx.strokeStyle = shape.strokeStyle
ctx.stroke()
}
}
}
addShape(shape) {
this.shapes.push(shape)
}
}
let root = new GraphicsObject();
let shp = new Shape(100,100,'blue', 'black')
shp.addCurve(new Curve(150,150,150,150,200,100))
root.addShape(shp)
context.activeObject = root
async function greet() {
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
greetMsgEl.textContent = await invoke("greet", { name: greetInputEl.value });
// splitPane(rootPane, 50, true)
}
window.addEventListener("DOMContentLoaded", () => {
// greetInputEl = document.querySelector("#greet-input");
// greetMsgEl = document.querySelector("#greet-msg");
rootPane = document.querySelector("#root")
rootPane.appendChild(toolbar())
rootPane.addEventListener("mousemove", (e) => {
mouseEvent = e;
})
// document.querySelector("#greet-form").addEventListener("submit", (e) => {
// e.preventDefault();
// greet();
// });
splitPane(rootPane, 10, true)
});
function stage() {
let stage = document.createElement("canvas")
let scroller = document.createElement("div")
stage.className = "stage"
stage.width = 1500
stage.height = 1000
scroller.className = "scroll"
canvases.push(stage)
scroller.appendChild(stage)
stage.addEventListener("mousedown", (e) => {
let mouse = getMousePos(stage, e)
switch (mode) {
case "draw":
context.mouseDown = true
context.activeShape = new Shape(mouse.x, mouse.y, context.fillStyle, context.strokeStyle, true, true)
context.activeObject.addShape(context.activeShape)
context.lastMouse = mouse
console.log(stage)
break;
default:
break;
}
context.lastMouse = mouse
updateUI()
})
stage.addEventListener("mouseup", (e) => {
context.mouseDown = false
let mouse = getMousePos(stage, e)
switch (mode) {
case "draw":
if (context.activeShape) {
if (vectorDist(mouse, context.lastMouse) > minSegmentSize) {
let midpoint = {x: (mouse.x+context.lastMouse.x)/2, y: (mouse.y+context.lastMouse.y)/2}
context.activeShape.addCurve(new Curve(midpoint.x, midpoint.y, midpoint.x, midpoint.y, mouse.x, mouse.y))
}
context.activeShape = undefined
}
break;
default:
break;
}
context.lastMouse = mouse
updateUI()
})
stage.addEventListener("mousemove", (e) => {
let mouse = getMousePos(stage, e)
switch (mode) {
case "draw":
if (context.activeShape) {
if (vectorDist(mouse, context.lastMouse) > minSegmentSize) {
let midpoint = {x: (mouse.x+context.lastMouse.x)/2, y: (mouse.y+context.lastMouse.y)/2}
context.activeShape.addCurve(new Curve(midpoint.x, midpoint.y, midpoint.x, midpoint.y, mouse.x, mouse.y))
}
}
break;
default:
break;
}
context.lastMouse = mouse
updateUI()
})
return scroller
}
function toolbar() {
let tools_scroller = document.createElement("div")
tools_scroller.className = "toolbar"
for (let tool in tools) {
let toolbtn = document.createElement("button")
toolbtn.className = "toolbtn"
let icon = document.createElement("img")
icon.className = "icon"
icon.src = tools[tool].icon
toolbtn.appendChild(icon)
tools_scroller.appendChild(toolbtn)
}
let tools_break = document.createElement("div")
tools_break.className = "horiz_break"
tools_scroller.appendChild(tools_break)
let fillColor = document.createElement("input")
let strokeColor = document.createElement("input")
fillColor.className = "color-field"
strokeColor.className = "color-field"
fillColor.value = "#ffffff"
strokeColor.value = "#000000"
fillColor.addEventListener('click', e => {
Coloris({
el: ".color-field",
selectInput: true,
focusInput: true,
theme: 'default',
swatches: context.swatches,
defaultColor: '#ffffff',
onChange: (color) => {
context.fillStyle = color;
}
})
})
strokeColor.addEventListener('click', e => {
Coloris({
el: ".color-field",
selectInput: true,
focusInput: true,
theme: 'default',
swatches: context.swatches,
defaultColor: '#000000',
onChange: (color) => {
context.strokeStyle = color;
}
})
})
// Fill and stroke colors use the same set of swatches
fillColor.addEventListener("change", e => {
context.swatches.unshift(fillColor.value)
if (context.swatches.length>12) context.swatches.pop();
})
strokeColor.addEventListener("change", e => {
context.swatches.unshift(strokeColor.value)
if (context.swatches.length>12) context.swatches.pop();
})
tools_scroller.appendChild(fillColor)
tools_scroller.appendChild(strokeColor)
return tools_scroller
}
function createPane() {
let div = document.createElement("div")
let header = document.createElement("div")
let content = stage() // TODO: change based on type
header.className = "header"
let button = document.createElement("button")
header.appendChild(button)
let icon = document.createElement("img")
icon.className="icon"
icon.src = "/assets/stage.svg"
button.appendChild(icon)
// div.style.display = "grid";
// div.style.gridTemplateColumns = `var(--lineheight) 1fr`
// div.style.gridTemplateRows = "1fr"
// header.style.gridArea = "1 / 1 / 2 / 2"
// content.style.gridArea = "1 / 2 / 2 / 3"
div.classList = ["vertical-grid", "pane"]
header.style.flex = "0 0 var(--lineheight)"
content.style.flex = "1 1 100%"
div.appendChild(header)
div.appendChild(content)
return div
}
function splitPane(div, percent, horiz) {
let content = div.firstElementChild
let div1 = document.createElement("div")
let div2 = document.createElement("div")
div1.className = "panecontainer"
div2.className = "panecontainer"
div1.appendChild(content)
div2.appendChild(createPane())
div.appendChild(div1)
div.appendChild(div2)
// div.style.display = "grid";
// if (horiz) {
// div.classList.add("horizontal-grid")
// div.style.gridTemplateColumns = `${percent}% 1fr`
// div1.style.gridArea = "1 / 1 / 2 / 2"
// div2.style.gridArea = "1 / 2 / 2 / 3"
// } else {
// div.classList.add("vertical-grid")
// div.style.gridTemplateRows = `${percent}% 1fr`
// div1.style.gridArea = "1 / 1 / 2 / 2"
// div2.style.gridArea = "2 / 1 / 3 / 2"
// }
if (horiz) {
div.className = "horizontal-grid"
} else {
div.className = "verical-grid"
}
div1.style.flex = `0 0 ${percent}%`
div2.style.flex = `1 1 auto`
Coloris({el: ".color-field"})
updateUI()
}
function updateUI() {
for (let canvas of canvases) {
let ctx = canvas.getContext("2d")
ctx.fillStyle = "white"
ctx.fillRect(0,0,canvas.width,canvas.height)
ctx.fillStyle = "green"
// ctx.fillRect(0,0,200,200)
context.ctx = ctx;
root.draw(context)
// let mouse;
// if (mouseEvent) {
// mouse = getMousePos(canvas, mouseEvent);
// } else {
// mouse = {x: 0, y: 0}
// }
// ctx.fillRect(mouse.x, mouse.y, 50,50)
}
// requestAnimationFrame(updateUI)
}