Skip to content

Commit

Permalink
updating writers to allow user to get Blob data directly
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjacobson committed Sep 6, 2024
1 parent a7943ad commit c95108b
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 44 deletions.
293 changes: 293 additions & 0 deletions examples/streamlinesAnimation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
<!doctype html>
<html>
<head>
<title>Streamlines Animation Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
<script src="../dist/stitch.global.js"></script>
<script>window.Stitch || document.write('<script src="https://unpkg.com/@stitchables/stitchjs/dist/stitch.global.js">\x3C/script>')</script>
</head>
<body>
<script>


class QT {
constructor(bb, cap) {
this.bb = bb;
this.cap = cap;
this.ps = [];
this.cs = [];
this.div = false;
}
sub() {
for (let i of [-1, 1]) for (let j of [-1, 1]) {
this.cs.push(new QT(new BB([this.bb.p[0] + i * 0.5 * this.bb.d[0], this.bb.p[1] + j * 0.5 * this.bb.d[1]], [0.5 * this.bb.d[0], 0.5 * this.bb.d[1]]), this.cap));
}
for (let p of this.ps) for (let c of this.cs) if (c.bb.cont(p)) c.ins(p);
this.ps = [];
this.div = true;
}
ins(p) {
if (!this.bb.cont(p.p)) return false;
if (this.ps.length < this.cap && !this.div) {
this.ps.push(p);
return true;
} else {
if (!this.div) this.sub();
for (let c of this.cs) if (c.ins(p)) return true;
}
}
q(rng, f) {
if (!f) f = [];
if (!this.bb.inter(rng)) return;
else {
for (let p of this.ps) if (rng.cont(p)) f.push(p);
if (this.div) for (let c of this.cs) c.q(rng, f);
}
return f;
}
}

class BB {
constructor(p, d) {
this.p = p;
this.d = d;
}
cont(p) {
for (let i of [0, 1]) if (p[i] < this.p[i] - this.d[i] || p[i] >= this.p[i] + this.d[i]) return false;
return true;
}
inter(bb) {
for (let i of [0, 1]) if (bb.p[i] - bb.d[i] > this.p[i] + this.d[i] || bb.p[i] + bb.d[i] < this.p[i] - this.d[i]) return false;
return true;
}
}

class ESS {
constructor(d, vf, sf, dsf) {
this.d = d;
this.vf = vf;
this.sf = sf;
this.dsf = dsf;
this.qt = new QT(new BB([0.5 * d[0], 0.5 * d[1]], [0.5 * d[0], 0.5 * d[1]]), 4);
this.sl = [];
}
rst() {
this.qt = new QT(new BB([0.5 * this.d[0], 0.5 * this.d[1]], [0.5 * this.d[0], 0.5 * this.d[1]]), 4);
this.sl = [];
}
csl(s) {
if (this.cp(s) || this.sl.length == 0) {
let tl = this.cssl(s);
this.sl.push(tl);
for (let p of tl) this.qt.ins({p: p, i: this.sl.length - 1});
for (let p of tl) {
let o = this.vf(p);
o = this.nrm(o);
o = this.rot(o);
o = this.mult(o, this.sf(p));
this.csl([p[0] + o[0], p[1] + o[1]]);
this.csl([p[0] - o[0], p[1] - o[1]]);
}
}
return this.sl;
}
cssl(s) {
let tqt = new QT(new BB([0.5 * this.d[0], 0.5 * this.d[1]], [0.5 * this.d[0], 0.5 * this.d[1]]), 4);
let l = 0;
tqt.ins({p: s, l: l});
let tl = [s];
let fl = false;
while(true) {
let lp = tl[tl.length - 1];
let d = this.vf(lp);
if (fl) d = this.mult(d, -1);
let np = [lp[0] + d[0], lp[1] + d[1]];
// let di = dist(0, 0, d[0], d[1]);
let di = Math.sqrt(d[0] * d[0] + d[1] * d[1]);
if (!this.cp(np, tqt, di, l, lp)) {
if (!fl) {
tl.reverse();
l = 0;
fl = true;
} else return tl;
} else {
tl.push(np);
l += fl ? -di : di;
tqt.ins({p: np, l: l});
}
}
}
// check point
cp(p, tqt, di, l, pp) {
if (!this.cb(p)) return false;
if (di) if (!this.csn(di)) return false;
if (!this.csp(p, pp)) return false;
if (pp) if (!this.csi(tqt, l, p)) return false;
return true;
}
// check boundary
cb(p) {
for (let i of [0, 1]) if (p[i] < 0 || p[i] > this.d[i]) return false;
return true;
}
// check sink
csn(di) { return di > 0.001; }
// check separation
csp(p, pp) {
let s = this.sf(p);
let ds = 1;
if (pp) ds = this.dsf(p);
let ns = this.qt.q(new BB(p, [s, s]));
// for (let n of ns) if (dist(p[0], p[1], n.p[0], n.p[1]) < ds * s) return false;
for (let n of ns) if (Math.sqrt((n.p[0] - p[0]) * (n.p[0] - p[0]) + (n.p[1] - p[1]) * (n.p[1] - p[1])) < ds * s) return false;
return true;
}
csi(tqt, l, p) {
let s = this.sf(p);
let ns = tqt.q(new BB(p, [s, s]));
for (let n of ns) {
// let cd = dist(p[0], p[1], n.p[0], n.p[1]);
let cd = Math.sqrt((n.p[0] - p[0]) * (n.p[0] - p[0]) + (n.p[1] - p[1]) * (n.p[1] - p[1]));
if (cd < s && Math.abs(l - n.l) > s) return false;
}
return true;
}
nrm(v) {
// let l = dist(0, 0, v[0], v[1]);
let l = Math.sqrt(v[0] * v[0] + v[1] * v[1]);
return [v[0] / l, v[1] / l];
}
rot(v) { return [v[1], -v[0]]; }
mult(v, m) { return [m * v[0], m * v[1]]; }
getSl() {
let output = [];
for (let sli = 0; sli < this.sl.length; sli++) {
let sl = this.sl[sli];
let sld = [];
for (let p of sl) {
let sf = this.sf(p);
let dsf = this.dsf(p);
let ns = this.qt.q(new BB(p, [sf, sf]));
// let minDist = min(min(p[0], this.d[0] - p[0]), min(p[1], this.d[1] - p[1]));
let minDist = Infinity;
for (let n of ns) {
if (sli != n.i) {
// let currDist = dist(p[0], p[1], n.p[0], n.p[1]);
let currDist = Math.sqrt((n.p[0] - p[0]) * (n.p[0] - p[0]) + (n.p[1] - p[1]) * (n.p[1] - p[1]))
if (currDist < minDist) minDist = currDist;
}
}
let tCoeff = minDist >= sf ? 1 : (minDist - sf * dsf) / (sf - sf * dsf);
sld.push({x: p[0], y: p[1], t: tCoeff});
}
output.push(sld);
}
return output;
}
}




