import { CLASS_NAME , STROKE_LINEJOIN } from "./consts" ; import { hasClass , addClass , splitUnit } from "@daybrush/utils" ; export interface Shape { left ? : number ; top ? : number ; right ? : number ; bottom ? : number ; width ? : number ; height ? : number ; fill ? : string ; strokeLinejoin ? : STROKE_LINEJOIN ; strokeWidth ? : number ; className ? : string ; origin ? : string | number ; [ key ] : any ; } export interface RoundRectShape extends Shape { round ? : number ; css ? : boolean ; } export interface PolyShape extends Shape { side ? : number ; split ? : number ; css ? : boolean ; innerRadius ? : number ; } export interface OvalShape extends Shape { r ? : number ; rx ? : number ; ry ? : number ; } function makeDOM ( tag ) { return document . createElementNS ( "http://www.w3.org/2000/svg" , tag ) ; } function makeSVGDOM ( ) { const el = makeDOM ( "svg" ) ; addClass ( el , CLASS_NAME ) ; return el ; } function setAttributes ( element , attributes ) { for ( const name in attributes ) { element . setAttribute ( name , attributes [ name ] ) ; } } function setStyles ( element , styles ) { const cssText = [ ] ; for ( const name in styles ) { cssText . push ( `${ name }:${ styles [ name ] };` ) ; } element . style . cssText += cssText . join ( "" ) ; } function getAbsoluteValue ( value , pos , size ) { const info = splitUnit ( value ) ; if ( info . unit === "%" ) { return ( pos + size * info . value / 100 ) + "px" ; } else if ( info . unit === "px" ) { return ( pos + info . value ) + "px" ; } else { return `calc(${ pos }px + ${ value })` ; } } function setOrigin ( container , { width , height , left , top , origin , } ) { if ( ! origin ) { return ; } let [ ox , oy = ox ] = `${ origin }` . split ( " " ) ; ox = getAbsoluteValue ( ox , left , width ) ; oy = getAbsoluteValue ( oy , top , height ) ; container . style . transformOrigin = `${ ox } ${ oy }` ; } function setViewBox ( container , { width , height , left , right , bottom , top , strokeWidth , className , } ) { if ( container && hasClass ( container , CLASS_NAME ) ) { className && className . split ( " " ) . forEach ( name => { addClass ( container , name ) ; } ) ; const [ , , boxWidth = 0 , boxHeight = 0 ] = ( container . getAttribute ( "viewBox" ) || "" ) . split ( " " ) . map ( pos => parseFloat ( pos || "0" ) ) ; container . setAttribute ( "viewBox" , "0 0 " + `${ Math . max ( left + width + right + strokeWidth , boxWidth ) } ${ Math . max ( top + height + bottom + strokeWidth , boxHeight ) }` ) ; } } export function getRect ( { left = 0 , top = 0 , side = 3 , rotate = 0 , innerRadius = 100 , height = 0 , split = 1 , width = height ? 0 : 100 , strokeLinejoin = "round" , strokeWidth = 0 , } ) { let xPoints = [ ] ; let yPoints = [ ] ; const sideCos = Math . cos ( Math . PI / side ) ; const startRad = Math . PI / 180 * rotate + Math . PI * ( ( side % 2 ? 0 : 1 / side ) - 1 / 2 ) ; for ( let i = 0 ; i < side ; ++ i ) { const rad = Math . PI * ( 1 / side * 2 * i ) + startRad ; const cos = Math . cos ( rad ) ; const sin = Math . sin ( rad ) ; xPoints . push ( cos ) ; yPoints . push ( sin ) ; if ( innerRadius !== 100 ) { if ( sideCos <= innerRadius / 100 ) { continue ; } else { xPoints . push ( innerRadius / 100 * Math . cos ( rad + Math . PI / side ) ) ; yPoints . push ( innerRadius / 100 * Math . sin ( rad + Math . PI / side ) ) ; } } } const minX = Math . min ( ... xPoints ) ; const minY = Math . min ( ... yPoints ) ; const maxX = Math . max ( ... xPoints ) ; const maxY = Math . max ( ... yPoints ) ; const isWidth = ! ! width ; const scale = isWidth ? width / ( maxX - minX ) : height / ( maxY - minY ) ; const isOuter = strokeLinejoin === "miter" || strokeLinejoin === "arcs" || strokeLinejoin === "miter-clip" ; const sideSin = Math . sin ( Math . PI / side ) ; const innerCos = Math . min ( sideCos , innerRadius / 100 ) ; const innerScale = scale * innerCos ; const diagonal = strokeWidth / 2 / ( sideCos === innerCos ? 1 : Math . sin ( Math . atan ( sideSin / ( sideCos - innerCos ) ) ) ) ; const outerScale = isOuter ? ( innerScale + diagonal ) / innerScale : 1 ; const pos = isOuter ? 0 : strokeWidth / 2 ; xPoints = xPoints . map ( xp => ( xp - minX * outerScale ) * scale + pos ) ; yPoints = yPoints . map ( yp => ( yp - minY * outerScale ) * scale + pos ) ; const pathWidth = ( maxX - minX ) * outerScale * scale + pos * 2 ; const pathHeight = ( maxY - minY ) * outerScale * scale + pos * 2 ; const length = xPoints . length ; const points = [ ] ; points . push ( [ left + xPoints [ 0 ] , top + yPoints [ 0 ] ] ) ; for ( let i = 1 ; i <= length ; ++ i ) { const x1 = xPoints [ i - 1 ] ; const y1 = yPoints [ i - 1 ] ; const x2 = xPoints [ i === length ? 0 : i ] ; const y2 = yPoints [ i === length ? 0 : i ] ; for ( let j = 1 ; j <= split ; ++ j ) { const x = ( x1 * ( split - j ) + x2 * j ) / split ; const y = ( y1 * ( split - j ) + y2 * j ) / split ; points . push ( [ left + x , top + y ] ) ; } } return { points , width : pathWidth , height : pathHeight } ; } export function getPath ( points ) { return points . map ( ( point , i ) => { return `${ i === 0 ? "M" : "L" } ${ point . join ( " " ) }` ; } ) . join ( " " ) + " Z" ; } export function be ( path , { left = 0 , top = 0 , right = 0 , bottom = 0 , side , split , rotate , innerRadius , height , width , fill = "transparent" , strokeLinejoin = "round" , strokeWidth = 0 , css = false , className , ... attributes } , container ? ) { const { points , width : pathWidth , height : pathHeight } = getRect ( { left , top , split , side , rotate , width , height , innerRadius , strokeLinejoin , strokeWidth } ) ; setViewBox ( container , { left , top , bottom , right , className , strokeWidth : 0 , width : pathWidth , height : pathHeight , } ) ; const d = getPath ( points ) ; css ? setStyles ( path , { d : `path('${ d }')` } ) : setAttributes ( path , { d } ) ; setAttributes ( path , { fill , "stroke-linejoin" : strokeLinejoin , "stroke-width" : `${ strokeWidth }` , ... attributes , } ) ; } export function star ( { side = 3 , innerRadius = 60 * Math . cos ( Math . PI / side ) , } , container ? ) { return poly ( { ... arguments [ 0 ] , innerRadius } , container ) ; } export function poly ( options , container = makeSVGDOM ( ) ) { const path = makeDOM ( "path" ) as SVGPathElement ; be ( path , options , container ) ; container . appendChild ( path ) ; return container ; } export function oval ( { left = 0 , top = 0 , right = 0 , bottom = 0 , fill = "transparent" , strokeLinejoin = "round" , strokeWidth = 0 , className , r = 0 , rx = r , ry = r , width = rx * 2 , height = ry * 2 , origin , ... attributes } , container = makeSVGDOM ( ) ) { const ellipse = makeDOM ( "ellipse" ) as SVGEllipseElement ; const halfStroke = strokeWidth / 2 ; setViewBox ( container , { strokeWidth , left , top , bottom , right , className , width , height , } ) ; setOrigin ( ellipse , { left : left + halfStroke , top : top + halfStroke , width , height , origin , } ) ; setAttributes ( ellipse , { fill , "cx" : left + halfStroke + width / 2 , "cy" : top + halfStroke + height / 2 , "rx" : width / 2 , "ry" : height / 2 , "stroke-linejoin" : strokeLinejoin , "stroke-width" : `${ strokeWidth }` , ... attributes , } ) ; container . appendChild ( ellipse ) ; return container ; } export function rect ( { left = 0 , top = 0 , right = 0 , bottom = 0 , round = 0 , width , height , fill = "transparent" , strokeLinejoin = "round" , strokeWidth = 0 , css = false , className , ... attributes } , container = makeSVGDOM ( ) ) { const path = makeDOM ( "path" ) as SVGPathElement ; setViewBox ( container , { left , top , bottom , right , className , width , height , strokeWidth , } ) ; setOrigin ( path , { left , top , width , height , origin , } ) ; const halfStroke = strokeWidth / 2 ; const pathWidth = width - round * 2 ; const pathHeight = height - round * 2 ; const d = `M${ left + round + halfStroke },${ top + halfStroke } h${ pathWidth } a${ round },${ round } 0 0 1 ${ round },${ round } v${ pathHeight } a${ round },${ round } 0 0 1 -${ round },${ round } h-${ pathWidth } a${ round },${ round } 0 0 1 -${ round },-${ round } v-${ pathHeight } a${ round },${ round } 0 0 1 ${ round },-${ round } z` ; css ? setStyles ( path , { d : `path('${ d }')` } ) : setAttributes ( path , { d } ) ; setAttributes ( path , { fill , "stroke-linejoin" : strokeLinejoin , "stroke-width" : `${ strokeWidth }` , ... attributes , } ) ; container . appendChild ( path ) ; return container ; } export const VERSION = "#__VERSION__#" ;