mirror of
https://github.com/remko/waforth
synced 2024-12-27 09:59:29 +01:00
Thurtle
This commit is contained in:
parent
59b56e5491
commit
b1546ad4a3
12 changed files with 520 additions and 6 deletions
25
.github/workflows/publish-thurtle.yml
vendored
Normal file
25
.github/workflows/publish-thurtle.yml
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
name: Publish Thurtle
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
|
||||
publish:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup
|
||||
- run: sudo apt-get install awscli
|
||||
- run: yarnpkg build
|
||||
- name: aws s3 sync
|
||||
run: |
|
||||
aws configure set region eu-central-1
|
||||
aws configure set aws_access_key_id ${{secrets.AWS_ACCESS_KEY_ID}}
|
||||
aws configure set aws_secret_access_key ${{secrets.AWS_SECRET_ACCESS_KEY}}
|
||||
aws s3 sync public/thurtle/ s3://${{secrets.AWS_SITE_BUCKET}}/thurtle/
|
||||
aws s3 sync public/waforth/dist/ s3://${{secrets.AWS_SITE_BUCKET}}/waforth/dist/ --exclude "*" --include "thurtle-*" --include "logo-*.svg" --include "turtle-*.svg"
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
public/waforth/
|
||||
public/thurtle/
|
||||
src/waforth.bulkmem.wat
|
||||
src/waforth.vanilla.wat
|
||||
src/web/benchmarks/sieve/sieve-c.js
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
WAForth is a small bootstrapping Forth interpreter and dynamic compiler for
|
||||
[WebAssembly](https://webassembly.org). You can see it in action
|
||||
[here](https://mko.re/waforth/).
|
||||
[in an interactive Forth console](https://mko.re/waforth/), and in [a Logo-like Turtle graphics language](https://mko.re/thurtle/).
|
||||
|
||||
It is [entirely written in (raw)
|
||||
WebAssembly](https://github.com/remko/waforth/blob/master/src/waforth.wat), and
|
||||
|
|
27
build-web.js
27
build-web.js
|
@ -30,9 +30,7 @@ function withWatcher(config, handleBuildFinished = () => {}, port = 8880) {
|
|||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
// Doing this first, because this may do some ES5 transformations
|
||||
await handleBuildFinished(result);
|
||||
|
||||
watchClients.forEach((res) => res.write("data: update\n\n"));
|
||||
watchClients.length = 0;
|
||||
}
|
||||
|
@ -62,16 +60,20 @@ let buildConfig = {
|
|||
path.join(__dirname, "src", "web", "tests", "tests"),
|
||||
path.join(__dirname, "src", "web", "benchmarks", "benchmarks"),
|
||||
path.join(__dirname, "src", "web", "examples", "prompt", "prompt"),
|
||||
path.join(__dirname, "src", "web", "thurtle", "thurtle"),
|
||||
],
|
||||
entryNames: dev ? "[name]" : "[name]-c$[hash]",
|
||||
assetNames: "[name]-c$[hash]",
|
||||
// target: "es6",
|
||||
outdir: path.join(__dirname, "public/waforth/dist"),
|
||||
publicPath: "/waforth/dist",
|
||||
external: ["fs", "stream", "util", "events"],
|
||||
minify: !dev,
|
||||
loader: {
|
||||
".wasm": "binary",
|
||||
".js": "jsx",
|
||||
".fs": "text",
|
||||
".svg": "file",
|
||||
},
|
||||
define: {
|
||||
WAFORTH_VERSION: watch
|
||||
|
@ -118,9 +120,19 @@ async function handleBuildFinished(result) {
|
|||
["tests", "public/waforth/tests"],
|
||||
["benchmarks", "public/waforth/benchmarks"],
|
||||
["prompt", "public/waforth/examples/prompt"],
|
||||
["thurtle", "public/thurtle", true],
|
||||
];
|
||||
for (const [base, outpath] of indexes) {
|
||||
for (const [base, outpath, bs] of indexes) {
|
||||
let index = INDEX_TEMPLATE.replace(/\$BASE/g, base);
|
||||
if (bs) {
|
||||
index = index.replace(
|
||||
"<body>",
|
||||
`<body>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
`
|
||||
);
|
||||
}
|
||||
for (const [out] of Object.entries(result.metafile.outputs)) {
|
||||
const outfile = path.basename(out);
|
||||
const sourcefile = outfile.replace(/-c\$[^.]+\./, ".");
|
||||
|
@ -145,7 +157,14 @@ if (watch) {
|
|||
}
|
||||
try {
|
||||
const data = await fs.promises.readFile(f);
|
||||
res.writeHead(200);
|
||||
res.writeHead(
|
||||
200,
|
||||
req.url.endsWith(".svg")
|
||||
? {
|
||||
"Content-Type": "image/svg+xml",
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
res.end(data);
|
||||
} catch (err) {
|
||||
res.writeHead(404);
|
||||
|
|
83
src/web/thurtle/examples.ts
Normal file
83
src/web/thurtle/examples.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
export type Example = {
|
||||
name: string;
|
||||
program: string;
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
name: "Square",
|
||||
program: `
|
||||
200 FORWARD
|
||||
90 RIGHT
|
||||
200 FORWARD
|
||||
90 RIGHT
|
||||
200 FORWARD
|
||||
90 RIGHT
|
||||
200 FORWARD
|
||||
90 RIGHT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "Square (w/ LOOP)",
|
||||
program: `
|
||||
: SQUARE ( n -- )
|
||||
4 0 DO
|
||||
DUP FORWARD
|
||||
90 RIGHT
|
||||
LOOP
|
||||
;
|
||||
|
||||
250 SQUARE
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "Seeker",
|
||||
program: `
|
||||
: SEEKER ( n -- )
|
||||
4 0 DO
|
||||
DUP FORWARD
|
||||
PENUP
|
||||
DUP FORWARD
|
||||
PENDOWN
|
||||
DUP FORWARD
|
||||
90 RIGHT
|
||||
LOOP
|
||||
;
|
||||
|
||||
100 SEEKER
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "Flower",
|
||||
program: `
|
||||
: SQUARE ( n -- )
|
||||
4 0 DO
|
||||
DUP FORWARD
|
||||
90 RIGHT
|
||||
LOOP
|
||||
;
|
||||
|
||||
: FLOWER ( n -- )
|
||||
12 0 DO
|
||||
DUP SQUARE
|
||||
30 RIGHT
|
||||
LOOP
|
||||
;
|
||||
|
||||
250 FLOWER
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "Spiral (Recursive)",
|
||||
program: `
|
||||
: SPIRAL ( n -- )
|
||||
DUP 1 < IF EXIT THEN
|
||||
DUP FORWARD
|
||||
20 RIGHT
|
||||
95 100 */ RECURSE
|
||||
;
|
||||
|
||||
140 SPIRAL
|
||||
`,
|
||||
},
|
||||
].map((e) => ({ ...e, program: e.program.trimStart() }));
|
52
src/web/thurtle/thurtle.css
Normal file
52
src/web/thurtle/thurtle.css
Normal file
|
@ -0,0 +1,52 @@
|
|||
.root {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.main {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
|
||||
textarea {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.world {
|
||||
border: thin solid rgb(171, 208, 166);
|
||||
border-radius: 0.5em;
|
||||
background-color: rgb(221, 248, 221);
|
||||
width: 100%;
|
||||
max-height: 350px;
|
||||
}
|
||||
|
||||
.world path {
|
||||
stroke: black;
|
||||
fill: white;
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
textarea.program {
|
||||
font-family: monospace;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.output {
|
||||
font-size: 0.8em;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.left-pane {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.right-pane {
|
||||
flex: 2;
|
||||
}
|
9
src/web/thurtle/thurtle.fs
Normal file
9
src/web/thurtle/thurtle.fs
Normal file
|
@ -0,0 +1,9 @@
|
|||
: FORWARD ( n -- ) S" forward" SCALL ;
|
||||
: BACKWARD ( n -- ) NEGATE FORWARD ;
|
||||
: LEFT ( n -- ) S" rotate" SCALL ;
|
||||
: RIGHT ( n -- ) NEGATE LEFT ;
|
||||
: PENDOWN ( -- ) 1 S" pen" SCALL ;
|
||||
: PENUP ( -- ) 0 S" pen" SCALL ;
|
||||
: HIDETURTLE ( -- ) 0 S" turtle" SCALL ;
|
||||
: SHOWTURTLE ( -- ) 1 S" turtle" SCALL ;
|
||||
: SETPENSIZE ( n -- ) S" setpensize" SCALL ;
|
264
src/web/thurtle/thurtle.ts
Normal file
264
src/web/thurtle/thurtle.ts
Normal file
|
@ -0,0 +1,264 @@
|
|||
import WAForth from "waforth";
|
||||
import "./thurtle.css";
|
||||
import turtle from "./turtle.svg";
|
||||
import logo from "../../../doc/logo.svg";
|
||||
import thurtleFS from "./thurtle.fs";
|
||||
import examples from "./examples";
|
||||
|
||||
const rootEl = document.createElement("div");
|
||||
rootEl.className = "root";
|
||||
rootEl.innerHTML = `<nav class="navbar navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img
|
||||
data-hook="logo"
|
||||
width="30"
|
||||
height="24"
|
||||
class="d-inline-block align-text-top"
|
||||
/>
|
||||
Thurtle
|
||||
</a>
|
||||
|
||||
<a
|
||||
role="button"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#helpModal"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-question-circle-fill"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247zm2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="main d-flex flex-column p-2">
|
||||
<div class="container mt-2 text-muted">
|
||||
<p>
|
||||
Interactive, Logo-like Turtle graphics language, using Forth (powered by
|
||||
<a href="https://github.com/remko/waforth">WAForth</a>).
|
||||
</p>
|
||||
</div>
|
||||
<div class="d-flex flex-row flex-grow-1">
|
||||
<div class="left-pane d-flex flex-column">
|
||||
<select class="form-select mb-2" data-hook="examples"></select>
|
||||
<textarea autofocus class="form-control program"></textarea>
|
||||
<button data-hook="run" class="btn btn-primary mt-2">Run</button>
|
||||
</div>
|
||||
<div class="d-flex flex-column ms-3 right-pane">
|
||||
<svg
|
||||
class="world"
|
||||
viewBox="0 0 1000 1000"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g transform="translate(500 500)">
|
||||
<g id="paths">
|
||||
</g>
|
||||
<image id="turtle" width="50" height="50" href="${turtle}" />
|
||||
</g>
|
||||
</svg>
|
||||
<form>
|
||||
<div class="form-group mt-3">
|
||||
<label>Output</label>
|
||||
<pre class="output"></pre>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="modal fade"
|
||||
id="helpModal"
|
||||
tabindex="-1"
|
||||
aria-labelledby="helpModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="helpModalLabel">Help</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>The following words for moving the turtle are available:
|
||||
<ul>
|
||||
<li><code>FORWARD ( n -- )</code>: Move forward by <code>n</code>.</li>
|
||||
<li><code>BACKWARD ( n -- )</code>: Move backward by <code>n</code>.</li>
|
||||
<li><code>LEFT ( n -- )</code>: Turn left by <code>n</code> degrees.</li>
|
||||
<li><code>RIGHT ( n -- )</code>: Turn right by <code>n</code> degrees.</li>
|
||||
<li><code>PENUP ( -- )</code>: Disable drawing while moving.</li>
|
||||
<li><code>PENDOWN ( -- )</code>: Enable drawing while moving.</li>
|
||||
<li><code>SETPENSIZE ( n -- )</code>: Set the width of the drawed strokes (default: 5).</li>
|
||||
<li><code>HIDETURTLE ( -- )</code>: Hide the turtle.</li>
|
||||
<li><code>SHOWTURTLE ( -- )</code>: Show the turtle.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
document.body.appendChild(rootEl);
|
||||
|
||||
const turtleEl = document.getElementById("turtle")!;
|
||||
let pathEl: SVGPathElement;
|
||||
const patshEl = document.getElementById("paths")!;
|
||||
const runButtonEl = document.querySelector(
|
||||
"button[data-hook=run]"
|
||||
)! as HTMLButtonElement;
|
||||
const examplesEl = document.querySelector(
|
||||
"[data-hook=examples]"
|
||||
)! as HTMLSelectElement;
|
||||
const programEl = document.querySelector("textarea") as HTMLTextAreaElement;
|
||||
const outputEl = document.querySelector("pre") as HTMLPreElement;
|
||||
(document.querySelector("img[data-hook=logo]")! as HTMLImageElement).src = logo;
|
||||
|
||||
enum PenState {
|
||||
Up = 0,
|
||||
Down = 1,
|
||||
}
|
||||
|
||||
let rotation = 0;
|
||||
let position = { x: 0, y: 0 };
|
||||
let pen = PenState.Down;
|
||||
let visible = true;
|
||||
|
||||
function newPathEl() {
|
||||
pathEl = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
pathEl.setAttribute("stroke-width", "5");
|
||||
pathEl.setAttribute("d", "M 0 0");
|
||||
patshEl.appendChild(pathEl);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
position.x = position.y = 0;
|
||||
rotation = 270;
|
||||
pen = PenState.Down;
|
||||
patshEl.innerHTML = "";
|
||||
newPathEl();
|
||||
outputEl.innerHTML = "";
|
||||
updateTurtle();
|
||||
}
|
||||
|
||||
function updateTurtle() {
|
||||
turtleEl.style.display = visible ? "block" : "none";
|
||||
turtleEl.setAttribute(
|
||||
"transform",
|
||||
`rotate(${rotation} ${position.x} ${position.y}) translate(${
|
||||
position.x - 25
|
||||
} ${position.y - 25})`
|
||||
);
|
||||
}
|
||||
|
||||
function rotate(deg: number) {
|
||||
rotation = rotation + deg;
|
||||
updateTurtle();
|
||||
}
|
||||
|
||||
function forward(d: number) {
|
||||
const dx = d * Math.cos((rotation * Math.PI) / 180.0);
|
||||
const dy = d * Math.sin((rotation * Math.PI) / 180.0);
|
||||
pathEl.setAttribute(
|
||||
"d",
|
||||
pathEl.getAttribute("d")! +
|
||||
" " +
|
||||
[pen === PenState.Down ? "l" : "m", dx, dy].join(" ")
|
||||
);
|
||||
|
||||
position.x += dx;
|
||||
position.y += dy;
|
||||
updateTurtle();
|
||||
}
|
||||
|
||||
function setPen(s: PenState) {
|
||||
pen = s;
|
||||
}
|
||||
|
||||
function setPenSize(s: number) {
|
||||
newPathEl();
|
||||
pathEl.setAttribute("stroke-width", s + "");
|
||||
}
|
||||
|
||||
function setVisible(b: boolean) {
|
||||
visible = b;
|
||||
updateTurtle();
|
||||
}
|
||||
|
||||
function loadExample(name: string) {
|
||||
programEl.value = examples.find((e) => e.name === name)!.program;
|
||||
examplesEl.value = name;
|
||||
}
|
||||
|
||||
for (const ex of examples) {
|
||||
const option = document.createElement("option");
|
||||
option.appendChild(document.createTextNode(ex.name));
|
||||
option.value = ex.name;
|
||||
examplesEl.appendChild(option);
|
||||
}
|
||||
examplesEl.addEventListener("change", (ev) => {
|
||||
loadExample((ev.target! as HTMLSelectElement).value);
|
||||
});
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
runButtonEl.disabled = true;
|
||||
reset();
|
||||
|
||||
const forth = new WAForth();
|
||||
await forth.load();
|
||||
forth.bind("forward", (stack) => {
|
||||
forward(stack.pop());
|
||||
});
|
||||
forth.bind("rotate", (stack) => {
|
||||
rotate(-stack.pop());
|
||||
});
|
||||
forth.bind("pen", (stack) => {
|
||||
setPen(stack.pop());
|
||||
});
|
||||
forth.bind("turtle", (stack) => {
|
||||
setVisible(stack.pop() != 0);
|
||||
});
|
||||
forth.bind("setpensize", (stack) => {
|
||||
setPenSize(stack.pop());
|
||||
});
|
||||
forth.interpret(thurtleFS);
|
||||
forth.onEmit = (c) => outputEl.appendChild(document.createTextNode(c));
|
||||
forth.interpret(programEl.value);
|
||||
programEl.focus();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
runButtonEl.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
runButtonEl.addEventListener("click", () => run());
|
||||
document.addEventListener("keydown", (ev) => {
|
||||
if (ev.key == "Enter" && (ev.metaKey || ev.ctrlKey)) {
|
||||
run();
|
||||
}
|
||||
});
|
||||
|
||||
reset();
|
||||
|
||||
loadExample(examples[1].name);
|
52
src/web/thurtle/turtle.svg
Normal file
52
src/web/thurtle/turtle.svg
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="200" height="200" viewBox="0, 0, 200, 200">
|
||||
<g id="g76">
|
||||
<g>
|
||||
<g id="g10">
|
||||
<path d="M74.297,129.567 L74.297,118.502 L52.398,118.502 C35.906,118.502 22.537,131.871 22.537,148.364 L22.537,148.364 L55.501,148.364 C65.882,148.364 74.297,139.948 74.297,129.567 z" fill="#3FAE2A" id="path2"/>
|
||||
<path d="M27.863,148.364 C30.209,134.212 42.498,123.419 57.315,123.419 L74.298,123.419 L74.298,118.502 L52.398,118.502 C35.907,118.502 22.537,131.871 22.537,148.364 z" fill="#379624" id="path4"/>
|
||||
<path d="M51.774,141.919 C50.158,141.919 48.847,140.608 48.847,138.992 C48.847,137.376 50.158,136.065 51.774,136.065 C53.391,136.065 54.701,137.376 54.701,138.992 C54.701,140.608 53.391,141.919 51.774,141.919 z" fill="#4A9C3B" id="circle6"/>
|
||||
<path d="M39.696,145.055 C37.214,145.055 35.201,143.043 35.201,140.56 C35.201,138.078 37.214,136.065 39.696,136.065 C42.178,136.065 44.191,138.078 44.191,140.56 C44.191,143.043 42.178,145.055 39.696,145.055 z" fill="#4A9C3B" id="circle8"/>
|
||||
</g>
|
||||
<g id="g20">
|
||||
<path d="M74.297,70.433 C74.297,60.052 65.881,51.636 55.501,51.636 L22.537,51.636 C22.537,68.129 35.906,81.497 52.398,81.497 L74.297,81.497 z" fill="#3FAE2A" id="path12"/>
|
||||
<path d="M27.863,51.636 C30.209,65.788 42.498,76.581 57.315,76.581 L74.298,76.581 L74.298,81.497 L52.398,81.497 C35.907,81.497 22.537,68.128 22.537,51.636 L27.863,51.636 z" fill="#379624" id="path14"/>
|
||||
<path d="M38.129,66.293 C35.517,66.293 33.399,64.176 33.399,61.564 C33.399,58.951 35.517,56.834 38.129,56.834 C40.741,56.834 42.859,58.951 42.859,61.564 C42.859,64.176 40.741,66.293 38.129,66.293 z" fill="#4A9C3B" id="circle16"/>
|
||||
<path d="M56.054,72.853 C52.504,72.853 49.627,69.975 49.627,66.426 C49.627,62.876 52.504,59.999 56.054,59.999 C59.603,59.999 62.481,62.876 62.481,66.426 C62.481,69.975 59.603,72.853 56.054,72.853 z" fill="#4A9C3B" id="circle18"/>
|
||||
</g>
|
||||
<g id="g32">
|
||||
<path d="M132.009,158.953 C139.35,151.612 139.35,139.711 132.009,132.37 L124.185,124.546 L108.7,140.031 C97.039,151.693 97.039,170.599 108.7,182.261 z" fill="#3FAE2A" id="path22"/>
|
||||
<path d="M112.466,178.496 C104.118,166.831 105.176,150.508 115.653,140.032 L127.661,128.024 L124.185,124.547 L108.7,140.032 C97.039,151.694 97.039,170.601 108.7,182.262 z" fill="#379624" id="path24"/>
|
||||
<path d="M125.153,159.618 C123.155,159.618 121.534,157.998 121.534,155.999 C121.534,154 123.155,152.38 125.153,152.38 C127.152,152.38 128.773,154 128.773,155.999 C128.773,157.998 127.152,159.618 125.153,159.618 z" fill="#4A9C3B" id="circle26"/>
|
||||
<path d="M112.728,162.623 C111.53,162.623 110.559,161.652 110.559,160.454 C110.559,159.256 111.53,158.284 112.728,158.284 C113.926,158.284 114.898,159.256 114.898,160.454 C114.898,161.652 113.926,162.623 112.728,162.623 z" fill="#4A9C3B" id="circle28"/>
|
||||
<path d="M116.565,168.192 C115.367,168.192 114.396,167.221 114.396,166.022 C114.396,164.824 115.367,163.853 116.565,163.853 C117.763,163.853 118.735,164.824 118.735,166.022 C118.735,167.221 117.763,168.192 116.565,168.192 z" fill="#4A9C3B" id="circle30"/>
|
||||
</g>
|
||||
<g id="g42">
|
||||
<path d="M132.009,67.629 C139.35,60.288 139.35,48.388 132.009,41.047 L108.7,17.738 L108.7,17.738 C97.039,29.399 97.039,48.307 108.7,59.968 L124.185,75.453 z" fill="#3FAE2A" id="path34"/>
|
||||
<path d="M112.466,21.504 C104.118,33.169 105.176,49.492 115.653,59.968 L127.661,71.976 L124.185,75.453 L108.7,59.968 C97.039,48.306 97.039,29.399 108.7,17.738 z" fill="#379624" id="path36"/>
|
||||
<path d="M115.115,39.976 C113.116,39.976 111.496,38.355 111.496,36.356 C111.496,34.358 113.116,32.737 115.115,32.737 C117.114,32.737 118.734,34.358 118.734,36.356 C118.734,38.355 117.114,39.976 115.115,39.976 z" fill="#4A9C3B" id="circle38"/>
|
||||
<path d="M125.153,52.704 C122.541,52.704 120.424,50.586 120.424,47.974 C120.424,45.362 122.541,43.244 125.153,43.244 C127.766,43.244 129.883,45.362 129.883,47.974 C129.883,50.586 127.766,52.704 125.153,52.704 z" fill="#4A9C3B" id="circle40"/>
|
||||
</g>
|
||||
<g id="g64">
|
||||
<path d="M198.819,100 L198.819,100 C198.819,88.506 189.501,79.188 178.007,79.188 L145.74,79.188 L145.74,120.811 L178.007,120.811 C189.501,120.812 198.819,111.494 198.819,100 z" fill="#3FAE2A" id="path44"/>
|
||||
<g id="g50">
|
||||
<path d="M176.969,128.504 C172.721,128.504 169.277,125.06 169.277,120.812 C169.277,116.564 172.721,113.12 176.969,113.12 C181.218,113.12 184.662,116.564 184.662,120.812 C184.662,125.06 181.218,128.504 176.969,128.504 z" fill="#FFFFFF" id="circle46"/>
|
||||
<path d="M176.969,124.658 C174.846,124.658 173.124,122.936 173.124,120.812 C173.124,118.688 174.846,116.966 176.969,116.966 C179.093,116.966 180.815,118.688 180.815,120.812 C180.815,122.936 179.093,124.658 176.969,124.658 z" fill="#5F4637" id="circle48"/>
|
||||
</g>
|
||||
<g id="g56">
|
||||
<path d="M176.969,86.88 C172.721,86.88 169.277,83.436 169.277,79.188 C169.277,74.94 172.721,71.496 176.969,71.496 C181.218,71.496 184.662,74.94 184.662,79.188 C184.662,83.436 181.218,86.88 176.969,86.88 z" fill="#FFFFFF" id="circle52"/>
|
||||
<path d="M176.969,83.034 C174.846,83.034 173.124,81.312 173.124,79.188 C173.124,77.064 174.846,75.342 176.969,75.342 C179.093,75.342 180.815,77.064 180.815,79.188 C180.815,81.312 179.093,83.034 176.969,83.034 z" fill="#5F4637" id="circle54"/>
|
||||
</g>
|
||||
<path d="M164.547,113.327 C163.349,113.327 162.378,112.356 162.378,111.158 C162.378,109.96 163.349,108.988 164.547,108.988 C165.745,108.988 166.716,109.96 166.716,111.158 C166.716,112.356 165.745,113.327 164.547,113.327 z" fill="#4A9C3B" id="circle58"/>
|
||||
<path d="M164.547,96.339 C161.935,96.339 159.817,94.222 159.817,91.61 C159.817,88.998 161.935,86.88 164.547,86.88 C167.159,86.88 169.277,88.998 169.277,91.61 C169.277,94.222 167.159,96.339 164.547,96.339 z" fill="#4A9C3B" id="circle60"/>
|
||||
<path d="M178.242,110.301 C174.662,110.301 171.76,107.398 171.76,103.818 C171.76,100.238 174.662,97.336 178.242,97.336 C181.822,97.336 184.725,100.238 184.725,103.818 C184.725,107.398 181.822,110.301 178.242,110.301 z" fill="#4A9C3B" id="circle62"/>
|
||||
</g>
|
||||
<path d="M40.993,100 L40.993,100 C40.993,73.814 62.221,52.585 88.408,52.585 L111.5,52.585 C137.687,52.585 158.915,73.813 158.915,100 L158.915,100 C158.915,126.186 137.687,147.415 111.5,147.415 L88.408,147.415 C62.221,147.415 40.993,126.186 40.993,100 z" fill="#A75A29" id="path66"/>
|
||||
<path d="M49.971,100 C49.971,78.806 67.214,61.564 88.407,61.564 L111.499,61.564 C132.694,61.564 149.936,78.806 149.936,99.999 C149.936,121.193 132.694,138.436 111.499,138.436 L88.407,138.436 C67.214,138.436 49.971,121.194 49.971,100 z" fill="#E89E6C" id="path68"/>
|
||||
<path d="M99.954,118.78 C89.582,118.78 81.174,110.372 81.174,100 C81.174,89.628 89.582,81.22 99.954,81.22 C110.326,81.22 118.734,89.628 118.734,100 C118.734,110.372 110.326,118.78 99.954,118.78 z" fill="#BD8158" id="circle70"/>
|
||||
<path d="M101.465,61.564 L101.465,138.436 L98.442,138.436 L98.442,61.564 z" fill="#BD8158" id="rect72"/>
|
||||
<path d="M122.239,73.717 C126.628,73.717 130.462,71.383 132.599,67.896 C126.537,63.899 119.288,61.564 111.499,61.564 L110.085,61.564 C110.085,68.276 115.527,73.717 122.239,73.717 z M89.822,61.564 L88.407,61.564 C80.62,61.564 73.37,63.898 67.309,67.895 C69.445,71.382 73.279,73.717 77.669,73.717 C84.381,73.717 89.822,68.276 89.822,61.564 z M125.651,99.999 C125.651,110.997 130.622,120.83 138.434,127.386 C145.528,120.409 149.936,110.711 149.936,99.999 C149.936,89.288 145.527,79.59 138.434,72.613 C130.621,79.169 125.651,89.002 125.651,99.999 z M132.599,132.105 C130.462,128.618 126.628,126.283 122.239,126.283 C115.527,126.283 110.085,131.724 110.085,138.437 L111.499,138.437 C119.288,138.436 126.537,136.102 132.599,132.105 z M77.669,126.283 C73.279,126.283 69.445,128.617 67.309,132.104 C73.37,136.101 80.62,138.436 88.407,138.436 L89.822,138.436 C89.822,131.724 84.381,126.283 77.669,126.283 z M74.257,99.999 C74.257,89.002 69.286,79.169 61.473,72.613 C54.38,79.59 49.971,89.288 49.971,99.999 C49.971,110.711 54.38,120.409 61.473,127.386 C69.287,120.83 74.257,110.997 74.257,99.999 z" fill="#BD8158" id="path74"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.3 KiB |
4
src/web/types/forth.d.ts
vendored
Normal file
4
src/web/types/forth.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
declare module "*.fs" {
|
||||
const value: string;
|
||||
export = value;
|
||||
}
|
4
src/web/types/images.d.ts
vendored
Normal file
4
src/web/types/images.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
declare module "*.svg" {
|
||||
const value: string;
|
||||
export = value;
|
||||
}
|
|
@ -6,7 +6,8 @@
|
|||
"target": "es2015",
|
||||
"typeRoots": ["./src/web/types"],
|
||||
"types": ["node"],
|
||||
"baseUrl": "./src/web"
|
||||
"baseUrl": "./src/web",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["./src/web"],
|
||||
"exclude": ["node_modules", "dist", "build"]
|
||||
|
|
Loading…
Reference in a new issue