@@ -21,7 +21,9 @@ interface PickRandomOpt<T> {
2121 avoidList ?: T [ ] ,
2222 usually ?: T [ ]
2323}
24+
2425type PickRandomFromList = < T > ( data : T [ ] , opt ?: PickRandomOpt < T | undefined > ) => T ;
26+
2527export const pickRandomFromList : PickRandomFromList = ( data , { avoidList = [ ] , usually = [ ] } = { } ) => {
2628 // Filter out avoid options
2729 const avoidSet = new Set (
@@ -66,6 +68,7 @@ interface DefaultOptions {
6668 bgColor : string [ ] ,
6769 gradientBgColor : string [ ]
6870}
71+
6972export const defaultOptions : DefaultOptions = {
7073 sex : [ "man" , "woman" ] ,
7174 faceColor : [ "#F9C9B6" , "#AC6651" ] ,
@@ -93,21 +96,61 @@ export const defaultOptions: DefaultOptions = {
9396 "linear-gradient(45deg, #56b5f0 0%, #45ccb5 100%)"
9497 ]
9598} ;
99+
100+ const stringToHashCode = ( str : string ) : number => {
101+ if ( str . length === 0 ) return 0
102+ let hash = 0
103+ let char
104+ for ( let i = 0 ; i < str . length ; i ++ ) {
105+ char = str . charCodeAt ( i ) ;
106+ hash = ( ( hash << 5 ) - hash ) + char ;
107+ hash |= 0 ; // Convert to 32bit integer
108+ }
109+ return Math . abs ( hash ) ;
110+ }
111+
112+ type PickByHashCodeOpts = {
113+ avoidList ?: string [ ] ,
114+ usually ?: string [ ]
115+ }
116+ const pickByHashCode = ( code : number , type : keyof DefaultOptions , opts ?: PickByHashCodeOpts ) : string => {
117+ const avoidList = opts && opts . avoidList || [ ]
118+ const usually = opts && opts . usually || [ ]
119+
120+ // Filter out avoid options
121+ const avoidSet = new Set < string > ( avoidList )
122+ let myDefaultOptions = defaultOptions [ type ] . filter ( item => ! avoidSet . has ( item ) )
123+
124+ // Increase selecting possibility of usually options
125+ myDefaultOptions = usually
126+ . filter ( Boolean )
127+ . reduce (
128+ ( acc , cur ) => acc . concat ( new Array ( 15 ) . fill ( cur ) ) ,
129+ [ ] as string [ ]
130+ )
131+ . concat ( myDefaultOptions )
132+
133+ const index = code % myDefaultOptions . length
134+ return myDefaultOptions [ index ]
135+ }
136+
96137export const genConfig : GenConfigFunc = ( userConfig = { } ) => {
138+ const isSeedConfig = typeof userConfig === 'string' ;
139+ const hashCode = isSeedConfig && stringToHashCode ( userConfig ) || 0
97140 const response = { } as Required < AvatarFullConfig > ;
98- response . sex = userConfig . sex || pickRandomFromList ( defaultOptions . sex ) ;
99- response . faceColor = userConfig . faceColor || pickRandomFromList ( defaultOptions . faceColor ) ;
100- response . earSize = userConfig . earSize || pickRandomFromList ( defaultOptions . earSize ) ;
101- response . eyeStyle = userConfig . eyeStyle || pickRandomFromList ( defaultOptions . eyeStyle ) ;
102- response . noseStyle = userConfig . noseStyle || pickRandomFromList ( defaultOptions . noseStyle ) ;
103- response . mouthStyle = userConfig . mouthStyle || pickRandomFromList ( defaultOptions . mouthStyle ) ;
104- response . shirtStyle = userConfig . shirtStyle || pickRandomFromList ( defaultOptions . shirtStyle ) ;
105- response . glassesStyle = userConfig . glassesStyle || pickRandomFromList ( defaultOptions . glassesStyle , { usually : [ "none" ] } ) ;
141+ response . sex = isSeedConfig ? pickByHashCode ( hashCode , 'sex' ) as Sex : ( userConfig . sex || pickRandomFromList ( defaultOptions . sex ) ) ;
142+ response . faceColor = isSeedConfig ? pickByHashCode ( hashCode , 'faceColor' ) : ( userConfig . faceColor || pickRandomFromList ( defaultOptions . faceColor ) ) ;
143+ response . earSize = isSeedConfig ? pickByHashCode ( hashCode , 'earSize' ) as EarSize : ( userConfig . earSize || pickRandomFromList ( defaultOptions . earSize ) ) ;
144+ response . eyeStyle = isSeedConfig ? pickByHashCode ( hashCode , 'eyeStyle' ) as EyeStyle : ( userConfig . eyeStyle || pickRandomFromList ( defaultOptions . eyeStyle ) ) ;
145+ response . noseStyle = isSeedConfig ? pickByHashCode ( hashCode , 'noseStyle' ) as NoseStyle : ( userConfig . noseStyle || pickRandomFromList ( defaultOptions . noseStyle ) ) ;
146+ response . mouthStyle = isSeedConfig ? pickByHashCode ( hashCode , 'mouthStyle' ) as MouthStyle : ( userConfig . mouthStyle || pickRandomFromList ( defaultOptions . mouthStyle ) ) ;
147+ response . shirtStyle = isSeedConfig ? pickByHashCode ( hashCode , 'shirtStyle' ) as ShirtStyle : ( userConfig . shirtStyle || pickRandomFromList ( defaultOptions . shirtStyle ) ) ;
148+ response . glassesStyle = isSeedConfig ? pickByHashCode ( hashCode , 'glassesStyle' , { usually : [ "none" ] } ) as GlassesStyle : ( userConfig . glassesStyle || pickRandomFromList ( defaultOptions . glassesStyle , { usually : [ "none" ] } ) ) ;
106149
107150 // Hair
108151 let hairColorAvoidList : string [ ] = [ ] ;
109152 let hairColorUsually : string [ ] = [ ] ;
110- if ( ! userConfig . hairColor ) {
153+ if ( isSeedConfig || ! userConfig . hairColor ) {
111154 switch ( response . sex ) {
112155 case "woman" : {
113156 hairColorAvoidList = response . faceColor === defaultOptions . faceColor [ 1 ] && [ "#77311D" ] || [ ] ;
@@ -118,48 +161,63 @@ export const genConfig: GenConfigFunc = (userConfig = {}) => {
118161 }
119162 }
120163 }
121- response . hairColor = userConfig . hairColor || pickRandomFromList ( defaultOptions . hairColor , {
122- avoidList : hairColorAvoidList ,
123- usually : hairColorUsually
124- } ) ;
164+ response . hairColor = isSeedConfig
165+ ? pickByHashCode ( hashCode , 'hairColor' , {
166+ avoidList : hairColorAvoidList ,
167+ usually : hairColorUsually
168+ } )
169+ : ( userConfig . hairColor || pickRandomFromList ( defaultOptions . hairColor , {
170+ avoidList : hairColorAvoidList ,
171+ usually : hairColorUsually
172+ } ) ) ;
125173
126- let myHairStyle = userConfig . hairStyle ;
127- if ( ! myHairStyle ) {
174+ if ( isSeedConfig || ! userConfig . hairStyle ) {
128175 switch ( response . sex ) {
129176 case "man" : {
130- myHairStyle = pickRandomFromList ( defaultOptions . hairStyleMan , { usually : [ "normal" , "thick" ] } ) ;
177+ response . hairStyle = isSeedConfig
178+ ? pickByHashCode ( hashCode , 'hairStyleMan' , { usually : [ "normal" , "thick" ] } ) as HairStyleMan
179+ : pickRandomFromList ( defaultOptions . hairStyleMan , { usually : [ "normal" , "thick" ] } ) ;
131180 break ;
132181 }
133182 case "woman" : {
134- myHairStyle = pickRandomFromList ( defaultOptions . hairStyleWoman ) ;
183+ response . hairStyle = isSeedConfig
184+ ? pickByHashCode ( hashCode , 'hairStyleWoman' ) as HairStyleWoman
185+ : pickRandomFromList ( defaultOptions . hairStyleWoman ) ;
135186 break ;
136187 }
137188 }
138189 }
139- response . hairStyle = myHairStyle ;
140190
141191 // Hat
142- response . hatStyle = userConfig . hatStyle || pickRandomFromList ( defaultOptions . hatStyle , { usually : [ "none" ] } ) ;
143- response . hatColor = userConfig . hatColor || pickRandomFromList ( defaultOptions . hatColor ) ;
192+ response . hatStyle = isSeedConfig
193+ ? pickByHashCode ( hashCode , 'hatStyle' , { usually : [ "none" ] } ) as HatStyle
194+ : ( userConfig . hatStyle || pickRandomFromList ( defaultOptions . hatStyle , { usually : [ "none" ] } ) ) ;
195+ response . hatColor = isSeedConfig ? pickByHashCode ( hashCode , 'hatColor' ) : ( userConfig . hatColor || pickRandomFromList ( defaultOptions . hatColor ) ) ;
144196 const _hairOrHatColor = response . hatStyle === "none" && response . hairColor || response . hatColor ;
145197
146198 // Eyebrow
147- if ( userConfig . eyeBrowStyle ) {
199+ if ( ! isSeedConfig && userConfig . eyeBrowStyle ) {
148200 response . eyeBrowStyle = userConfig . eyeBrowStyle
149201 } else {
150202 response . eyeBrowStyle = response . sex === "woman"
151- ? pickRandomFromList ( defaultOptions . eyeBrowWoman )
203+ ? isSeedConfig
204+ ? pickByHashCode ( hashCode , 'eyeBrowWoman' ) as EyeBrowStyle
205+ : pickRandomFromList ( defaultOptions . eyeBrowWoman )
152206 : "up"
153207 }
154208
155209 // Shirt color
156- response . shirtColor = userConfig . shirtColor || pickRandomFromList ( defaultOptions . shirtColor , { avoidList : [ _hairOrHatColor ] } ) ;
210+ response . shirtColor = isSeedConfig
211+ ? pickByHashCode ( hashCode , 'shirtColor' , { avoidList : [ _hairOrHatColor ] } )
212+ : userConfig . shirtColor || pickRandomFromList ( defaultOptions . shirtColor , { avoidList : [ _hairOrHatColor ] } ) ;
157213
158214 // Background color
159- if ( userConfig . isGradient ) {
215+ if ( ! isSeedConfig && userConfig . isGradient ) {
160216 response . bgColor = userConfig . bgColor || pickRandomFromList ( defaultOptions . gradientBgColor ) ;
161217 } else {
162- response . bgColor = userConfig . bgColor || pickRandomFromList ( defaultOptions . bgColor , { avoidList : [ _hairOrHatColor , response . shirtColor ] } ) ;
218+ response . bgColor = isSeedConfig
219+ ? pickByHashCode ( hashCode , 'bgColor' , { avoidList : [ _hairOrHatColor , response . shirtColor ] } )
220+ : userConfig . bgColor || pickRandomFromList ( defaultOptions . bgColor , { avoidList : [ _hairOrHatColor , response . shirtColor ] } ) ;
163221 }
164222
165223 return response ;
0 commit comments