let frameCount = 50;
let [w, h] = [1000, 1000];
let patterns = [];
for (let f = 0; f < frameCount; f++) {
let pattern = new Stitch.Core.Pattern(w, h);
let scn = function(px, py, sx, sy, omi, oma) {
let x = 0.25 * (px + 10000) / sx;
let y = 0.25 * (py + 10000) / sy;
let z = 0.2 * Math.cos(2 * Math.PI * f / frameCount) + 100;
let w = 0.2 * Math.sin(2 * Math.PI * f / frameCount) + 100;
return Stitch.Math.Utils.map(Stitch.Math.noise(x, y, z, w), 0, 1, omi, oma);
}
let vf = function(p) {
let a = scn(p[0], p[1], 0.3 * w, 0.3 * h, 0, 4 * Math.PI);
let r = 0.003 * Math.min(w, h);
let o = Stitch.Math.Utils.map(f, 0, frameCount, 0, Math.PI);
// let o = 0;
return [r * Math.cos(a + o), r * Math.sin(a + o)];
};
let sf = function(p) { return 0.03 * Math.min(w, h); }
let dsf = function(p) { return 0.5; }
let ess = new ESS([w, h], vf, sf, dsf);
for (let x = 0; x < w; x += 10) {
for (let y = 0; y < h; y += 10) {
ess.csl([x, y]);
}
}
let sld = ess.getSl();

let thread = pattern.addThread(0, 0, 0);
for (let sl of sld) {
// let polyline = new Stitch.Math.Polyline(false);
// for (let p of sl) {
// polyline.addVertex(p.x, p.y);
// }
// thread.addRun(new Stitch.Runs.Run(polyline, 1.5));
if (sl.length > 3) {
let classicSatin = new Stitch.Runs.ClassicSatin(1);
for (let i = 1; i < sl.length - 1; i++) {
let prev = new Stitch.Math.Vector(sl[i - 1].x, sl[i - 1].y);
let curr = new Stitch.Math.Vector(sl[i - 0].x, sl[i - 0].y);
let next = new Stitch.Math.Vector(sl[i + 1].x, sl[i + 1].y);
let n = curr.subtract(prev).add(next.subtract(curr)).divide(2)
.rotate(0.5 * Math.PI).normalized();
classicSatin.addVector(curr.add(n.multiply(8 * sl[i].t * sl[i].t + 3)));
classicSatin.addVector(curr.subtract(n.multiply(8 * sl[i].t * sl[i].t + 3)));
}
thread.addRun(classicSatin);
}
}
patterns.push(pattern);
}

