diff --git a/src/create-composite-object.js b/src/create-composite-object.js new file mode 100644 index 0000000..291154d --- /dev/null +++ b/src/create-composite-object.js @@ -0,0 +1,93 @@ +'use strict'; + +const dotty = require("dotty"); +const defaultValue = require("default-value"); + +const createObject = require("./create-object"); +const createPropertyMapper = require("./property-mapper"); +const max = require("./util/max"); +const min = require("./util/min"); + +function getPosition(object) { + return { + x: defaultValue(object.x, 0), + y: defaultValue(object.y, 0) + } +} + +module.exports = function createCompositeObject(options) { + let mapProperties = createPropertyMapper(options.propertyMap); + + let compositeObject = createObject(Object.assign({ + onSet: function setCompositeProperties(properties) { + let mappedProperties = mapProperties(properties); + + Object.keys(mappedProperties).forEach((objectName) => { + if (objectName !== "_") { + if (this.objects[objectName] != null) { + this.objects[objectName].set(mappedProperties[objectName]); + } else { + throw new Error(`The object specified in the property map as '${objectName}' does not exist`); + } + } + }); + + /* We return the `_` object here, which contains all the unmapped + * properties - we assume that all unmapped properties are meant to + * apply to the composite object rather than its child objects. The + * original `.set` method (as defined in createObject) will then use + * this result. */ + return defaultValue(mappedProperties._, {}); + }, + onRender: function onRender(context) { + Object.keys(this.objects).forEach((objectName) => { + let object = this.objects[objectName]; + let position = getPosition(object); + + object.render(context, { + x: object.x - this.renderOffsetX, + y: object.y - this.renderOffsetY + }); + }); + }, + onRecalculateSize: function onRecalculateSize() { + let objectMetrics = Object.keys(this.objects).map((objectName) => { + let object = this.objects[objectName]; + let position = getPosition(object); + + console.log(object.type, [object.x, object.y], [position.x, position.y], object.renderWidth, object.renderHeight); + + return { + x1: position.x, + y1: position.y, + x2: position.x + object.renderWidth, + y2: position.y + object.renderHeight + } + }); + + let x1 = min(objectMetrics.map(object => object.x1)); + let y1 = min(objectMetrics.map(object => object.y1)); + let x2 = max(objectMetrics.map(object => object.x2)); + let y2 = max(objectMetrics.map(object => object.y2)); + + return { + width: x2 - x1, + height: y2 - y1, + offsetX: x1, + offsetY: y1 + } + } + }, options)); + + Object.keys(compositeObject.objects).forEach((objectName) => { + compositeObject.objects[objectName].on("recalculatedSize", () => { + compositeObject.recalculateSize(); + }); + + compositeObject.objects[objectName].on("renderedToCache", () => { + compositeObject.bustCache(); + }); + }); + + return compositeObject; +}; diff --git a/src/index.js b/src/index.js index 780e19b..5aad1f8 100644 --- a/src/index.js +++ b/src/index.js @@ -2,5 +2,6 @@ module.exports = { validateSync: require("./validate-sync"), - createObject: require("./create-object") + createObject: require("./create-object"), + createCompositeObject: require("./create-composite-object") } diff --git a/src/util/max.js b/src/util/max.js new file mode 100644 index 0000000..b0e3790 --- /dev/null +++ b/src/util/max.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function max(values) { + return Math.max(...values); +} diff --git a/src/util/min.js b/src/util/min.js new file mode 100644 index 0000000..005c428 --- /dev/null +++ b/src/util/min.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function min(values) { + return Math.min(...values); +}