add brush preview
This commit is contained in:
parent
4430d6d125
commit
37520f34f7
|
@ -26,6 +26,10 @@ body {
|
||||||
line-height: var(--line-height);
|
line-height: var(--line-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fonts */
|
/* Fonts */
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { CodeEditor, getLineStart } from "rkgk/code-editor.js";
|
import { CodeEditor, getLineStart } from "rkgk/code-editor.js";
|
||||||
|
import { BrushPreview } from "rkgk/brush-preview.js";
|
||||||
|
|
||||||
const defaultBrush = `
|
const defaultBrush = `
|
||||||
-- This is your brush.
|
-- This is your brush.
|
||||||
|
|
57
static/brush-preview.js
Normal file
57
static/brush-preview.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { Pixmap } from "rkgk/haku.js";
|
||||||
|
|
||||||
|
export class BrushPreview extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.canvas = this.appendChild(document.createElement("canvas"));
|
||||||
|
this.ctx = this.canvas.getContext("2d");
|
||||||
|
this.#resizeCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
#resizeCanvas() {
|
||||||
|
this.canvas.width = this.clientWidth;
|
||||||
|
this.canvas.height = this.clientHeight;
|
||||||
|
|
||||||
|
if (this.pixmap != null) {
|
||||||
|
this.pixmap.destroy();
|
||||||
|
}
|
||||||
|
this.pixmap = new Pixmap(this.canvas.width, this.canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
#renderBrushInner(haku) {
|
||||||
|
haku.resetVm();
|
||||||
|
|
||||||
|
let evalResult = haku.evalBrush();
|
||||||
|
if (evalResult.status != "ok") {
|
||||||
|
return { status: "error", phase: "eval", result: evalResult };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pixmap.clear();
|
||||||
|
let renderResult = haku.renderValue(
|
||||||
|
this.pixmap,
|
||||||
|
this.canvas.width / 2,
|
||||||
|
this.canvas.height / 2,
|
||||||
|
);
|
||||||
|
if (renderResult.status != "ok") {
|
||||||
|
return { status: "error", phase: "render", result: renderResult };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ctx.putImageData(this.pixmap.getImageData(), 0, 0);
|
||||||
|
|
||||||
|
return { status: "ok" };
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBrush(haku) {
|
||||||
|
this.classList.remove("error");
|
||||||
|
let result = this.#renderBrushInner(haku);
|
||||||
|
if (result.status == "error") {
|
||||||
|
this.classList.add("error");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("rkgk-brush-preview", BrushPreview);
|
|
@ -32,7 +32,7 @@ main {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: [left] 1fr [right-resize] auto [right] var(--right-width);
|
grid-template-columns: [left] 1fr [right-resize] auto [right] minmax(0, var(--right-width));
|
||||||
|
|
||||||
/* Pass all events through. Children may receive events as normal. */
|
/* Pass all events through. Children may receive events as normal. */
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@ -43,23 +43,34 @@ main {
|
||||||
|
|
||||||
&>.right {
|
&>.right {
|
||||||
grid-column: right / right;
|
grid-column: right / right;
|
||||||
height: fit-content;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
max-height: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: row;
|
grid-template-rows: minmax(0, min-content);
|
||||||
justify-content: stretch;
|
grid-template-columns: [floating] max-content [resize] min-content [docked] auto;
|
||||||
|
|
||||||
&>rkgk-resize-handle {
|
padding-left: 16px;
|
||||||
flex-shrink: 0;
|
|
||||||
height: auto;
|
pointer-events: none;
|
||||||
|
|
||||||
|
&>* {
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&>rkgk-brush-editor {
|
&>rkgk-resize-handle {
|
||||||
height: auto;
|
pointer-events: auto;
|
||||||
overflow: auto;
|
}
|
||||||
flex-grow: 1;
|
|
||||||
|
&>.docked>rkgk-brush-editor {
|
||||||
|
max-height: 100%;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&>.floating>rkgk-brush-preview {
|
||||||
|
width: 128px;
|
||||||
|
height: 128px;
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,6 +186,8 @@ rkgk-code-editor {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
&>.layer {
|
&>.layer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -259,14 +272,29 @@ rkgk-code-editor {
|
||||||
resize: none;
|
resize: none;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:has(textarea:focus) {
|
||||||
|
outline: 1px solid var(--color-brand-blue);
|
||||||
|
outline-offset: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Brush editor */
|
/* Brush editor */
|
||||||
|
|
||||||
rkgk-brush-editor {
|
rkgk-brush-editor.rkgk-panel {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&>.text-area {
|
&>.text-area {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -283,8 +311,8 @@ rkgk-brush-editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
&>.error-header {
|
&>.error-header {
|
||||||
margin-top: 1em;
|
margin: 0;
|
||||||
margin-bottom: 0;
|
margin-top: 0.5em;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: var(--color-error);
|
color: var(--color-error);
|
||||||
}
|
}
|
||||||
|
@ -296,6 +324,36 @@ rkgk-brush-editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Brush preview */
|
||||||
|
|
||||||
|
rkgk-brush-preview {
|
||||||
|
--checkerboard-light: #f2f2f2;
|
||||||
|
--checkerboard-dark: #e1e1e1;
|
||||||
|
--checkerboard-size: 64px;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
background:
|
||||||
|
repeating-conic-gradient(var(--checkerboard-light) 0% 25%, var(--checkerboard-dark) 0% 50%)
|
||||||
|
50% 50% / var(--checkerboard-size) var(--checkerboard-size);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
&>canvas {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "(error)";
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Welcome screen */
|
/* Welcome screen */
|
||||||
|
|
||||||
rkgk-welcome {
|
rkgk-welcome {
|
||||||
|
@ -359,7 +417,8 @@ rkgk-connection-status {
|
||||||
}
|
}
|
||||||
|
|
||||||
&.icon {
|
&.icon {
|
||||||
padding: 4px 4px;
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
|
|
|
@ -16,6 +16,7 @@ let main = document.querySelector("main");
|
||||||
let canvasRenderer = main.querySelector("rkgk-canvas-renderer");
|
let canvasRenderer = main.querySelector("rkgk-canvas-renderer");
|
||||||
let reticleRenderer = main.querySelector("rkgk-reticle-renderer");
|
let reticleRenderer = main.querySelector("rkgk-reticle-renderer");
|
||||||
let brushEditor = main.querySelector("rkgk-brush-editor");
|
let brushEditor = main.querySelector("rkgk-brush-editor");
|
||||||
|
let brushPreview = main.querySelector("rkgk-brush-preview");
|
||||||
let welcome = main.querySelector("rkgk-welcome");
|
let welcome = main.querySelector("rkgk-welcome");
|
||||||
let connectionStatus = main.querySelector("rkgk-connection-status");
|
let connectionStatus = main.querySelector("rkgk-connection-status");
|
||||||
|
|
||||||
|
@ -260,10 +261,25 @@ function readUrl(urlString) {
|
||||||
updateUrl(session, canvasRenderer.viewport),
|
updateUrl(session, canvasRenderer.viewport),
|
||||||
);
|
);
|
||||||
|
|
||||||
brushEditor.renderHakuResult("Compilation", currentUser.setBrush(brushEditor.code));
|
function compileBrush() {
|
||||||
|
let compileResult = currentUser.setBrush(brushEditor.code);
|
||||||
|
brushEditor.renderHakuResult("Compilation", compileResult);
|
||||||
|
|
||||||
|
if (compileResult.status != "ok") return;
|
||||||
|
|
||||||
|
let previewResult = brushPreview.renderBrush(currentUser.haku);
|
||||||
|
if (previewResult.status == "error") {
|
||||||
|
brushEditor.renderHakuResult(
|
||||||
|
previewResult.phase == "eval" ? "Evaluation" : "Rendering",
|
||||||
|
previewResult.result,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileBrush();
|
||||||
brushEditor.addEventListener(".codeChanged", async () => {
|
brushEditor.addEventListener(".codeChanged", async () => {
|
||||||
flushPlotQueue();
|
flushPlotQueue();
|
||||||
brushEditor.renderHakuResult("Compilation", currentUser.setBrush(brushEditor.code));
|
compileBrush();
|
||||||
session.sendSetBrush(brushEditor.code);
|
session.sendSetBrush(brushEditor.code);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ export class ResizeHandle extends HTMLElement {
|
||||||
let mouseDown = await listen([this, "mousedown"]);
|
let mouseDown = await listen([this, "mousedown"]);
|
||||||
let startingSize = this.size;
|
let startingSize = this.size;
|
||||||
if (mouseDown.button == 0) {
|
if (mouseDown.button == 0) {
|
||||||
|
mouseDown.preventDefault();
|
||||||
this.classList.add("dragging");
|
this.classList.add("dragging");
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
import "rkgk/live-reload.js";
|
import "rkgk/live-reload.js";
|
||||||
|
|
||||||
import "rkgk/brush-editor.js";
|
import "rkgk/brush-editor.js";
|
||||||
|
import "rkgk/brush-preview.js";
|
||||||
import "rkgk/canvas-renderer.js";
|
import "rkgk/canvas-renderer.js";
|
||||||
import "rkgk/connection-status.js";
|
import "rkgk/connection-status.js";
|
||||||
import "rkgk/framework.js";
|
import "rkgk/framework.js";
|
||||||
|
@ -59,16 +60,22 @@
|
||||||
<hr>
|
<hr>
|
||||||
<a href="/docs/rkgk.html">Manual</a>
|
<a href="/docs/rkgk.html">Manual</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right">
|
<div class="right">
|
||||||
|
<div class="floating">
|
||||||
|
<rkgk-brush-preview></rkgk-brush-preview>
|
||||||
|
</div>
|
||||||
<rkgk-resize-handle
|
<rkgk-resize-handle
|
||||||
data-direction="vertical"
|
data-direction="vertical"
|
||||||
data-target="panels-overlay"
|
data-target="panels-overlay"
|
||||||
data-target-property="--right-width"
|
data-target-property="--right-width"
|
||||||
data-init-size="384"
|
data-init-size="512"
|
||||||
data-min-size="128"></rkgk-resize-handle>
|
data-min-size="384"></rkgk-resize-handle>
|
||||||
|
<div class="docked">
|
||||||
<rkgk-brush-editor></rkgk-brush-editor>
|
<rkgk-brush-editor></rkgk-brush-editor>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<rkgk-welcome>
|
<rkgk-welcome>
|
||||||
<dialog name="welcome-dialog" class="rkgk-panel">
|
<dialog name="welcome-dialog" class="rkgk-panel">
|
||||||
|
|
Loading…
Reference in a new issue