Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ need to include the parts of the library that you need.
```

* `color`: color spaces, encoding, and format conversion.
* `types`: utility types; time, units, constants, small arrays, URIs.
* `types`: utility types; time, units, constants, small arrays.
* `geometry`: geometric types (point, segment, triangle, rect, quad), meshes,
transforms, utility functions, and algorithms (intersection, envelope).
* `strokes`: the primary `Stroke` data type and `InProgressStroke` builder.
Expand Down
6 changes: 0 additions & 6 deletions ink/brush/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,9 @@ cc_library(
":brush_coat",
":brush_paint",
":brush_tip",
"//ink/types:uri",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
],
)
Expand All @@ -138,9 +136,7 @@ cc_test(
"//ink/geometry:point",
"//ink/geometry:vec",
"//ink/types:duration",
"//ink/types:uri",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:status_matchers",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
Expand Down Expand Up @@ -250,10 +246,8 @@ cc_library(
"//ink/geometry:point",
"//ink/geometry:vec",
"//ink/types:fuzz_domains",
"//ink/types:uri",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
"@com_google_fuzztest//fuzztest",
],
Expand Down
4 changes: 2 additions & 2 deletions ink/brush/brush_coat_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ TEST(BrushCoatTest, GetRequiredAttributeIdsWithColorShift) {
TEST(BrushCoatTest, GetRequiredAttributeIdsWithoutWindingTextures) {
BrushPaint paint = {
.texture_layers = {BrushPaint::TextureLayer{
.color_texture_id = std::string(kTestTextureId),
.client_color_texture_id = std::string(kTestTextureId),
.mapping = BrushPaint::TextureMapping::kTiling,
}},
};
Expand All @@ -154,7 +154,7 @@ TEST(BrushCoatTest, GetRequiredAttributeIdsWithoutWindingTextures) {
TEST(BrushCoatTest, GetRequiredAttributeIdsWithWindingTextures) {
BrushPaint paint = {
.texture_layers = {BrushPaint::TextureLayer{
.color_texture_id = std::string(kTestTextureId),
.client_color_texture_id = std::string(kTestTextureId),
.mapping = BrushPaint::TextureMapping::kWinding,
}},
};
Expand Down
69 changes: 12 additions & 57 deletions ink/brush/brush_family.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,18 @@
#include "ink/brush/brush_family.h"

#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "ink/brush/brush_coat.h"
#include "ink/brush/brush_paint.h"
#include "ink/brush/brush_tip.h"
#include "ink/types/uri.h"

namespace ink {

Expand All @@ -47,49 +43,23 @@ uint32_t BrushFamily::MaxBrushCoats() {
return 10;
}

BrushFamily::BrushFamily(std::vector<BrushCoat> coats, std::optional<Uri> uri,
BrushFamily::BrushFamily(absl::Span<const BrushCoat> coats,
absl::string_view client_brush_family_id,
const InputModel& input_model)
: coats_(std::move(coats)),
uri_(std::move(uri)),
: coats_(coats.begin(), coats.end()),
client_brush_family_id_(client_brush_family_id),
input_model_(input_model) {}

absl::StatusOr<BrushFamily> BrushFamily::Create(
absl::Span<const BrushCoat> coats, absl::string_view uri_string,
const InputModel& input_model) {
if (uri_string.empty()) {
return BrushFamily::Create(coats, std::nullopt, input_model);
}
absl::StatusOr<Uri> uri = Uri::Parse(uri_string);
if (!uri.ok()) {
return uri.status();
}
return BrushFamily::Create(coats, *uri, input_model);
}

absl::StatusOr<BrushFamily> BrushFamily::Create(const BrushTip& tip,
const BrushPaint& paint,
absl::string_view uri_string,
const InputModel& input_model) {
const BrushTip& tip, const BrushPaint& paint,
absl::string_view client_brush_family_id, const InputModel& input_model) {
BrushCoat coat = {.tips = {tip}, .paint = paint};
return BrushFamily::Create(absl::MakeConstSpan(&coat, 1), uri_string,
input_model);
}

namespace {

absl::Status ValidateBrushUri(const Uri& uri) {
if (uri.GetAssetType() != Uri::AssetType::kBrushFamily) {
return absl::InvalidArgumentError(absl::StrFormat(
"URI asset-type for a `BrushFamily` must be '%v'. Got '%v'",
Uri::AssetType::kBrushFamily, uri.GetAssetType()));
}
return absl::OkStatus();
return BrushFamily::Create(absl::MakeConstSpan(&coat, 1),
client_brush_family_id, input_model);
}

} // namespace

absl::StatusOr<BrushFamily> BrushFamily::Create(
absl::Span<const BrushCoat> coats, std::optional<Uri> uri,
absl::Span<const BrushCoat> coats, absl::string_view client_brush_family_id,
const InputModel& input_model) {
if (coats.size() > MaxBrushCoats()) {
return absl::InvalidArgumentError(
Expand All @@ -102,29 +72,14 @@ absl::StatusOr<BrushFamily> BrushFamily::Create(
return status;
}
}
if (uri.has_value()) {
if (absl::Status status = ValidateBrushUri(*uri); !status.ok()) {
return status;
}
}
return BrushFamily(std::vector<BrushCoat>(coats.begin(), coats.end()),
std::move(uri), input_model);
}

absl::StatusOr<BrushFamily> BrushFamily::Create(const BrushTip& tip,
const BrushPaint& paint,
std::optional<Uri> uri,
const InputModel& input_model) {
BrushCoat coat = {.tips = {tip}, .paint = paint};
return BrushFamily::Create(absl::MakeConstSpan(&coat, 1), std::move(uri),
input_model);
return BrushFamily(coats, client_brush_family_id, input_model);
}

std::string BrushFamily::ToFormattedString() const {
std::string formatted =
absl::StrCat("BrushFamily(coats=[", absl::StrJoin(coats_, ", "), "]");
if (uri_.has_value()) {
absl::StrAppend(&formatted, ", uri='", *uri_, "'");
if (!client_brush_family_id_.empty()) {
absl::StrAppend(&formatted, ", id='", client_brush_family_id_, "'");
}
formatted.push_back(')');
return formatted;
Expand Down
68 changes: 21 additions & 47 deletions ink/brush/brush_family.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#define INK_STROKES_BRUSH_BRUSH_FAMILY_H_

#include <cstdint>
#include <optional>
#include <string>
#include <variant>
#include <vector>
Expand All @@ -27,22 +26,12 @@
#include "ink/brush/brush_coat.h"
#include "ink/brush/brush_paint.h"
#include "ink/brush/brush_tip.h"
#include "ink/types/uri.h"

namespace ink {

// A `BrushFamily` combines one or more `BrushCoat`s and an optional URI to
// describe a family of brushes.
//
// The URI exists for the convenience of higher level serialization and asset
// management APIs. Aside from being checked for valid formatting and being
// forwarded on copy or move, the URI will not*** be used by Ink APIs that
// consume a `BrushFamily`.
//
// *** During the implementation phase of `BrushTip` and `BrushPaint`, the URI
// will be inspected by stroke mesh creation to enable a set of stock Ink
// brushes. This behavior will be replaced when a helper library is able to fill
// out the specification of the stock brushes using the new tip and paint types.
// A `BrushFamily` combines one or more `BrushCoat`s with an optional
// client-specified string ID, which exists for the convenience of higher level
// serialization and asset management APIs.
class BrushFamily {
public:
// LINT.IfChange(input_model_types)
Expand Down Expand Up @@ -71,11 +60,10 @@ class BrushFamily {
// to have. Note that this limit may increase in the future.
static uint32_t MaxBrushCoats();

// Creates a `BrushFamily` with the given `tips`, `paint`, and an optional
// `uri`.
// Creates a `BrushFamily` with the given `tips`, `paint`, and optional
// string `id`.
//
// Performs validation of the `tips`, `paint`, and the `uri` if present.
// Returns an error if argument validation fails.
// Performs validation of the `tips` and `paint`.
//
// For a tip to be valid the following must hold:
// * Every numeric, `Angle` or `Duration32` property must be finite and
Expand All @@ -86,7 +74,6 @@ class BrushFamily {
//
// For a brush paint to be valid the following must hold:
// * For each texture_layer the following must hold:
// - The color texture URI must be a texture asset type.
// - size components must be finite and greater than 0.
// - offset components must be in interval [0, 1].
// - rotation component must be finite.
Expand All @@ -108,35 +95,14 @@ class BrushFamily {
// rendering tiling and winding textures in a single `BrushPaint`.
// * Every enum property must be equal to one of the named enumerators for
// that property's type.
//
// For a non-nullopt URI to be valid the following must hold:
// * The asset type must be "brush-family".
//
// If the `BrushFamily` has a URI set, other Ink code will implicitly assume
// that (1) the tip/paint data is canonical for that URI, and (2) the client
// application has a `BrushProvider` implementation that can map from that URI
// to the canonical tip/paint data. In particular, when serializing brush data
// to proto, if a URI is set, then only the URI will be stored, and the
// tip/paint data will be omitted.
static absl::StatusOr<BrushFamily> Create(
const BrushTip& tip, const BrushPaint& paint,
std::optional<Uri> uri = std::nullopt,
const InputModel& input_model = DefaultInputModel());
static absl::StatusOr<BrushFamily> Create(
absl::Span<const BrushCoat> coats, std::optional<Uri> uri = std::nullopt,
const InputModel& input_model = DefaultInputModel());
// Same as above, except that the URI is parsed from the given URI string (and
// an error is returned if the URI string fails to parse). An empty URI string
// is treated as a nullopt URI.
static absl::StatusOr<BrushFamily> Create(
const BrushTip& tip, const BrushPaint& paint,
absl::string_view uri_string,
const BrushTip& tip, const BrushPaint& paint, absl::string_view id = "",
const InputModel& input_model = DefaultInputModel());
static absl::StatusOr<BrushFamily> Create(
absl::Span<const BrushCoat> coats, absl::string_view uri_string,
absl::Span<const BrushCoat> coats, absl::string_view id = "",
const InputModel& input_model = DefaultInputModel());

// Constructs a brush-family with default tip and paint, and no URI.
// Constructs a brush-family with default tip and paint and empty id.
BrushFamily() = default;

BrushFamily(const BrushFamily&) = default;
Expand All @@ -145,23 +111,29 @@ class BrushFamily {
BrushFamily& operator=(BrushFamily&&) = default;

absl::Span<const BrushCoat> GetCoats() const;
const std::optional<Uri>& GetUri() const;
const InputModel& GetInputModel() const;

// Returns the ID for this brush family specified by the client that
// originally created it, or an empty string if no ID was specified. This is
// considered when comparing `BrushFaily` objects for equality, but it is
// not assumed that two `BrushFamily` objects with the same IDs are
// equivalent, and the ID is not otherwise used internally by Ink.
const std::string& GetClientBrushFamilyId() const;

template <typename Sink>
friend void AbslStringify(Sink& sink, const BrushFamily& family) {
sink.Append(family.ToFormattedString());
}

private:
BrushFamily(std::vector<BrushCoat> coats, std::optional<Uri> uri,
BrushFamily(absl::Span<const BrushCoat> coats, absl::string_view id,
const InputModel& input_model);

// Implementation helper for AbslStringify.
std::string ToFormattedString() const;

std::vector<BrushCoat> coats_ = {BrushCoat{.tips = {BrushTip{}}}};
std::optional<Uri> uri_;
std::string client_brush_family_id_;
InputModel input_model_;
};

Expand All @@ -172,7 +144,9 @@ inline absl::Span<const BrushCoat> BrushFamily::GetCoats() const {
return coats_;
}

inline const std::optional<Uri>& BrushFamily::GetUri() const { return uri_; }
inline const std::string& BrushFamily::GetClientBrushFamilyId() const {
return client_brush_family_id_;
}

inline const BrushFamily::InputModel& BrushFamily::GetInputModel() const {
return input_model_;
Expand Down
Loading