import { Vector } from "./vector" ; import { BorderConfig , BorderType } from "./borderconfig" ; import { Config } from "./config" ; import { PolygonConfig } from "./PolygonConfig" ; export { Transmutation } ; class Transmutation { ctx : CanvasRenderingContext2D ; canvas : HTMLCanvasElement ; background : string ; characterOn : number ; config : Config ; constructor ( canvas , config ) { this . canvas = canvas ; this . ctx = canvas . getContext ( "2d" ) ; this . background = "white" ; this . characterOn = 0 ; this . config = config ; } GetCharacterOn ( ) { return this . characterOn ; } SetConfig ( config ) { this . config = config ; } DrawMiddle ( maxRadius , middleCords , config , rotation ) { const apothem = maxRadius * Math . cos ( Math . PI / config . Sides ( ) ) ; this . Polygon ( middleCords , maxRadius , config . Sides ( ) * 2 , rotation + Math . PI / 2 ) ; this . Polygon ( middleCords , maxRadius , config . Sides ( ) , rotation + Math . PI / 2 ) ; this . PolyVertexIntersections ( middleCords , maxRadius , config . Sides ( ) * 2 , rotation + ( Math . PI / 2 + Math . PI / config . Sides ( ) ) ) ; if ( config . Arcs ( ) ) { this . PolyMidpointArcs ( middleCords , maxRadius , config . Sides ( ) * 2 , rotation + ( Math . PI / 2 + Math . PI / config . Sides ( ) ) , maxRadius / 3 ) ; this . PolyMidpointArcs ( middleCords , maxRadius , config . Sides ( ) * 2 , rotation + ( Math . PI / 2 + Math . PI / config . Sides ( ) ) , maxRadius / 3.5 ) ; } const midpointCircleRadius = maxRadius / 8 ; if ( config . Circles ( ) ) { this . PolyMidpointCircles ( middleCords , apothem , config . Sides ( ) , rotation + ( Math . PI / 2 + Math . PI / config . Sides ( ) ) , midpointCircleRadius ) ; } return ( apothem - midpointCircleRadius ) * 0.99 ; } DrawBorder ( maxRadius , middleCords , border , rotation ) { let remainingRadius = 1 ; let direction = 1 ; border . GetBorderTypes ( ) . forEach ( ( type ) => { switch ( type ) { case BorderType . Line : this . Circle ( middleCords , maxRadius * remainingRadius ) ; break ; case BorderType . Space : break ; case BorderType . Text : this . CircleText ( middleCords , maxRadius * remainingRadius , ( ( maxRadius * remainingRadius ) / 20 ) * this . config . GetTextScale ( ) , rotation * direction ) ; direction *= - 1 ; break ; } remainingRadius -= 0.05 ; } ) ; return maxRadius * remainingRadius ; } public Draw ( width , height , rotation ) { this . characterOn = 0 ; this . Start ( ) ; let direction = 1 ; const middleCords = new Vector ( width / 2 , height / 2 ) ; let maxRadius = this . DrawBorder ( Math . min ( width , height ) * 0.48 , middleCords , this . config . GetBorderConfig ( ) , rotation * direction ) ; direction *= this . config . GetBorderConfig ( ) . GetBorderTypes ( ) . length % 2 === 0 ? 1 : - 1 ; this . config . GetPolygonConfigs ( ) . forEach ( polyConfig => { maxRadius = this . DrawMiddle ( maxRadius , middleCords , polyConfig , rotation * direction ) ; direction *= - 1 ; } ) ; this . ctx . beginPath ( ) ; this . ctx . arc ( middleCords . x ( ) , middleCords . y ( ) , maxRadius , 0 , 2 * Math . PI ) ; this . ctx . fill ( ) ; maxRadius = this . DrawBorder ( maxRadius , middleCords , this . config . GetInnerBoarderConfig ( ) , rotation * direction ) ; direction *= this . config . GetInnerBoarderConfig ( ) . GetBorderTypes ( ) . length % 2 === 0 ? 1 : - 1 ; if ( this . config . GetInnerPolygonConfig ( ) != null ) { this . DrawMiddle ( maxRadius , middleCords , this . config . GetInnerPolygonConfig ( ) , rotation * direction * - 1 ) ; } } Start ( ) { this . ctx . fillStyle = this . background ; this . ctx . rect ( 0 , 0 , this . canvas . width , this . canvas . height ) ; this . ctx . fill ( ) ; } Polygon ( pos , radius , sides , rotation ) { this . ctx . strokeStyle = this . config . GetLineColor ( ) ; this . ctx . beginPath ( ) ; const angleIncrement = ( Math . PI * 2 ) / sides ; for ( let vertex = 0 ; vertex < sides ; vertex ++ ) { this . ctx . lineTo ( pos . x ( ) + radius * Math . cos ( rotation + angleIncrement * vertex ) , pos . y ( ) + radius * Math . sin ( rotation + angleIncrement * vertex ) ) ; } this . ctx . closePath ( ) ; this . ctx . stroke ( ) ; } PolyMidpointCircles ( pos , radius , sides , rotation , circleRadius ) { const angleIncrement = ( Math . PI * 2 ) / sides ; for ( let vertex = 0 ; vertex < sides ; vertex ++ ) { this . ctx . strokeStyle = this . config . GetLineColor ( ) ; this . ctx . fillStyle = this . background ; const angle = rotation + angleIncrement * vertex ; const adjustedPos = Vector . fromAngle ( angle ) . scale ( radius ) . add ( pos ) ; this . ctx . beginPath ( ) ; this . ctx . arc ( adjustedPos . x ( ) , adjustedPos . y ( ) , circleRadius , 0 , Math . PI * 2 ) ; this . ctx . fill ( ) ; this . ctx . stroke ( ) ; this . SpecialSymbol ( adjustedPos , circleRadius , angle ) ; } } PolyVertexIntersections ( pos , radius , sides , rotation ) { this . ctx . strokeStyle = this . config . GetLineColor ( ) ; const angleIncrement = ( Math . PI * 2 ) / sides ; for ( let vertex = 0 ; vertex < sides ; vertex ++ ) { this . ctx . beginPath ( ) ; this . ctx . moveTo ( pos . x ( ) , pos . y ( ) ) ; this . ctx . lineTo ( pos . x ( ) + radius * Math . cos ( rotation + angleIncrement * vertex ) , pos . y ( ) + radius * Math . sin ( rotation + angleIncrement * vertex ) ) ; this . ctx . stroke ( ) ; } } NextSymbol ( pos , size , angle ) { const g = this . config . GetAlphabet ( ) . Glyph ( this . config . GetSentence ( ) . charAt ( this . characterOn % this . config . GetSentence ( ) . length ) ) ; if ( g !== null ) { g . Draw ( this . ctx , pos , Vector . one ( ) . scale ( size ) , angle , this . config . GetLineColor ( ) ) ; } this . characterOn += 1 ; } SpecialSymbol ( pos , size , angle ) { } CircleText ( pos , radius , fontSize , rotation ) { const letters = ( ( radius * Math . PI * 2 ) / fontSize ) * 0.7 ; const angle = ( Math . PI * 2 ) / letters ; for ( let i = 0 ; i < letters - 1 ; i ++ ) { const curAngle = angle * i + rotation ; this . NextSymbol ( pos . add ( Vector . fromAngle ( curAngle ) . scale ( radius ) ) , fontSize , curAngle ) ; } } Circle ( pos , radius ) { this . ctx . strokeStyle = this . config . GetLineColor ( ) ; this . ctx . beginPath ( ) ; this . ctx . arc ( pos . x ( ) , pos . y ( ) , radius , 0 , Math . PI * 2 ) ; this . ctx . stroke ( ) ; } ThreePointArc ( initial , median , final ) { const control = median . scale ( 2 ) . subtract ( initial . scale ( 0.5 ) ) . subtract ( final . scale ( 0.5 ) ) ; this . ctx . beginPath ( ) ; this . ctx . moveTo ( initial . x ( ) , initial . y ( ) ) ; this . ctx . quadraticCurveTo ( control . x ( ) , control . y ( ) , final . x ( ) , final . y ( ) ) ; this . ctx . stroke ( ) ; } PolyMidpointArcs ( pos , radius , sides , rotation , arcRadius ) { this . ctx . strokeStyle = this . config . GetLineColor ( ) ; this . ctx . fillStyle = this . background ; const angleIncrement = ( Math . PI * 2 ) / sides ; for ( let vertex = 2 ; vertex <= sides ; vertex += 2 ) { const pointOne = new Vector ( pos . x ( ) + radius * Math . cos ( rotation + angleIncrement * ( vertex - 2 ) ) , pos . y ( ) + radius * Math . sin ( rotation + angleIncrement * ( vertex - 2 ) ) ) ; const pointTwo = new Vector ( pos . x ( ) + radius * Math . cos ( rotation + angleIncrement * ( vertex - 1 ) ) , pos . y ( ) + radius * Math . sin ( rotation + angleIncrement * ( vertex - 1 ) ) ) ; const pointThree = new Vector ( pos . x ( ) + radius * Math . cos ( rotation + angleIncrement * vertex ) , pos . y ( ) + radius * Math . sin ( rotation + angleIncrement * vertex ) ) ; const arcPointOne = pointTwo . add ( pointOne . subtract ( pointTwo ) . normal ( ) . scale ( arcRadius ) ) ; const arcPointTwo = pointTwo . add ( pointThree . subtract ( pointTwo ) . normal ( ) . scale ( arcRadius ) ) ; const arcPointMidpoint = pointTwo . add ( pos . subtract ( pointTwo ) . normal ( ) . scale ( arcRadius ) ) ; this . ThreePointArc ( arcPointOne , arcPointMidpoint , arcPointTwo ) ; } } }