class Tree{ pos: p5.Vector = p.createVector() sizeMultiplier: number = 1 stemRadius: number leafCount: number leafColors: number[][] stemColor: number[] stemHeight: number leafHeight: number leafRadius: number isSmooth: boolean isPlaceholder: boolean container: Container animationProgress: number = 0 animationDirection: number = -1 decorations: Decoration[] = [] constructor(properties: object){ if (properties == null){ this.isPlaceholder = true; } else { for (let key in properties){ this[key] = properties[key]; } } } //Always called mousePressed(){ for (let d of [...this.decorations].reverse()){ if (d.mouseIsOver){ d.mousePressed(); let index = this.decorations.findIndex(deco => deco == d); this.decorations[index] = this.decorations[this.decorations.length - 1]; this.decorations[this.decorations.length - 1] = d; break; } } } //Always called mouseReleased(){ for (let d of this.decorations){ d.mouseReleased(); } } get smoothProgress(): number{ return this.smoothStep(0, 1, this.animationProgress); } containsPosition(pos: p5.Vector, excludeStem?: boolean){ let size = game.height, x = (p.width - size) / 2; let px = pos.x, py = pos.y; let pixelColor = (p.get(px, py) as number[]).slice(0, 3); let colorIsValid = game.tree.getAllColors(excludeStem).find(c => equals(c, pixelColor)) != null; return colorIsValid && px > x && px < x + size && py > 0 && py < size - 20; } update(){ this.animationProgress += 0.05 * this.animationDirection; this.animationProgress = this.animationProgress > 1 ? 1 : this.animationProgress < 0 ? 0 : this.animationProgress; if (this.isPlaceholder){ this.pos = game.center; this.sizeMultiplier = game.height * 0.05; } else { this.pos = p5.Vector.lerp(this.container.center, game.center, this.smoothProgress); this.sizeMultiplier = p.lerp(this.container.dim.y * 0.05, game.height * 0.05, this.smoothProgress); } this.decorations.forEach((d, i) => { if (d.isToDelete){ this.decorations.splice(i, 1); if (d instanceof Ball){ d.graphics.remove(); } } }); this.decorations.forEach(d => d.update()); } display(pos?: p5.Vector, dim?: p5.Vector){ let sizeMultiplier = this.sizeMultiplier; if (pos || dim){ sizeMultiplier = dim.y * 0.05; } else { pos = this.pos; } p.push(); p.translate(pos.x, pos.y); p.scale(sizeMultiplier); if (this.isPlaceholder){ p.fill(0); p.stroke(100, 220, 100); p.strokeWeight(0.05); p.textSize(1); p.textAlign(p.CENTER, p.CENTER); p.text('Select your Christmas Tree!', 0, 0); } else { this.drawTree(); this.drawDecorations(); if (debug && this === game.tree){ p.stroke(255, 0, 0, 80); p.strokeWeight(0.05); for (let i = -10; i <= 10; i++){ p.line(-10, i, 10, i); p.line(i, -10, i, 10); } } } p.pop(); } drawTree(){ p.push(); p.translate(0, 1); p.fill(this.stemColor); p.stroke(0); p.strokeWeight(0.05); p.rect(-this.stemRadius, 0, this.stemRadius * 2, this.stemHeight); let drawLeaf = (r: number, h: number) => { if (this.isSmooth){ let cps = [ p.createVector(40, 70), p.createVector(60, 90), p.createVector(100, 130), p.createVector(-100, 130), p.createVector(-60, 90), p.createVector(-40, 70) ]; cps.forEach(c => { c.x *= r / 100; c.y *= h / 100; }); p.beginShape(); p.vertex(0, 0); p.bezierVertex(cps[0].x, cps[0].y, cps[1].x, cps[1].y, r, h); p.bezierVertex(cps[2].x, cps[2].y, cps[3].x, cps[3].y, -r, h); p.bezierVertex(cps[4].x, cps[4].y, cps[5].x, cps[5].y, 0, 0); p.endShape(); if (debug){ p.stroke(255, 0, 0); cps.forEach(c => p.ellipse(c.x, c.y, 0)); } } else { p.triangle(0, 0, r, h, -r, h); } } for (let i = 0; i < this.leafCount; i++){ let y = -i * this.leafHeight / 2; let lr = this.leafRadius * (this.leafCount - i) / this.leafCount; p.push(); p.translate(0, y); p.fill(this.leafColors[i % this.leafColors.length]); drawLeaf(lr, this.leafHeight); p.pop(); } p.pop(); } drawDecorations(){ for (let d of this.decorations){ if (d.needsValidation){ if (!d.hasValidPosition){ d.isToDelete = true; } d.needsValidation = false; } } for (let d of this.decorations){ if (released && d.isBoundToMouse){ d.needsValidation = true; d.isBoundToMouse = false; if (d instanceof Chain){ d.p1IsBoundToMouse = false; d.p2IsBoundToMouse = false; } } d.display(); } } getAllColors(withoutStem?: boolean): number[][]{ let colors = []; colors.push(...this.leafColors, [0, 0, 0]); if (!withoutStem){ colors.push(this.stemColor); } return colors; } smoothStep(start: number, end: number, t: number): number{ function clamp(x: number, lowLimit: number, upLimit: number): number{ if (x < lowLimit) x = lowLimit; if (x > upLimit) x = upLimit; return x; } t = clamp((t - start) / (end - start), 0, 1); return t * t * t * (t * (t * 6 - 15) + 10); } toJSON(index){ let list = [ "stemRadius", "leafCount", "leafColors", "stemColor", "stemHeight", "leafHeight", "leafRadius", "isSmooth", "isPlaceholder", "decorations" ] let obj = {}; for (let item of list){ obj[item] = this[item]; } return obj; } restoreFrom(rawTree: object){ for (let key in rawTree){ if (key === 'decorations'){ this[key] = [] rawTree[key].forEach(rawDeco => { let deco = Decoration.restoreFrom(rawDeco); this[key].push(deco); }); } else { this[key] = rawTree[key]; } } } }