let svg = Stitch.Graphics.getSvg(patterns[0], window.innerWidth, window.innerHeight);
svg.setAttribute('style', 'margin: auto; position: absolute; inset: 0;');
document.body.append(svg);

function animate() {
c++;
svg.remove();
svg = Stitch.Graphics.getSvg(patterns[c % frameCount], window.innerWidth, window.innerHeight);
svg.setAttribute('style', 'margin: auto; position: absolute; inset: 0;');
document.body.append(svg);
requestAnimationFrame(() => { setTimeout(animate, 120); });
}
let c = 0;
animate();

const [dstZip, pesZip] = [new JSZip(), new JSZip()];
for (let i = 0; i < frameCount; i++) {
const wMm = Stitch.Math.Utils.inToMm(3.8);
const hMm = Stitch.Math.Utils.inToMm(3.8);
const dstData = Stitch.IO.getData(patterns[i], wMm, hMm, `essAnimation-${i}.dst`);
const pesData = Stitch.IO.getData(patterns[i], wMm, hMm, `essAnimation-${i}.pes`);
dstZip.file(`essAnimation-${i}.dst`, dstData, { binary: true });
pesZip.file(`essAnimation-${i}.pes`, pesData, { binary: true });
}

window.addEventListener("keydown", async (e) => {
if (e.code === "KeyD") {
console.log(dstZip);
dstZip.generateAsync({ type: "blob" })
.then((content) => {
const a = document.createElement('a');
a.href = window.URL.createObjectURL(content);
a.download = 'essAnimationDst.zip';
a.click();
});
} else if (e.code === "KeyP") {
pesZip.generateAsync({ type: "blob" })
.then((content) => {
const a = document.createElement('a');
a.href = URL.createObjectURL(content);
a.download = 'essAnimationPes.zip';
a.click();
});
}
});

</script>
</body>
</html>
4 changes: 2 additions & 2 deletions src/IO/Writers/DSTWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class DSTWriter implements IWriter {
}
return new Uint8Array([b0, b1, b2]);
}
write(stitches: IResolvedStitches, filename: string): void {
write(stitches: IResolvedStitches, filename: string): (number | string | Uint8Array)[] {
this.data = [];
this.data.push(`LA:${Utils.padRight(filename.split('.')[0] || '', 16, ' ')}\r`);
this.data.push(`ST:${Utils.padLeft(stitches.stitchCount.toString(), 7, ' ')}\r`);
Expand Down Expand Up @@ -169,7 +169,7 @@ export class DSTWriter implements IWriter {
this.data.push(' '.repeat(387));
this.encodeStitches(stitches);
this.data.push(this.encodeRecord(0, 0, 'END'));
Utils.saveData(this.data, filename);
return this.data;
}
encodeStitches(stitches: IResolvedStitches): void {
let xx = 0;
Expand Down
5 changes: 4 additions & 1 deletion src/IO/Writers/IWriter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { IResolvedStitches } from '../../Core/Pattern';

export interface IWriter {
write: (resolvedStitches: IResolvedStitches, filename: string) => void;
write: (
resolvedStitches: IResolvedStitches,
filename: string,
) => (number | string | Uint8Array)[];
}
7 changes: 5 additions & 2 deletions src/IO/Writers/PESWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export class PESWriter implements IWriter {
writeInt(value: number, bytes: number, endien = 'L'): void {
this.data.push(Utils.integerToBinary(value, bytes, endien));
}
write(stitchPattern: IResolvedStitches, filename: string) {
write(
stitchPattern: IResolvedStitches,
filename: string,
): (number | string | Uint8Array)[] {
this.data = [];
this.writeString('#PES0001');
this.writeBytes(new Uint8Array([0x16]));
Expand Down Expand Up @@ -75,7 +78,7 @@ export class PESWriter implements IWriter {
);
for (let i = 0; i < 3; i++) graphicsOffsetValue[i] = graphicsOffsetBytes[i];
for (let i = 0; i < stitchPattern.threads.length + 1; i++) this.writePECGraphics();
Utils.saveData(this.data, filename);
return this.data;
}
encodeCommand(command: string, dx: number, dy: number): number {
switch (command) {
Expand Down
5 changes: 2 additions & 3 deletions src/IO/Writers/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ export const Utils = {
alert(`[Stitch.IO.Utils.integerToBinary] Unexpected endien value: ${endien}`);
return byteArray;
},
saveData: function (data: (number | string | Uint8Array)[], filename: string): void {
const blob = new Blob(data as BlobPart[], { type: 'application/octet-stream' });
const url = window.URL.createObjectURL(blob);
saveData: function (data: Blob, filename: string): void {
const url = window.URL.createObjectURL(data);
const a = document.createElement('a');
document.body.appendChild(a);
a.href = url;
Expand Down
Loading

0 comments on commit c95108b

Please sign in to comment.