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
256 lines
7.3 KiB
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];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
} |