Y2xhc3MgS29rYWNQNUV4dCB7CiAgICN0YXNrcyA9IG5ldyBNYXAoKTsKICAgYWRkKHYpIHsKICAgICAgdGhpcy4jdGFza3Muc2V0KHYpOwogICB9CiAgIHJlbW92ZSh2KSB7CiAgICAgIHRoaXMuI3Rhc2tzLmRlbGV0ZSh2KTsKICAgfQogICBnZXQgaXRlcmF0b3IoKSB7CiAgICAgIHJldHVybiB0aGlzLiN0YXNrcy5rZXlzKCk7CiAgIH0KICAgZGVsYXkocykgewogICAgICBpZiAoIXMpIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlKSk7CiAgICAgIGxldCB3YWl0ZXIgPSB7fTsKICAgICAgdGhpcy5hZGQod2FpdGVyKTsKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7CiAgICAgICAgIHdhaXRlci51cGRhdGUgPSAoKSA9PiB7CiAgICAgICAgICAgIGlmICgtLXMgPiAwKSByZXR1cm47CiAgICAgICAgICAgIHRoaXMucmVtb3ZlKHdhaXRlcik7CiAgICAgICAgICAgIHJlc29sdmUoKTsKICAgICAgICAgfTsKICAgICAgfSk7CiAgIH0KICAgc3RlcCgpIHsKICAgICAgY29uc3QgeyBpdGVyYXRvciB9ID0gdGhpczsKICAgICAgd2hpbGUgKHRydWUpIHsKICAgICAgICAgbGV0IG50ID0gaXRlcmF0b3IubmV4dCgpOwogICAgICAgICBpZiAobnQuZG9uZSkgYnJlYWs7CiAgICAgICAgIGxldCB7IHZhbHVlIH0gPSBudDsKICAgICAgICAgaWYgKHZhbHVlLmNvbnN0cnVjdG9yID09PSBGdW5jdGlvbikgewogICAgICAgICAgICB2YWx1ZSgpOwogICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgfQogICAgICAgICB2YWx1ZS51cGRhdGUgJiYgdmFsdWUudXBkYXRlKCk7CiAgICAgICAgIHZhbHVlLnJlZHJhdyAmJiB2YWx1ZS5yZWRyYXcoKTsKICAgICAgfQogICB9CiAgIHRleHQoc3RyaW5nLCB4eCwgeXksIHNpemUsIGZvbnQpIHsKICAgICAgbGV0IGluZm8gPSB0aGlzLnRleHRCb3VuZHMoc3RyaW5nLCB4eCwgeXksIHNpemUsIGZvbnQpOwogICAgICB0ZXh0Rm9udChpbmZvLmZvbnQpOwogICAgICB0ZXh0U2l6ZShpbmZvLnNpemUpOwogICAgICB0ZXh0KGluZm8uc3RyaW5nLCBpbmZvLngsIGluZm8ueSk7CiAgIH0KICAgdGV4dEJvdW5kcyhzdHJpbmcsIHh4LCB5eSwgc2l6ZSwgZm9udCkgewogICAgICBsZXQgbXlGb250ID0gZm9udDsKICAgICAgbGV0IHRleHQxID0gc3RyaW5nOwogICAgICBsZXQgZm9udFNpemVTbWFsbCA9IHNpemU7CiAgICAgIGxldCB4ID0geHg7CiAgICAgIGxldCB5ID0geXk7CiAgICAgIGxldCBib3VuZGluZ19ib3ggPSBteUZvbnQudGV4dEJvdW5kcyh0ZXh0MSwgeCwgeSwgZm9udFNpemVTbWFsbCk7CiAgICAgIHggLT0gYm91bmRpbmdfYm94Lng7CiAgICAgIHkgLT0gYm91bmRpbmdfYm94Lnk7CiAgICAgIHJldHVybiB7CiAgICAgICAgIHg6IHggKyB4eCwKICAgICAgICAgeTogeSArIHl5LAogICAgICAgICBzaXplOiBmb250U2l6ZVNtYWxsLAogICAgICAgICBmb250OiBteUZvbnQsCiAgICAgICAgIHN0cmluZzogc3RyaW5nLAogICAgICAgICByZWN0OiB7CiAgICAgICAgICAgIHg6IHggKyBib3VuZGluZ19ib3gueCwKICAgICAgICAgICAgeTogeSArIGJvdW5kaW5nX2JveC55LAogICAgICAgICAgICB3OiBib3VuZGluZ19ib3gudywKICAgICAgICAgICAgaDogYm91bmRpbmdfYm94LmgsCiAgICAgICAgIH0sCiAgICAgIH07CiAgIH0KfQpmdW5jdGlvbiBkcmF3KCkgewogICBrb2thYy5zdGVwKCk7Cn0KCgpjb25zdCBrb2thYyA9IG5ldyBLb2thY1A1RXh0KCk7CgpsZXQgbXlGb250OwpmdW5jdGlvbiBwcmVsb2FkKCkgewogICBteUZvbnQgPSBsb2FkRm9udCgiaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL2tzdG9zdC9Lb2thY1A1RXh0L3NyYy9mb250cy9Ob3RvU2Fuc0tSLUJvbGQub3RmIik7Cn0KYXN5bmMgZnVuY3Rpb24gc2V0dXAoKSB7CiAgIC8vIOuhnOyngeydgCBzZXR1cCDslYjsl5DshJzrp4wg7IaQIOuMgOuptCDrkJjrj4TroZ0g7L2U65Oc66W8IOq1rOyEse2VqAogICBsZXQgc2l6ZVdpZHRoID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiI3N0ZyIpLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLndpZHRoOwogICBsZXQgc2l6ZUhlaWdodCA9IHNpemVXaWR0aCAqIC41OwogICAvLyBzaXplV2lkdGggPSBpbm5lcldpZHRoOwogICAvLyBzaXplSGVpZ2h0ID0gaW5uZXJIZWlnaHQ7CiAgIGNyZWF0ZUNhbnZhcyhzaXplV2lkdGgsIHNpemVIZWlnaHQpOwogICBub1N0cm9rZSgpOwogICBrb2thYy5hZGQoKGUpID0+IGJhY2tncm91bmQoMjApKTsKICAga29rYWMuYWRkKChlKSA9PiB7CiAgICAgIGZpbGwoIiMwMGRkMDAiKTsKICAgICAga29rYWMudGV4dChgYWJjXG5kZWZcbu2VnOq4gGAsIDEwLCAwLCAxMzAsIG15Rm9udCk7CiAgIH0pOwogICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgIGNsYXNzIE1vdGhlciB7CiAgICAgIGNvbnN0cnVjdG9yKCkgewogICAgICAgICB0aGlzLnggPSAwOwogICAgICAgICB0aGlzLnkgPSAwOwogICAgICAgICB0aGlzLnIgPSAyMDsKICAgICAgICAgdGhpcy5hbHBoYSA9IDA7CiAgICAgICAgIHRoaXMuYWxwaGFUb2dnbGUgPSBmYWxzZTsKICAgICAgfQogICAgICB1cGRhdGUoKSB7CiAgICAgICAgIHRoaXMueCArPSAwLjE7CiAgICAgICAgIGlmICghdGhpcy5hbHBoYVRvZ2dsZSkgewogICAgICAgICAgICB0aGlzLmFscGhhKys7CiAgICAgICAgICAgIGlmICh0aGlzLmFscGhhID49IDI1NSkgdGhpcy5hbHBoYVRvZ2dsZSA9ICF0aGlzLmFscGhhVG9nZ2xlOwogICAgICAgICB9IGVsc2UgewogICAgICAgICAgICB0aGlzLmFscGhhLS07CiAgICAgICAgICAgIGlmICh0aGlzLmFscGhhIDw9IDApIHRoaXMuYWxwaGFUb2dnbGUgPSAhdGhpcy5hbHBoYVRvZ2dsZTsKICAgICAgICAgfQogICAgICB9CiAgICAgIHJlZHJhdygpIHsKICAgICAgICAgZmlsbCgyNTUsIDAsIDAsIHRoaXMuYWxwaGEpOwogICAgICAgICBjaXJjbGUodGhpcy54LCB0aGlzLnksIHRoaXMuciAqIDIpOwogICAgICB9CiAgIH0KICAgZm9yIChsZXQgaSA9IDA7IGkgPCAxMDsgaSsrKSB7CiAgICAgIGxldCBjMiA9IG5ldyBNb3RoZXIoKTsKICAgICAgYzIueCA9IGMyLnI7CiAgICAgIGMyLnkgPSBjMi5yICsgaSAqIGMyLnIgKiAyOwogICAgICBrb2thYy5hZGQoYzIpOwogICAgICBsZXQgYzMgPSB7IHI6IDEwIH07CiAgICAgIGMzLnJlZHJhdyA9IGZ1bmN0aW9uICgpIHsKICAgICAgICAgZmlsbCgyNTUsIDI1NSwgMCk7CiAgICAgICAgIGNpcmNsZShjMi54LCBjMi55LCB0aGlzLnIgKiAyKTsKICAgICAgfTsKICAgICAga29rYWMuYWRkKGMzKTsKCiAgICAgIGtva2FjLmFkZCgoKSA9PiB7CiAgICAgICAgIGxldCBib3VuZCA9IGtva2FjLnRleHRCb3VuZHMoCiAgICAgICAgICAgIGAke01hdGgucmFuZG9tKCl9YCwKICAgICAgICAgICAgYzIueCwKICAgICAgICAgICAgYzIueSwKICAgICAgICAgICAgMjAsCiAgICAgICAgICAgIG15Rm9udCwKICAgICAgICAgICAgdHJ1ZQogICAgICAgICApOwogICAgICAgICBsZXQgbWFyZ2luID0gNDsKICAgICAgICAgcHVzaCgpOwogICAgICAgICBzdHJva2UoMjU1KTsKICAgICAgICAgZmlsbCgzMCk7CiAgICAgICAgIHJlY3QoCiAgICAgICAgICAgIGMyLnIgKyBtYXJnaW4gKyBib3VuZC5yZWN0LnggLSBtYXJnaW4sCiAgICAgICAgICAgIGJvdW5kLnJlY3QueSAtIG1hcmdpbiAtIGJvdW5kLnJlY3QuaCAqIDAuNSwKICAgICAgICAgICAgYm91bmQucmVjdC53ICsgbWFyZ2luICogMiwKICAgICAgICAgICAgYm91bmQucmVjdC5oICsgbWFyZ2luICogMgogICAgICAgICApOwogICAgICAgICBwb3AoKTsKICAgICAgICAgbGV0IHsgc3RyaW5nLCB4LCB5LCBmb250LCBzaXplIH0gPSBib3VuZDsKICAgICAgICAgZmlsbCgyNTUpOwogICAgICAgICB0ZXh0Rm9udChmb250KTsKICAgICAgICAgdGV4dFNpemUoc2l6ZSk7CiAgICAgICAgIHRleHQoc3RyaW5nLCBjMi5yICsgbWFyZ2luICsgeCwgeSAtIGJvdW5kLnJlY3QuaCAqIDAuNSk7CiAgICAgIH0pOwogICB9Cn0K
class KokacP5Ext {
#tasks = new Map();
add(v) {
this.#tasks.set(v);
}
remove(v) {
this.#tasks.delete(v);
}
get iterator() {
return this.#tasks.keys();
}
delay(s) {
if (!s) return new Promise((resolve) => setTimeout(resolve));
let waiter = {};
this.add(waiter);
return new Promise((resolve) => {
waiter.update = () => {
if (--s > 0) return;
this.remove(waiter);
resolve();
};
});
}
step() {
const { iterator } = this;
while (true) {
let nt = iterator.next();
if (nt.done) break;
let { value } = nt;
if (value.constructor === Function) {
value();
continue;
}
value.update && value.update();
value.redraw && value.redraw();
}
}
text(string, xx, yy, size, font) {
let info = this.textBounds(string, xx, yy, size, font);
textFont(info.font);
textSize(info.size);
text(info.string, info.x, info.y);
}
textBounds(string, xx, yy, size, font) {
let myFont = font;
let text1 = string;
let fontSizeSmall = size;
let x = xx;
let y = yy;
let bounding_box = myFont.textBounds(text1, x, y, fontSizeSmall);
x -= bounding_box.x;
y -= bounding_box.y;
return {
x: x + xx,
y: y + yy,
size: fontSizeSmall,
font: myFont,
string: string,
rect: {
x: x + bounding_box.x,
y: y + bounding_box.y,
w: bounding_box.w,
h: bounding_box.h,
},
};
}
}
function draw() {
kokac.step();
}
const kokac = new KokacP5Ext();
let myFont;
function preload() {
myFont = loadFont("https://cdn.jsdelivr.net/gh/kstost/KokacP5Ext/src/fonts/NotoSansKR-Bold.otf");
}
async function setup() {
// 로직은 setup 안에서만 손 대면 되도록 코드를 구성함
let sizeWidth = document.querySelector("#stg").getBoundingClientRect().width;
let sizeHeight = sizeWidth * .5;
// sizeWidth = innerWidth;
// sizeHeight = innerHeight;
createCanvas(sizeWidth, sizeHeight);
noStroke();
kokac.add((e) => background(20));
kokac.add((e) => {
fill("#00dd00");
kokac.text(`abc\ndef\n한글`, 10, 0, 130, myFont);
});
//-----------------------------------------
class Mother {
constructor() {
this.x = 0;
this.y = 0;
this.r = 20;
this.alpha = 0;
this.alphaToggle = false;
}
update() {
this.x += 0.1;
if (!this.alphaToggle) {
this.alpha++;
if (this.alpha >= 255) this.alphaToggle = !this.alphaToggle;
} else {
this.alpha--;
if (this.alpha <= 0) this.alphaToggle = !this.alphaToggle;
}
}
redraw() {
fill(255, 0, 0, this.alpha);
circle(this.x, this.y, this.r * 2);
}
}
for (let i = 0; i < 10; i++) {
let c2 = new Mother();
c2.x = c2.r;
c2.y = c2.r + i * c2.r * 2;
kokac.add(c2);
let c3 = { r: 10 };
c3.redraw = function () {
fill(255, 255, 0);
circle(c2.x, c2.y, this.r * 2);
};
kokac.add(c3);
kokac.add(() => {
let bound = kokac.textBounds(
`${Math.random()}`,
c2.x,
c2.y,
20,
myFont,
true
);
let margin = 4;
push();
stroke(255);
fill(30);
rect(
c2.r + margin + bound.rect.x - margin,
bound.rect.y - margin - bound.rect.h * 0.5,
bound.rect.w + margin * 2,
bound.rect.h + margin * 2
);
pop();
let { string, x, y, font, size } = bound;
fill(255);
textFont(font);
textSize(size);
text(string, c2.r + margin + x, y - bound.rect.h * 0.5);
});
}
}