document.oncontextmenu = (e) => { e.preventDefault(); }
cloudImg = loadImage("https://inaridarkfox4231.github.io/assets/texture/cloud.png");
createCanvas(windowWidth, windowHeight, WEBGL);
IA = new foxIA.Interaction(this.canvas, {factory:(function (){
return new ShapePointer(this.width, this.height, this._renderer);
shapes = new CrossReferenceArray();
bg = createGraphics(width, height);
baseCloud = createGraphics(width, height, WEBGL);
info = createGraphics(width, height);
info.translate(width / 2, height / 2);
bgCam.camera(0,0,1,0,0,0,0,1,0);
bgCam.ortho(-width/2, width/2, -height/2, height/2, 0, 1);
setCamera(createCamera());
shapes.loop("updateMove");
drawBackground(width, height, millis()/12000, millis()/16000);
directionalLight(128, 128, 128, 0.5, 0.5, -1);
shapes.loop("drawGuide", [info]);
const TEXT_SIZE = min(width, height)*0.04;
info.textSize(min(width, height)*0.04);
info.textAlign(CENTER, CENTER);
info.text("Draw a shape.", 0, -TEXT_SIZE*0.65);
info.text("Mouse, stylus pen and touch are available.", 0, TEXT_SIZE*0.65);
info.textAlign(LEFT, TOP);
info.text(frameRate().toFixed(3), -width/2 + 5, -height/2 + 5);
image(info, -width/2, -height/2);
shapes.loopReverse("remove");
function drawBackground(w, h, s, t){
baseCloud.texture(cloudImg);
baseCloud.textureMode(NORMAL);
baseCloud.textureWrap(REPEAT);
const wRatio = w/(3*cloudImg.width);
const hRatio = h/(3*cloudImg.height);
baseCloud.vertex(-w/2, -h/2, s, t);
baseCloud.vertex(w/2, -h/2, s + wRatio, t);
baseCloud.vertex(w/2, h/2, s + wRatio, t + hRatio);
baseCloud.vertex(-w/2, h/2, s, t + hRatio);
bg.image(baseCloud, 0, 0);
bg.background(64, 128, 255);
image(bg, -width/2, -height/2);
function findCenter(vectors) {
const center = createVector();
for (let i = 0; i < vectors.length; i++) {
center.div(vectors.length);
for (let i = 0; i < vectors.length; i++) {
function createShape3D(gId, vectors){
const geom = new p5.Geometry();
const z1 = new p5.Vector(0, 0, 20);
const z2 = new p5.Vector(0, 0, -20);
let tesselateVertices = this._renderer._triangulate(verticesToArray(vectors));
tesselateVertices = arrayToVertices(tesselateVertices);
tesselateVertices = verticesFilter(tesselateVertices);
for (let i=0; i<tesselateVertices.length; i++) {
geom.vertices.push(p5.Vector.add(tesselateVertices[i], z1));
for (let i=0; i<tesselateVertices.length; i++) {
geom.vertices.push(p5.Vector.add(tesselateVertices[tesselateVertices.length-(i+1)], z2));
for (let i=0; i<tesselateVertices.length * 2; i+=3) {
geom.faces.push([i, i+1, i+2]);
index = tesselateVertices.length * 2;
for (const v of vectors) {
geom.vertices.push(p5.Vector.add(v, z1));
geom.vertices.push(p5.Vector.add(v, z2));
const len = vectors.length * 2;
for (let i=0; i<len; i+=2) {
geom.faces.push([index + i, index + i+1, index + (i+2)%len]);
geom.faces.push([index + (i+3)%len, index + (i+2)%len, index + i+1]);
geom._makeTriangleEdges()._edgesToVertices();
this._renderer.createBuffers(gId, geom);
function verticesToArray(vertices) {
for (let ver of vertices) {
array.push(ver.x, ver.y, ver.z);
for(let k=0; k<9; k++){ array.push(0); }
function arrayToVertices(array) {
for (let i=0; i<array.length; i+=12) {
vertices.push(createVector(array[i], array[i+1], array[i+2]));
function verticesFilter(vertices) {
for (let i = 0; i < vertices.length; i += 3) {
const ba = p5.Vector.sub(vertices[i+1], vertices[i]);
const bc = p5.Vector.sub(vertices[i+1], vertices[i+2]);
const cross = p5.Vector.cross(ba, bc);
if (p5.Vector.mag(cross) != 0) {
filtered.push(vertices[i], vertices[i+1], vertices[i+2]);
class ShapePointer extends foxIA.PointerPrototype{
this.shape = new ShapeMesh(this.renderer);
this.shape.initialize(this.x - this.w/2, this.y - this.h/2);
this.shape.addVertex(this.x - this.w/2, this.y - this.h/2);
this.shape = new ShapeMesh(this.renderer);
this.shape.initialize(this.x - this.w/2, this.y - this.h/2);
this.shape.addVertex(this.x - this.w/2, this.y - this.h/2);
this.position = createVector();
this.shapeColor = color(0);
this.vectors.push(createVector(x, y, 0));
if(this.completed){ return; }
const v = createVector(x, y, 0);
if (this.vectors.length > 10 && v.dist(this.vectors[0]) < 20) {
const col = hsv2rgb(Math.random(), 1, 1);
this.shapeColor = color(col.r*255, col.g*255, col.b*255);
} else if (this.vectors.length > 0 && v.dist(this.vectors[this.vectors.length - 1]) > 10) {
if(this.completed){ return; }
gr.fill(this.shapeColor);
gr.stroke(this.shapeColor);
gr.circle(this.vectors[0].x, this.vectors[0].y, 10);
for (let i = 0; i < this.vectors.length; i++) {
gr.vertex(this.vectors[i].x, this.vectors[i].y);
gr.fill(this.shapeColor);
this.vectors[this.vectors.length - 1].x,
this.vectors[this.vectors.length - 1].y,
this.position = findCenter(this.vectors);
this.movingSpeed = (random() < 0.5 ? 1 : -1) * random(0.1, 0.2);
this.rotationSpeed = (random() < 0.5 ? 1 : -1) * random(0.02, 0.05) / 16;
this.gId = `shape3D${geomId}`;
createShape3D(this.gId, this.vectors);
if(!this.completed){ return; }
this.position.y += deltaTime * this.movingSpeed
this.rotation += deltaTime * this.rotationSpeed;
const SIZE_SCALE = min(width, height);
if(this.position.y < -height*2){
this.position.x = random(-width/2, width);
this.position.y = height*2;
this.position.z -= SIZE_SCALE;
}else if(this.position.y > height*2){
this.position.x = random(-width/2, width);
this.position.y = -height*2;
this.position.z -= SIZE_SCALE;
if(this.life===0){ this.kill(); }
if(!this.completed){ return; }
ambientMaterial(this.shapeColor);
translate(this.position);
this.renderer.drawBuffers(this.gId);
class CrossReferenceArray extends Array{
element.belongingArray = this;
elementArray.forEach((element) => { this.add(element); })
let index = this.indexOf(element, 0);
loop(methodName, args = []){
if(this.length === 0){ return; }
for(let i = 0; i < this.length; i++){
this[i][methodName](...args);
loopReverse(methodName, args = []){
if(this.length === 0){ return; }
for(let i = this.length - 1; i >= 0; i--){
this[i][methodName](...args);
function hsv2rgb(h, s, v){
let _r = constrain(abs(((6 * h) % 6) - 3) - 1, 0, 1);
let _g = constrain(abs(((6 * h + 4) % 6) - 3) - 1, 0, 1);
let _b = constrain(abs(((6 * h + 2) % 6) - 3) - 1, 0, 1);
_r = _r * _r * (3 - 2 * _r);
_g = _g * _g * (3 - 2 * _g);
_b = _b * _b * (3 - 2 * _b);
result.r = v * (1 - s + s * _r);
result.g = v * (1 - s + s * _g);
result.b = v * (1 - s + s * _b);