2014-01-10 19:48:28 -05:00
|
|
|
{hexToHSL, hslToHex} = require 'lib/utils'
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
module.exports = class SpriteBuilder
|
|
|
|
constructor: (@thangType, @options) ->
|
2014-01-10 19:48:28 -05:00
|
|
|
@options ?= {}
|
2014-01-03 13:32:13 -05:00
|
|
|
raw = _.cloneDeep(@thangType.get('raw'))
|
|
|
|
@shapeStore = raw.shapes
|
|
|
|
@containerStore = raw.containers
|
|
|
|
@animationStore = raw.animations
|
2014-01-10 19:48:28 -05:00
|
|
|
@buildColorMaps()
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
setOptions: (@options) ->
|
|
|
|
|
|
|
|
buildMovieClip: (animationName, movieClipArgs...) ->
|
|
|
|
animData = @animationStore[animationName]
|
2014-01-30 19:17:41 -05:00
|
|
|
unless animData
|
|
|
|
console.error "couldn't find animData from", @animationStore, "for", animationName
|
|
|
|
return null
|
2014-01-03 13:32:13 -05:00
|
|
|
locals = {}
|
|
|
|
_.extend locals, @buildMovieClipShapes(animData.shapes)
|
|
|
|
_.extend locals, @buildMovieClipContainers(animData.containers)
|
|
|
|
_.extend locals, @buildMovieClipAnimations(animData.animations)
|
|
|
|
_.extend locals, @buildMovieClipGraphics(animData.graphics)
|
|
|
|
anim = new createjs.MovieClip()
|
|
|
|
movieClipArgs ?= []
|
|
|
|
labels = {}
|
|
|
|
labels[animationName] = 0
|
|
|
|
anim.initialize(
|
|
|
|
movieClipArgs[0] ? createjs.MovieClip.INDEPENDENT, # mode
|
|
|
|
movieClipArgs[1] ? 0, # start position
|
|
|
|
movieClipArgs[2] ? true, # loops
|
|
|
|
labels)
|
|
|
|
for tweenData in animData.tweens
|
|
|
|
tween = createjs.Tween
|
|
|
|
for func in tweenData
|
|
|
|
args = _.cloneDeep(func.a)
|
|
|
|
@dereferenceArgs(args, locals)
|
|
|
|
tween = tween[func.n](args...)
|
|
|
|
anim.timeline.addTween(tween)
|
|
|
|
|
|
|
|
anim.nominalBounds = new createjs.Rectangle(animData.bounds...)
|
|
|
|
if animData.frameBounds
|
|
|
|
anim.frameBounds = (new createjs.Rectangle(bounds...) for bounds in animData.frameBounds)
|
|
|
|
anim
|
|
|
|
|
|
|
|
dereferenceArgs: (args, locals) ->
|
|
|
|
for key, val of args
|
|
|
|
if locals[val]
|
|
|
|
args[key] = locals[val]
|
|
|
|
else if val is null
|
|
|
|
args[key] = {}
|
|
|
|
else if _.isString(val) and val.indexOf('createjs.') is 0
|
|
|
|
args[key] = eval(val) # TODO: Security risk
|
|
|
|
else if _.isObject(val) or _.isArray(val)
|
|
|
|
@dereferenceArgs(val, locals)
|
|
|
|
args
|
|
|
|
|
|
|
|
buildMovieClipShapes: (localShapes) ->
|
|
|
|
map = {}
|
|
|
|
for localShape in localShapes
|
|
|
|
if localShape.im
|
|
|
|
shape = new createjs.Shape()
|
|
|
|
shape._off = true
|
|
|
|
else
|
|
|
|
shape = @buildShapeFromStore(localShape.gn)
|
|
|
|
if localShape.m
|
|
|
|
shape.mask = map[localShape.m]
|
|
|
|
map[localShape.bn] = shape
|
|
|
|
map
|
|
|
|
|
|
|
|
buildMovieClipContainers: (localContainers) ->
|
|
|
|
map = {}
|
|
|
|
for localContainer in localContainers
|
|
|
|
container = @buildContainerFromStore(localContainer.gn)
|
|
|
|
container.setTransform(localContainer.t...)
|
|
|
|
container._off = localContainer.o if localContainer.o?
|
|
|
|
container.alpha = localContainer.al if localContainer.al?
|
|
|
|
map[localContainer.bn] = container
|
|
|
|
map
|
|
|
|
|
|
|
|
buildMovieClipAnimations: (localAnimations) ->
|
|
|
|
map = {}
|
|
|
|
for localAnimation in localAnimations
|
|
|
|
animation = @buildMovieClip(localAnimation.gn, localAnimation.a)
|
|
|
|
animation.setTransform(localAnimation.t...)
|
|
|
|
map[localAnimation.bn] = animation
|
|
|
|
map
|
|
|
|
|
|
|
|
buildMovieClipGraphics: (localGraphics) ->
|
|
|
|
map = {}
|
|
|
|
for localGraphic in localGraphics
|
|
|
|
graphic = new createjs.Graphics().p(localGraphic.p)
|
|
|
|
map[localGraphic.bn] = graphic
|
|
|
|
map
|
|
|
|
|
|
|
|
buildShapeFromStore: (shapeKey, debug=false) ->
|
|
|
|
shapeData = @shapeStore[shapeKey]
|
|
|
|
shape = new createjs.Shape()
|
|
|
|
if shapeData.lf?
|
|
|
|
shape.graphics.lf shapeData.lf...
|
|
|
|
else if shapeData.fc?
|
2014-01-10 19:48:28 -05:00
|
|
|
shape.graphics.f @colorMap[shapeKey] or shapeData.fc
|
2014-01-03 13:32:13 -05:00
|
|
|
if shapeData.ls?
|
|
|
|
shape.graphics.ls shapeData.ls...
|
|
|
|
else if shapeData.sc?
|
|
|
|
shape.graphics.s shapeData.sc
|
|
|
|
shape.graphics.ss shapeData.ss... if shapeData.ss?
|
|
|
|
shape.graphics.de shapeData.de... if shapeData.de?
|
|
|
|
shape.graphics.p shapeData.p if shapeData.p?
|
|
|
|
shape.setTransform shapeData.t...
|
|
|
|
shape
|
|
|
|
|
|
|
|
buildContainerFromStore: (containerKey) ->
|
|
|
|
console.error "Yo we don't have no", containerKey unless containerKey
|
|
|
|
contData = @containerStore[containerKey]
|
|
|
|
cont = new createjs.Container()
|
|
|
|
cont.initialize()
|
|
|
|
for childData in contData.c
|
|
|
|
if _.isString(childData)
|
|
|
|
child = @buildShapeFromStore(childData)
|
|
|
|
else
|
|
|
|
child = @buildContainerFromStore(childData.gn)
|
|
|
|
child.setTransform(childData.t...)
|
|
|
|
cont.addChild(child)
|
|
|
|
cont.bounds = new createjs.Rectangle(contData.b...)
|
|
|
|
cont
|
2014-01-10 19:48:28 -05:00
|
|
|
|
|
|
|
buildColorMaps: ->
|
|
|
|
@colorMap = {}
|
|
|
|
colorGroups = @thangType.get('colorGroups')
|
|
|
|
return if _.isEmpty colorGroups
|
|
|
|
colorConfig = @options.colorConfig
|
|
|
|
# colorConfig ?= {team: {hue:0.4, saturation: -0.5, lightness: -0.5}} # test config
|
|
|
|
return if not colorConfig
|
|
|
|
|
|
|
|
for group, config of colorConfig
|
|
|
|
continue unless colorGroups[group] # color group not found...
|
|
|
|
@buildColorMapForGroup(colorGroups[group], config)
|
|
|
|
|
|
|
|
buildColorMapForGroup: (shapes, config) ->
|
|
|
|
return unless shapes.length
|
|
|
|
colors = @initColorMap(shapes)
|
|
|
|
@adjustHuesForColorMap(colors, config.hue)
|
2014-01-12 14:54:50 -05:00
|
|
|
@adjustValueForColorMap(colors, 1, config.lightness)
|
|
|
|
@adjustValueForColorMap(colors, 2, config.saturation)
|
2014-01-10 19:48:28 -05:00
|
|
|
@applyColorMap(shapes, colors)
|
|
|
|
|
|
|
|
initColorMap: (shapes) ->
|
|
|
|
colors = {}
|
|
|
|
for shapeKey in shapes
|
|
|
|
shape = @shapeStore[shapeKey]
|
|
|
|
continue if (not shape.fc?) or colors[shape.fc]
|
|
|
|
hsl = hexToHSL(shape.fc)
|
|
|
|
colors[shape.fc] = hsl
|
|
|
|
colors
|
|
|
|
|
|
|
|
adjustHuesForColorMap: (colors, targetHue) ->
|
|
|
|
hues = (hsl[0] for hex, hsl of colors)
|
|
|
|
|
|
|
|
# 'rotate' the hue spectrum so averaging works
|
|
|
|
if Math.max(hues) - Math.min(hues) > 0.5
|
|
|
|
hues = (if h < 0.5 then h + 1.0 else h for h in hues)
|
|
|
|
averageHue = sum(hues) / hues.length
|
|
|
|
averageHue %= 1
|
|
|
|
# end result should be something like a hue array of [0.9, 0.3] gets an average of 0.1
|
|
|
|
|
|
|
|
targetHue ?= 0
|
|
|
|
diff = targetHue - averageHue
|
|
|
|
hsl[0] = (hsl[0] + diff + 1) % 1 for hex, hsl of colors
|
2014-01-12 14:54:50 -05:00
|
|
|
|
|
|
|
adjustValueForColorMap: (colors, index, targetValue) ->
|
|
|
|
values = (hsl[index] for hex, hsl of colors)
|
|
|
|
averageValue = sum(values) / values.length
|
|
|
|
targetValue ?= 0.5
|
|
|
|
diff = targetValue - averageValue
|
|
|
|
for hex, hsl of colors
|
|
|
|
hsl[index] = Math.max(0, Math.min(1, hsl[index] + diff))
|
2014-01-10 19:48:28 -05:00
|
|
|
|
|
|
|
applyColorMap: (shapes, colors) ->
|
|
|
|
for shapeKey in shapes
|
|
|
|
shape = @shapeStore[shapeKey]
|
|
|
|
continue if (not shape.fc?) or not(colors[shape.fc])
|
|
|
|
@colorMap[shapeKey] = hslToHex(colors[shape.fc])
|
|
|
|
|
|
|
|
|
|
|
|
sum = (nums) -> _.reduce(nums, (s, num) -> s + num)
|