diff options
Diffstat (limited to 'source/game/StarDrawable.cpp')
-rw-r--r-- | source/game/StarDrawable.cpp | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/source/game/StarDrawable.cpp b/source/game/StarDrawable.cpp new file mode 100644 index 0000000..5fd5071 --- /dev/null +++ b/source/game/StarDrawable.cpp @@ -0,0 +1,273 @@ +#include "StarDrawable.hpp" +#include "StarColor.hpp" +#include "StarJsonExtra.hpp" +#include "StarDataStreamExtra.hpp" +#include "StarAssets.hpp" +#include "StarImageMetadataDatabase.hpp" +#include "StarGameTypes.hpp" +#include "StarRoot.hpp" + +namespace Star { + +void Drawable::ImagePart::addDirectives(String const& directives, bool keepImageCenterPosition) { + if (directives.empty()) + return; + + if (keepImageCenterPosition) { + auto imageMetadata = Root::singleton().imageMetadataDatabase(); + Vec2F imageSize = Vec2F(imageMetadata->imageSize(image)); + image = AssetPath::addDirectives(image, {directives}); + Vec2F newImageSize = Vec2F(imageMetadata->imageSize(image)); + + // If we are trying to maintain the image center, PRE translate the image by + // the change in size / 2 + transformation *= Mat3F::translation((imageSize - newImageSize) / 2); + } else { + image = AssetPath::addDirectives(image, {directives}); + } +} + +void Drawable::ImagePart::removeDirectives(bool keepImageCenterPosition) { + if (keepImageCenterPosition) { + auto imageMetadata = Root::singleton().imageMetadataDatabase(); + Vec2F imageSize = Vec2F(imageMetadata->imageSize(image)); + image = AssetPath::removeDirectives(image); + Vec2F newImageSize = Vec2F(imageMetadata->imageSize(image)); + + // If we are trying to maintain the image center, PRE translate the image by + // the change in size / 2 + transformation *= Mat3F::translation((imageSize - newImageSize) / 2); + } else { + image = AssetPath::removeDirectives(image); + } +} + +Drawable Drawable::makeLine(Line2F const& line, float lineWidth, Color const& color, Vec2F const& position) { + Drawable drawable; + drawable.part = LinePart{move(line), lineWidth}; + drawable.color = color; + drawable.position = position; + + return drawable; +} + +Drawable Drawable::makePoly(PolyF poly, Color const& color, Vec2F const& position) { + Drawable drawable; + drawable.part = PolyPart{move(poly)}; + drawable.color = color; + drawable.position = position; + + return drawable; +} + +Drawable Drawable::makeImage(String image, float pixelSize, bool centered, Vec2F const& position, Color const& color) { + Drawable drawable; + Mat3F transformation = Mat3F::identity(); + if (centered) { + auto imageMetadata = Root::singleton().imageMetadataDatabase(); + Vec2F imageSize = Vec2F(imageMetadata->imageSize(image)); + transformation.translate(-imageSize / 2); + } + + transformation.scale(pixelSize); + + drawable.part = ImagePart{move(image), move(transformation)}; + drawable.position = position; + drawable.color = color; + + return drawable; +} + +Drawable::Drawable() + : color(Color::White), fullbright(false) {} + +Drawable::Drawable(Json const& json) { + if (auto line = json.opt("line")) { + part = LinePart{jsonToLine2F(*line), json.getFloat("width")}; + } else if (auto poly = json.opt("poly")) { + part = PolyPart{jsonToPolyF(*poly)}; + } else if (auto image = json.opt("image")) { + auto imageString = image->toString(); + Mat3F transformation = Mat3F::identity(); + if (auto transformationConfig = json.opt("transformation")) { + transformation = jsonToMat3F(*transformationConfig); + } else { + if (json.getBool("centered", true)) { + auto imageMetadata = Root::singleton().imageMetadataDatabase(); + Vec2F imageSize = Vec2F(imageMetadata->imageSize(imageString)); + transformation.translate(-imageSize / 2); + } + if (auto rotation = json.optFloat("rotation")) + transformation.rotate(*rotation); + if (json.getBool("mirrored", false)) + transformation.scale(Vec2F(-1, 1)); + if (auto scale = json.optFloat("scale")) + transformation.scale(*scale); + } + + part = ImagePart{move(imageString), move(transformation)}; + } + position = json.opt("position").apply(jsonToVec2F).value(); + color = json.opt("color").apply(jsonToColor).value(Color::White); + fullbright = json.getBool("fullbright", false); +} + +Json Drawable::toJson() const { + JsonObject json; + if (auto line = part.ptr<LinePart>()) { + json.set("line", jsonFromLine2F(line->line)); + json.set("width", line->width); + } else if (auto poly = part.ptr<PolyPart>()) { + json.set("poly", jsonFromPolyF(poly->poly)); + } else if (auto image = part.ptr<ImagePart>()) { + json.set("image", image->image); + json.set("transformation", jsonFromMat3F(image->transformation)); + } + + json.set("position", jsonFromVec2F(position)); + json.set("color", jsonFromColor(color)); + json.set("fullbright", fullbright); + + return move(json); +} + +void Drawable::translate(Vec2F const& translation) { + position += translation; +} + +void Drawable::rotate(float rotation, Vec2F const& rotationCenter) { + if (auto line = part.ptr<LinePart>()) + line->line.rotate(rotation); + else if (auto poly = part.ptr<PolyPart>()) + poly->poly.rotate(rotation); + else if (auto image = part.ptr<ImagePart>()) + image->transformation.rotate(rotation); + + position = (position - rotationCenter).rotate(rotation) + rotationCenter; +} + +void Drawable::scale(float scaling, Vec2F const& scaleCenter) { + scale(Vec2F::filled(scaling), scaleCenter); +} + +void Drawable::scale(Vec2F const& scaling, Vec2F const& scaleCenter) { + if (auto line = part.ptr<LinePart>()) + line->line.scale(scaling); + else if (auto poly = part.ptr<PolyPart>()) + poly->poly.scale(scaling); + else if (auto image = part.ptr<ImagePart>()) + image->transformation.scale(scaling); + + position = (position - scaleCenter).piecewiseMultiply(scaling) + scaleCenter; +} + +void Drawable::transform(Mat3F const& transformation) { + Vec2F localTranslation = transformation.transformVec2(Vec2F()); + Mat3F localTransform = Mat3F::translation(-localTranslation) * transformation; + + if (auto line = part.ptr<LinePart>()) + line->line.transform(localTransform); + else if (auto poly = part.ptr<PolyPart>()) + poly->poly.transform(localTransform); + else if (auto image = part.ptr<ImagePart>()) + image->transformation = localTransform * image->transformation; + + position = transformation.transformVec2(position); +} + +void Drawable::rebase(Vec2F const& newBase) { + if (auto line = part.ptr<LinePart>()) + line->line.translate(position - newBase); + else if (auto poly = part.ptr<PolyPart>()) + poly->poly.translate(position - newBase); + else if (auto image = part.ptr<ImagePart>()) + image->transformation.translate(position - newBase); + + position = newBase; +} + +RectF Drawable::boundBox(bool cropImages) const { + RectF boundBox = RectF::null(); + if (auto line = part.ptr<LinePart>()) { + boundBox.combine(line->line.min()); + boundBox.combine(line->line.max()); + + } else if (auto poly = part.ptr<PolyPart>()) { + boundBox.combine(poly->poly.boundBox()); + + } else if (auto image = part.ptr<ImagePart>()) { + auto imageMetadata = Root::singleton().imageMetadataDatabase(); + RectF imageRegion = RectF::null(); + if (cropImages) { + RectU nonEmptyRegion = imageMetadata->nonEmptyRegion(image->image); + if (!nonEmptyRegion.isNull()) + imageRegion = RectF(nonEmptyRegion); + } else { + imageRegion = RectF::withSize(Vec2F(), Vec2F(imageMetadata->imageSize(image->image))); + } + + if (!imageRegion.isNull()) { + boundBox.combine(image->transformation.transformVec2(Vec2F(imageRegion.xMin(), imageRegion.yMin()))); + boundBox.combine(image->transformation.transformVec2(Vec2F(imageRegion.xMax(), imageRegion.yMin()))); + boundBox.combine(image->transformation.transformVec2(Vec2F(imageRegion.xMin(), imageRegion.yMax()))); + boundBox.combine(image->transformation.transformVec2(Vec2F(imageRegion.xMax(), imageRegion.yMax()))); + } + } + + if (!boundBox.isNull()) + boundBox.translate(position); + + return boundBox; +} + +DataStream& operator>>(DataStream& ds, Drawable::LinePart& line) { + ds >> line.line; + ds >> line.width; + return ds; +} + +DataStream& operator<<(DataStream& ds, Drawable::LinePart const& line) { + ds << line.line; + ds << line.width; + return ds; +} + +DataStream& operator>>(DataStream& ds, Drawable::PolyPart& poly) { + ds >> poly.poly; + return ds; +} + +DataStream& operator<<(DataStream& ds, Drawable::PolyPart const& poly) { + ds << poly.poly; + return ds; +} + +DataStream& operator>>(DataStream& ds, Drawable::ImagePart& image) { + ds >> image.image; + ds >> image.transformation; + return ds; +} + +DataStream& operator<<(DataStream& ds, Drawable::ImagePart const& image) { + ds << image.image; + ds << image.transformation; + return ds; +} + +DataStream& operator>>(DataStream& ds, Drawable& drawable) { + ds >> drawable.part; + ds >> drawable.position; + ds >> drawable.color; + ds >> drawable.fullbright; + return ds; +} + +DataStream& operator<<(DataStream& ds, Drawable const& drawable) { + ds << drawable.part; + ds << drawable.position; + ds << drawable.color; + ds << drawable.fullbright; + return ds; +} + +} |