2023-06-20 04:33:09 +00:00
|
|
|
#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 {
|
|
|
|
|
2023-06-24 12:49:47 +00:00
|
|
|
Drawable::ImagePart& Drawable::ImagePart::addDirectives(Directives const& directives, bool keepImageCenterPosition) {
|
2023-06-24 15:16:40 +00:00
|
|
|
if (!directives)
|
2023-06-24 12:49:47 +00:00
|
|
|
return *this;
|
2023-06-20 04:33:09 +00:00
|
|
|
|
|
|
|
if (keepImageCenterPosition) {
|
|
|
|
auto imageMetadata = Root::singleton().imageMetadataDatabase();
|
|
|
|
Vec2F imageSize = Vec2F(imageMetadata->imageSize(image));
|
2023-06-24 12:49:47 +00:00
|
|
|
image.directives += directives;
|
2023-06-20 04:33:09 +00:00
|
|
|
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 {
|
2023-06-24 12:49:47 +00:00
|
|
|
image.directives += directives;
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
2023-06-24 12:49:47 +00:00
|
|
|
|
|
|
|
return *this;
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
2023-06-25 08:12:54 +00:00
|
|
|
Drawable::ImagePart& Drawable::ImagePart::addDirectivesGroup(DirectivesGroup const& directivesGroup, bool keepImageCenterPosition) {
|
|
|
|
if (directivesGroup.empty())
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
if (keepImageCenterPosition) {
|
|
|
|
auto imageMetadata = Root::singleton().imageMetadataDatabase();
|
|
|
|
Vec2F imageSize = Vec2F(imageMetadata->imageSize(image));
|
|
|
|
for (Directives const& directives : directivesGroup.list())
|
|
|
|
image.directives += 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 {
|
|
|
|
for (Directives const& directives : directivesGroup.list())
|
|
|
|
image.directives += directives;
|
|
|
|
}
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2023-06-24 12:49:47 +00:00
|
|
|
Drawable::ImagePart& Drawable::ImagePart::removeDirectives(bool keepImageCenterPosition) {
|
2023-06-20 04:33:09 +00:00
|
|
|
if (keepImageCenterPosition) {
|
|
|
|
auto imageMetadata = Root::singleton().imageMetadataDatabase();
|
|
|
|
Vec2F imageSize = Vec2F(imageMetadata->imageSize(image));
|
2023-06-24 12:49:47 +00:00
|
|
|
image.directives.clear();
|
2023-06-20 04:33:09 +00:00
|
|
|
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 {
|
2023-06-24 12:49:47 +00:00
|
|
|
image.directives.clear();
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
2023-06-24 12:49:47 +00:00
|
|
|
|
|
|
|
return *this;
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Drawable Drawable::makeLine(Line2F const& line, float lineWidth, Color const& color, Vec2F const& position) {
|
|
|
|
Drawable drawable;
|
2024-02-28 17:11:55 +00:00
|
|
|
drawable.part = LinePart{std::move(line), lineWidth, {}};
|
2023-06-20 04:33:09 +00:00
|
|
|
drawable.color = color;
|
|
|
|
drawable.position = position;
|
|
|
|
|
|
|
|
return drawable;
|
|
|
|
}
|
|
|
|
|
|
|
|
Drawable Drawable::makePoly(PolyF poly, Color const& color, Vec2F const& position) {
|
|
|
|
Drawable drawable;
|
2024-02-19 15:55:19 +00:00
|
|
|
drawable.part = PolyPart{std::move(poly)};
|
2023-06-20 04:33:09 +00:00
|
|
|
drawable.color = color;
|
|
|
|
drawable.position = position;
|
|
|
|
|
|
|
|
return drawable;
|
|
|
|
}
|
|
|
|
|
2023-06-24 12:49:47 +00:00
|
|
|
Drawable Drawable::makeImage(AssetPath image, float pixelSize, bool centered, Vec2F const& position, Color const& color) {
|
2023-06-20 04:33:09 +00:00
|
|
|
Drawable drawable;
|
|
|
|
Mat3F transformation = Mat3F::identity();
|
|
|
|
if (centered) {
|
|
|
|
auto imageMetadata = Root::singleton().imageMetadataDatabase();
|
|
|
|
Vec2F imageSize = Vec2F(imageMetadata->imageSize(image));
|
|
|
|
transformation.translate(-imageSize / 2);
|
|
|
|
}
|
|
|
|
|
2023-07-02 22:51:42 +00:00
|
|
|
if (pixelSize != 1.0f)
|
|
|
|
transformation.scale(pixelSize);
|
2023-06-20 04:33:09 +00:00
|
|
|
|
2024-02-19 15:55:19 +00:00
|
|
|
drawable.part = ImagePart{std::move(image), std::move(transformation)};
|
2023-06-20 04:33:09 +00:00
|
|
|
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")) {
|
2024-02-28 17:11:55 +00:00
|
|
|
part = LinePart{jsonToLine2F(*line), json.getFloat("width"), {}};
|
2023-06-20 04:33:09 +00:00
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
|
2024-02-19 15:55:19 +00:00
|
|
|
part = ImagePart{std::move(imageString), std::move(transformation)};
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
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>()) {
|
2023-06-24 12:49:47 +00:00
|
|
|
json.set("image", AssetPath::join(image->image));
|
2023-06-20 04:33:09 +00:00
|
|
|
json.set("transformation", jsonFromMat3F(image->transformation));
|
|
|
|
}
|
|
|
|
|
|
|
|
json.set("position", jsonFromVec2F(position));
|
|
|
|
json.set("color", jsonFromColor(color));
|
|
|
|
json.set("fullbright", fullbright);
|
|
|
|
|
2023-06-26 18:48:27 +00:00
|
|
|
return json;
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-06-24 12:49:47 +00:00
|
|
|
// I need to find out if this is for network serialization or not eventually
|
2023-06-20 04:33:09 +00:00
|
|
|
DataStream& operator>>(DataStream& ds, Drawable::ImagePart& image) {
|
2023-06-26 18:48:27 +00:00
|
|
|
ds >> image.image;
|
2023-06-20 04:33:09 +00:00
|
|
|
ds >> image.transformation;
|
|
|
|
return ds;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataStream& operator<<(DataStream& ds, Drawable::ImagePart const& image) {
|
2023-06-26 18:48:27 +00:00
|
|
|
ds << image.image;
|
2023-06-20 04:33:09 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|