-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
505 additions
and
166 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
src/app/evolvingLSystems/evolving-l-systems.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<h1> | ||
Evolving L Systems | ||
</h1> | ||
|
||
|
||
<p> | ||
This is a Turtle which will draw some evolutions of L-Systems. | ||
</p> | ||
|
||
<p> | ||
This means that you will tell a turtle where it shall go to step-by-step only defining a rule, | ||
and this rule will be re-interpreted for every expansion. | ||
</p> | ||
<p> | ||
Therefore here are some background informations: | ||
</p> | ||
|
||
<p> | ||
The turtle has a direction it looks at and a position. | ||
You can the turtle to turn left or right by a fixed degree. | ||
And you can tell the turtle to take one step into the direction. | ||
|
||
At the end I will show you which way the turtle took. | ||
</p> | ||
|
||
<p> | ||
A L-System is a Grammar which has one function. | ||
And the function will lead to a combination of 'F', '-', '+', '[' or ']' | ||
End for each expansion any 'F' will be replaced by the value of the function. | ||
|
||
e.g. the Function F -> F - F will lead to F-F by one expansion, F-F - F-F by two expansion and so forth. | ||
</p> | ||
|
||
<div class="was-validated"> | ||
<p> | ||
<label> | ||
The right side of the Function: F -> | ||
<span class="input-group is-invalid" style="width: 10rem; display: inline-flex;"> | ||
<input type="text" class="form-control" [(ngModel)]="LGrammar" [min]="1" pattern="[fF\-\+\[\] ]*"/> | ||
|
||
</span> | ||
</label> | ||
</p> | ||
<p> | ||
<label> | ||
the rotation angle of the turtle in degrees: | ||
<span class="input-group is-invalid" style="width: 10rem; display: inline-flex;"> | ||
<input type="number" class="form-control" [(ngModel)]="angle" [min]="1" [max]="360"/> | ||
|
||
</span> | ||
</label> | ||
</p> | ||
<p> | ||
<label> | ||
how often shall I expand the grammar?: | ||
<span class="input-group is-invalid" style="width: 10rem; display: inline-flex;"> | ||
<input type="number" class="form-control" [(ngModel)]="expansions" [min]="0"/> | ||
</span> | ||
</label> | ||
</p> | ||
<p> | ||
<label> | ||
how far does the turtle go with every step ?: | ||
<span class="input-group is-invalid" style="width: 10rem; display: inline-flex;"> | ||
<input type="number" class="form-control" [(ngModel)]="stepSize" [min]="1"/> | ||
</span> | ||
</label> | ||
</p> | ||
</div> | ||
|
||
<p class="mb-5"> | ||
<button type="button" | ||
class="btn btn-outline-primary" | ||
(click)="onStart()"> | ||
Start: | ||
</button> | ||
</p> | ||
|
||
|
||
<p style="height: 2rem;"></p> | ||
|
||
<canvas #turtleCanvas [attr.width]="canvasWidth" [attr.height]="canvasHeight"> | ||
</canvas> |
Empty file.
129 changes: 129 additions & 0 deletions
129
src/app/evolvingLSystems/evolving-l-systems.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; | ||
import { Turtle } from './turtle'; | ||
|
||
@Component({ | ||
selector: 'app-evolving-l-systems', | ||
templateUrl: './evolving-l-systems.component.html', | ||
styleUrls: ['./evolving-l-systems.component.scss'] | ||
}) | ||
export class EvolvingLSystemsComponent implements AfterViewInit { | ||
|
||
// e.g. F + F - F -F + F | ||
public LGrammar: string = ' F + F - F -F + F'; | ||
|
||
public stepSize: number = 8; | ||
|
||
public expansions: number = 1; | ||
public angle: number = 90; | ||
|
||
@ViewChild('turtleCanvas') | ||
private turtleCanvas!: ElementRef<HTMLCanvasElement>; | ||
private canvasContext!: CanvasRenderingContext2D; | ||
|
||
public canvasWidth : number = 64; | ||
public canvasHeight : number = 64; | ||
|
||
|
||
ngAfterViewInit(): void { | ||
this.canvasContext = this.turtleCanvas?.nativeElement.getContext('2d') as CanvasRenderingContext2D; | ||
} | ||
|
||
|
||
public onStart(): void { | ||
|
||
if (this.expansions < 0) { | ||
return; | ||
} | ||
|
||
if (this.LGrammar.length == 0) { | ||
return; | ||
} | ||
|
||
const commands = this.expand(); | ||
|
||
const turtle = new Turtle(); | ||
|
||
for (let i = 0; i < commands.length; i++) { | ||
const current = commands.charAt(i); | ||
|
||
switch (current) { | ||
case 'F': | ||
case 'f': | ||
turtle.step(); | ||
break; | ||
case '-': | ||
turtle.rotate( -1 * this.angle); | ||
break; | ||
case '+': | ||
turtle.rotate( this.angle); | ||
break; | ||
|
||
// TODO: add stack for self-similarity | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
this.drawPath( turtle); | ||
|
||
} | ||
|
||
private expand(): string { | ||
let result = this.LGrammar; | ||
|
||
for (let i = 0; i < this.expansions; i++) { | ||
result = result.replaceAll( 'F', this.LGrammar); | ||
} | ||
|
||
return result.replaceAll(' ', ''); | ||
} | ||
|
||
private drawPath( turtle: Turtle) { | ||
const span = turtle.getDimension(); | ||
const padding = 1; | ||
this.canvasWidth = span.bottomRight.x - span.topLeft.x + 2 * padding; // add padding | ||
this.canvasWidth = Math.max( this.canvasWidth, 1 + 2 * padding); | ||
|
||
this.canvasHeight = span.topLeft.y - span.bottomRight.y + 2 * padding ; | ||
this.canvasHeight = Math.max( this.canvasHeight, 1 + 2 * padding ); | ||
|
||
|
||
this.canvasWidth *= this.stepSize; | ||
this.canvasHeight *= this.stepSize; | ||
|
||
|
||
|
||
setTimeout( () => { | ||
|
||
this.canvasContext.clearRect( 0, 0, this.canvasWidth, this.canvasHeight); | ||
|
||
this.canvasContext.beginPath(); | ||
|
||
// shift turtle away from dimension minimum | ||
const origin = { x : Math.abs( span.topLeft.x) + padding, y: Math.abs( span.bottomRight.y) + padding}; | ||
|
||
this.canvasContext.moveTo( origin.x * this.stepSize , this.canvasHeight - origin.y * this.stepSize); | ||
|
||
turtle.getPath().reduce( (prev, cur, idx, arr) => { | ||
if ( !!prev && prev.x == cur.x && prev.y == cur.y || cur.teleported) { | ||
return cur; | ||
} | ||
const posX = (cur.x + origin.x) * this.stepSize; | ||
const posY = this.canvasHeight - (cur.y + origin.y) * this.stepSize; // flip y since canvas y direction is from top to bottom | ||
|
||
this.canvasContext.lineTo( posX, posY); | ||
|
||
return cur; | ||
}); | ||
|
||
this.canvasContext.stroke(); | ||
|
||
}, 0); | ||
} | ||
|
||
} | ||
|
||
interface IPosition { | ||
x: number, | ||
y: number, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { EvolvingLSystemsComponent } from './evolving-l-systems.component'; | ||
import { NgModule } from '@angular/core'; | ||
import { CommonModule } from '@angular/common'; | ||
import { RouterModule, Routes } from '@angular/router'; | ||
import { FormsModule } from '@angular/forms'; | ||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; | ||
|
||
const routes: Routes = [ | ||
{ | ||
path: '', | ||
component: EvolvingLSystemsComponent, | ||
} | ||
]; | ||
|
||
@NgModule({ | ||
declarations: [ | ||
EvolvingLSystemsComponent, | ||
], | ||
imports: [ | ||
CommonModule, | ||
FormsModule, | ||
NgbTooltipModule, | ||
RouterModule.forChild(routes), | ||
], | ||
exports: [ | ||
RouterModule, | ||
] | ||
}) | ||
export class EvolvingLSystemsModule { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import * as _ from 'lodash'; | ||
|
||
export class Turtle { | ||
|
||
private pos: IPosition; | ||
|
||
public getPosition(): IPosition { | ||
return _.cloneDeep( this.pos); | ||
} | ||
|
||
private dimension: Rectangle = { | ||
topLeft: { x: 0, y:0}, | ||
bottomRight: { x: 0, y:0}, | ||
} | ||
|
||
public getDimension(): Rectangle { | ||
return _.cloneDeep( this.dimension); | ||
} | ||
|
||
private path: Array<IPosition>; | ||
|
||
public getPath() { | ||
return _.cloneDeep( this.path); | ||
} | ||
|
||
constructor() { | ||
this.pos = { x: 0, y: 0, dir: 0}; | ||
this.path = [ _.cloneDeep( this.pos)]; | ||
} | ||
|
||
public rotate( degree: number) { | ||
this.pos.dir += degree; | ||
this.path.push ( _.cloneDeep( this.pos)); | ||
} | ||
|
||
public step() { | ||
const radians = this.pos.dir / 360 * Math.PI * 2; | ||
|
||
const stepSizeX = Math.cos( radians); | ||
const stepSizeY = Math.sin( radians); | ||
|
||
this.pos.x += stepSizeX, | ||
this.pos.y += stepSizeY, | ||
|
||
this.path.push ( _.cloneDeep( this.pos)); | ||
this.updateDimension(); | ||
} | ||
|
||
|
||
public teleport( newPosition : IPosition) { | ||
this.pos = _.cloneDeep( newPosition); | ||
this.pos.teleported = true; | ||
this.updateDimension(); | ||
this.path.push ( _.cloneDeep( this.pos)); | ||
} | ||
|
||
private updateDimension() { | ||
this.dimension.topLeft.x = Math.min( this.pos.x, this.dimension.topLeft.x); | ||
this.dimension.topLeft.y = Math.max( this.pos.y, this.dimension.topLeft.y); | ||
this.dimension.bottomRight.x = Math.max( this.pos.x, this.dimension.bottomRight.x); | ||
this.dimension.bottomRight.y = Math.min( this.pos.y, this.dimension.bottomRight.y); | ||
} | ||
|
||
} | ||
|
||
export interface IPosition { | ||
/** | ||
* x-Position | ||
*/ | ||
x: number, | ||
|
||
/** | ||
* y-Position | ||
*/ | ||
y: number, | ||
|
||
/** direction in degrees */ | ||
dir: number, | ||
|
||
teleported?: boolean | ||
} | ||
|
||
export interface Rectangle { | ||
topLeft: { x: number, y: number}, | ||
bottomRight: { x: number, y: number}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters