UNPKG

plotboilerplate

Version:

A simple javascript plotting boilerplate for 2d stuff.

207 lines (168 loc) 7.88 kB
/** * @classdesc The irregular hexagon tile from the Girih set. * * @requires Bounds * @requires Circle * @requires GirihTile * @requires Line * @requires Polygon * @requires TileType * @requires Vertex * * @author Ikaros Kappler * @date 2013-11-28 * @modified 2014-04-05 Ikaros Kappler (member array outerTilePolygons added). * @modified 2015-03-19 Ikaros Kappler (added toSVG()). * @modified 2020-10-31 Refactored to work with PlotBoilerplate. * @modified 2020-11-13 Ported from vanilla JS to TypeScript. * @version 2.0.1-alpha * @file GirihHexagon * @public **/ import { Circle } from "../../Circle"; import { GirihTile, TileType } from "./GirihTile"; import { Line } from "../../Line"; import { Polygon } from "../../Polygon"; import { Vertex } from "../../Vertex"; export class GirihHexagon extends GirihTile { /** * @constructor * @extends GirihTile * @name GirihHexagon * @param {Vertex} position * @param {number} edgeLength */ constructor( position:Vertex, edgeLength?:number ) { super( position, edgeLength, TileType.IRREGULAR_HEXAGON ); // Overwrite the default symmetries: // the hexagon tile has a 180° symmetry (5/10 * 360°) this.uniqueSymmetries = 5; // Init the actual decahedron shape with the passed size let pointA:Vertex = new Vertex(0,0); let pointB:Vertex = pointA; const startPoint:Vertex = pointA; let oppositePoint:Vertex = null; this.addVertex( pointB ); // TODO: use radians here const angles:Array<number> = [ 0.0, 72.0, 144.0, 144.0, 72.0 // 144.0 ]; let theta:number = 0.0; for( var i = 0; i < angles.length; i++ ) { theta += (180.0 - angles[i]); pointA = pointB; // center of rotation pointB = pointB.clone(); pointB.x -= this.edgeLength; pointB.rotate( theta * (Math.PI/180.0), pointA ); this.addVertex( pointB ); if( i == 2 ) oppositePoint = pointB; } // Center and move to desired position const move:Vertex = new Vertex( (oppositePoint.x - startPoint.x)/2.0, (oppositePoint.y - startPoint.y)/2.0 ); for( var i = 0; i < this.vertices.length; i++ ) { this.vertices[i].add( position ).sub( move ); } this.textureSource.min.x = 77/500.0; this.textureSource.min.y = 11/460.0; this.textureSource.max.x = this.textureSource.min.x + 205/500.0; this.textureSource.max.y = this.textureSource.min.y + 150/460.0; this.baseBounds = this.getBounds(); this._buildInnerPolygons( this.edgeLength ); this._buildOuterPolygons( this.edgeLength ); // Only call AFTER the inner polygons were created! }; /** * @override */ clone() : GirihTile { return new GirihHexagon( this.position.clone(), this.edgeLength ).rotate( this.rotation ); }; private _buildInnerPolygons( edgeLength:number ) : void { // Connect all edges half-the-way const innerTile:Polygon = new Polygon(); innerTile.addVertex( this.vertices[0].clone().scale( 0.5, this.vertices[1] ) ); innerTile.addVertex( this.vertices[1].clone().scale( 0.5, this.vertices[2] ) ); // Compute the next inner polygon vertex by the intersection of two circles const circleA:Circle = new Circle( innerTile.vertices[1], innerTile.vertices[0].distance(innerTile.vertices[1]) ); const circleB :Circle= new Circle( this.vertices[2].clone().scale( 0.5, this.vertices[3] ), circleA.radius ); // TODO: the following piece of code occurs exactly four times. // -> refactor! (DRY) // There is definitely an intersection let intersection:Line|null = circleA.circleIntersection( circleB ); // The intersection is definitely not empty (by construction) // One of the two points is inside the tile, the other is outside. // Locate the inside point. // Use the point that is closer to the center if( this.position.distance(intersection.a) < this.position.distance(intersection.b) ) innerTile.addVertex(intersection.a); else innerTile.addVertex(intersection.b); innerTile.addVertex( circleB.center.clone() ); // var i = 3; // Move circles circleA.center = circleB.center; circleB.center = this.vertices[3].clone().scale( 0.5, this.vertices[4] ); intersection = circleA.circleIntersection( circleB ); // The intersection is definitely not empty (by construction) // There are two points again (one inside, one outside the tile) // Use the point that is closer to the center if( this.position.distance(intersection.a) < this.position.distance(intersection.b) ) innerTile.addVertex(intersection.a); else innerTile.addVertex(intersection.b); innerTile.addVertex( circleB.center.clone() ); innerTile.addVertex( this.vertices[4].clone().scale( 0.5, this.vertices[5] ) ); // Move circles circleA.center = innerTile.vertices[ innerTile.vertices.length-1 ]; circleB.center = this.vertices[5].clone().scale( 0.5, this.vertices[0] ); intersection = circleA.circleIntersection( circleB ); // The intersection is definitely not empty (by construction) // There are two points again (one inside, one outside the tile) // Use the point that is closer to the center if( this.position.distance(intersection.a) < this.position.distance(intersection.b) ) innerTile.addVertex(intersection.a); else innerTile.addVertex(intersection.b); innerTile.addVertex( circleB.center.clone() ); // Move circles circleA.center = innerTile.vertices[ innerTile.vertices.length-1 ]; circleB.center = innerTile.vertices[ 0 ]; intersection = circleA.circleIntersection( circleB ); // The intersection is definitely not empty (by construction) // There are two points again (one inside, one outside the tile) // Use the point that is closer to the center if( this.position.distance(intersection.a) < this.position.distance(intersection.b) ) innerTile.addVertex(intersection.a); else innerTile.addVertex(intersection.b); innerTile.addVertex( circleB.center.clone() ); this.innerTilePolygons.push( innerTile ); }; private _buildOuterPolygons( edgeLength:number ) : void { // First add the two triangles at the 'ends' of the shape. const indicesA:Array<number> = [ 0, 3 ]; // 6:2 const indicesB:Array<number> = [ 0, 5 ]; // 10:2 for( var i = 0; i < indicesA.length; i++ ) { const indexA:number = indicesA[i]; const indexB:number = indicesB[i]; // The triangle const outerTileX:Polygon = new Polygon(); outerTileX.addVertex( this.getVertexAt(indexA+1).clone() ); outerTileX.addVertex( this.innerTilePolygons[0].getVertexAt(indexB).clone() ); outerTileX.addVertex( this.innerTilePolygons[0].getVertexAt(indexB+1).clone() ); this.outerTilePolygons.push( outerTileX ); // The first 'kite' const outerTileY:Polygon = new Polygon(); outerTileY.addVertex( this.getVertexAt(indexA+2).clone() ); outerTileY.addVertex( this.innerTilePolygons[0].getVertexAt(indexB+1).clone() ); outerTileY.addVertex( this.innerTilePolygons[0].getVertexAt(indexB+2).clone() ); outerTileY.addVertex( this.innerTilePolygons[0].getVertexAt(indexB+3).clone() ); this.outerTilePolygons.push( outerTileY ); // The second 'kite' const outerTileZ:Polygon = new Polygon(); outerTileZ.addVertex( this.getVertexAt(indexA+3).clone() ); outerTileZ.addVertex( this.innerTilePolygons[0].getVertexAt(indexB+3).clone() ); outerTileZ.addVertex( this.innerTilePolygons[0].getVertexAt(indexB+4).clone() ); outerTileZ.addVertex( this.innerTilePolygons[0].getVertexAt(indexB+5).clone() ); this.outerTilePolygons.push( outerTileZ ); } }; }