You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
7.3 KiB

2 years ago
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];
}
}
}
}