Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 30e04bcb3cf6
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 9da8622d7c49
Choose a head ref
  • 9 commits
  • 8 files changed
  • 1 contributor

Commits on Dec 16, 2019

  1. Signature

    eggrobin committed Dec 16, 2019
    Copy the full SHA
    dc02fb3 View commit details

Commits on Dec 17, 2019

  1. basic tests

    eggrobin committed Dec 17, 2019
    Copy the full SHA
    ed3381f View commit details

Commits on Dec 21, 2019

  1. name

    eggrobin committed Dec 21, 2019
    Copy the full SHA
    1277feb View commit details

Commits on Dec 23, 2019

  1. Forget

    eggrobin committed Dec 23, 2019
    Copy the full SHA
    2905732 View commit details
  2. test output

    eggrobin committed Dec 23, 2019
    Copy the full SHA
    5c4728b View commit details
  3. serialization

    eggrobin committed Dec 23, 2019
    Copy the full SHA
    4c8d226 View commit details
  4. comments

    eggrobin committed Dec 23, 2019
    Copy the full SHA
    fbb45e5 View commit details

Commits on Dec 25, 2019

  1. after pleroy's review

    eggrobin committed Dec 25, 2019
    Copy the full SHA
    11a3b94 View commit details
  2. Merge pull request #2411 from eggrobin/signature

    Signature
    eggrobin authored Dec 25, 2019
    Copy the full SHA
    9da8622 View commit details
3 changes: 3 additions & 0 deletions geometry/geometry.vcxproj
Original file line number Diff line number Diff line change
@@ -46,6 +46,8 @@
<ClInclude Include="rp2_point_body.hpp" />
<ClInclude Include="serialization.hpp" />
<ClInclude Include="serialization_body.hpp" />
<ClInclude Include="signature.hpp" />
<ClInclude Include="signature_body.hpp" />
<ClInclude Include="sign_body.hpp" />
<ClInclude Include="sign.hpp" />
<ClInclude Include="sphere.hpp" />
@@ -69,6 +71,7 @@
<ClCompile Include="r3_element_test.cpp" />
<ClCompile Include="rotation_test.cpp" />
<ClCompile Include="rp2_point_test.cpp" />
<ClCompile Include="signature_test.cpp" />
<ClCompile Include="sign_test.cpp" />
<ClCompile Include="symmetric_bilinear_form_test.cpp" />
</ItemGroup>
9 changes: 9 additions & 0 deletions geometry/geometry.vcxproj.filters
Original file line number Diff line number Diff line change
@@ -152,6 +152,12 @@
<ClInclude Include="interval_body.hpp">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="signature.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="signature_body.hpp">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="sign_test.cpp">
@@ -205,5 +211,8 @@
<ClCompile Include="symmetric_bilinear_form_test.cpp">
<Filter>Test Files</Filter>
</ClCompile>
<ClCompile Include="signature_test.cpp">
<Filter>Test Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
5 changes: 5 additions & 0 deletions geometry/orthogonal_map.hpp
Original file line number Diff line number Diff line change
@@ -26,6 +26,9 @@ FORWARD_DECLARE_FROM(permutation,
FORWARD_DECLARE_FROM(rotation,
TEMPLATE(typename FromFrame, typename ToFrame) class,
Rotation);
FORWARD_DECLARE_FROM(signature,
TEMPLATE(typename FromFrame, typename ToFrame) class,
Signature);
FORWARD_DECLARE_FROM(symmetric_bilinear_form,
TEMPLATE(typename Scalar, typename Frame) class,
SymmetricBilinearForm);
@@ -100,6 +103,8 @@ class OrthogonalMap : public LinearMap<FromFrame, ToFrame> {
friend class internal_permutation::Permutation;
template<typename From, typename To>
friend class internal_rotation::Rotation;
template<typename From, typename To>
friend class internal_signature::Signature;

template<typename From, typename Through, typename To>
friend OrthogonalMap<From, To> operator*(
2 changes: 1 addition & 1 deletion geometry/orthogonal_map_body.hpp
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ template<typename FromFrame, typename ToFrame>
template<typename Scalar>
Trivector<Scalar, ToFrame> OrthogonalMap<FromFrame, ToFrame>::operator()(
Trivector<Scalar, FromFrame> const& trivector) const {
return determinant_ * trivector;
return Trivector<Scalar, ToFrame>(determinant_ * trivector.coordinates());
}

template<typename FromFrame, typename ToFrame>
119 changes: 119 additions & 0 deletions geometry/signature.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#pragma once

#include "geometry/frame.hpp"
#include "geometry/orthogonal_map.hpp"
#include "geometry/sign.hpp"

namespace principia {
namespace geometry {
namespace internal_signature {

using base::not_null;

struct DeduceSignPreservingOrientation final {};
struct DeduceSignReversingOrientation final {};

// A coordinate change whose matrix is a signature matrix, i.e., a diagonal
// matrix with ±1 on the diagonal. There are 8 possible signatures: the
// identity 𝟙, the central inversion -𝟙, the 180° rotations around all three
// axes, and the reflections across the planes orthogonal to all three axes.
template<typename FromFrame, typename ToFrame>
class Signature : public LinearMap<FromFrame, ToFrame> {
public:
using DeduceSign =
std::conditional_t<FromFrame::handedness == ToFrame::handedness,
DeduceSignPreservingOrientation,
DeduceSignReversingOrientation>;

constexpr Signature(Sign x, Sign y, DeduceSign z);
constexpr Signature(Sign x, DeduceSign y, Sign z);
constexpr Signature(DeduceSign x, Sign y, Sign z);

constexpr Sign Determinant() const override;

template<typename F = FromFrame,
typename T = ToFrame,
typename = std::enable_if_t<F::handedness == T::handedness>>
static constexpr Signature Identity();

template<typename F = FromFrame,
typename T = ToFrame,
typename = std::enable_if_t<F::handedness != T::handedness>>
static constexpr Signature CentralInversion();

constexpr Signature<ToFrame, FromFrame> Inverse() const;

template<typename Scalar>
Vector<Scalar, ToFrame> operator()(
Vector<Scalar, FromFrame> const& vector) const;

template<typename Scalar>
Bivector<Scalar, ToFrame> operator()(
Bivector<Scalar, FromFrame> const& bivector) const;

template<typename Scalar>
Trivector<Scalar, ToFrame> operator()(
Trivector<Scalar, FromFrame> const& trivector) const;

template<typename Scalar>
SymmetricBilinearForm<Scalar, ToFrame> operator()(
SymmetricBilinearForm<Scalar, FromFrame> const& form) const;

OrthogonalMap<FromFrame, ToFrame> Forget() const;

void WriteToMessage(not_null<serialization::LinearMap*> message) const;
template<typename F = FromFrame,
typename T = ToFrame,
typename = std::enable_if_t<base::is_serializable_v<F> &&
base::is_serializable_v<T>>>
static Signature ReadFromMessage(serialization::LinearMap const& message);

void WriteToMessage(not_null<serialization::Signature*> message) const;
template<typename F = FromFrame,
typename T = ToFrame,
typename = std::enable_if_t<base::is_serializable_v<F> &&
base::is_serializable_v<T>>>
static Signature ReadFromMessage(serialization::Signature const& message);

private:
constexpr Signature(Sign x, Sign y, Sign z);

Sign x_;
Sign y_;
Sign z_;

static constexpr Sign determinant_ =
FromFrame::handedness == ToFrame::handedness ? Sign::Positive()
: Sign::Negative();

template<typename From, typename To>
friend class Signature;

template<typename From, typename Through, typename To>
friend Signature<From, To> operator*(Signature<Through, To> const& left,
Signature<From, Through> const& right);

template<typename From, typename To>
friend std::ostream& operator<<(std::ostream& out,
Signature<From, To> const& signature);
};

template<typename FromFrame, typename ThroughFrame, typename ToFrame>
Signature<FromFrame, ToFrame> operator*(
Signature<ThroughFrame, ToFrame> const& left,
Signature<FromFrame, ThroughFrame> const& right);

template<typename FromFrame, typename ToFrame>
std::ostream& operator<<(std::ostream& out,
Signature<FromFrame, ToFrame> const& signature);

} // namespace internal_signature

using internal_signature::DeduceSignPreservingOrientation;
using internal_signature::DeduceSignReversingOrientation;
using internal_signature::Signature;

} // namespace geometry
} // namespace principia

#include "geometry/signature_body.hpp"
169 changes: 169 additions & 0 deletions geometry/signature_body.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#pragma once

#include "geometry/signature.hpp"

namespace principia {
namespace geometry {
namespace internal_signature {

template<typename FromFrame, typename ToFrame>
constexpr Signature<FromFrame, ToFrame>::Signature(Sign const x,
Sign const y,
DeduceSign const z)
: x_(x), y_(y), z_(x * y * determinant_) {}

template<typename FromFrame, typename ToFrame>
constexpr Signature<FromFrame, ToFrame>::Signature(Sign const x,
DeduceSign const y,
Sign const z)
: x_(x), y_(x * determinant_ * z), z_(z) {}

template<typename FromFrame, typename ToFrame>
constexpr Signature<FromFrame, ToFrame>::Signature(DeduceSign const x,
Sign const y,
Sign const z)
: x_(determinant_ * y * z), y_(y), z_(z) {}

template<typename FromFrame, typename ToFrame>
constexpr Sign Signature<FromFrame, ToFrame>::Determinant() const {
return determinant_;
}

template<typename FromFrame, typename ToFrame>
constexpr Signature<ToFrame, FromFrame> Signature<FromFrame, ToFrame>::Inverse()
const {
return Signature<ToFrame, FromFrame>(x_, y_, z_);
}

template<typename FromFrame, typename ToFrame>
template<typename F, typename T, typename>
constexpr Signature<FromFrame, ToFrame>
Signature<FromFrame, ToFrame>::Identity() {
return Signature(
Sign::Positive(), Sign::Positive(), DeduceSignPreservingOrientation{});
}

template<typename FromFrame, typename ToFrame>
template<typename F, typename T, typename>
constexpr Signature<FromFrame, ToFrame>
Signature<FromFrame, ToFrame>::CentralInversion() {
return Signature(
Sign::Negative(), Sign::Negative(), DeduceSignReversingOrientation{});
}

template<typename FromFrame, typename ToFrame>
template<typename Scalar>
Vector<Scalar, ToFrame> Signature<FromFrame, ToFrame>::operator()(
Vector<Scalar, FromFrame> const& vector) const {
return Vector<Scalar, ToFrame>({x_ * vector.coordinates().x,
y_ * vector.coordinates().y,
z_ * vector.coordinates().z});
}

template<typename FromFrame, typename ToFrame>
template<typename Scalar>
Bivector<Scalar, ToFrame> Signature<FromFrame, ToFrame>::operator()(
Bivector<Scalar, FromFrame> const& bivector) const {
return Bivector<Scalar, ToFrame>(
{determinant_ * x_ * bivector.coordinates().x,
determinant_ * y_ * bivector.coordinates().y,
determinant_ * z_ * bivector.coordinates().z});
}

template<typename FromFrame, typename ToFrame>
template<typename Scalar>
Trivector<Scalar, ToFrame> Signature<FromFrame, ToFrame>::operator()(
Trivector<Scalar, FromFrame> const& trivector) const {
return Trivector<Scalar, ToFrame>(determinant_ * trivector.coordinates());
}

template<typename FromFrame, typename ToFrame>
template<typename Scalar>
SymmetricBilinearForm<Scalar, ToFrame>
Signature<FromFrame, ToFrame>::operator()(
SymmetricBilinearForm<Scalar, FromFrame> const& form) const {
return SymmetricBilinearForm<Scalar, ToFrame>();
}

template<typename FromFrame, typename ToFrame>
OrthogonalMap<FromFrame, ToFrame> Signature<FromFrame, ToFrame>::Forget()
const {
if (x_ == y_ && y_ == z_) {
// TODO(phl): unsound rotation; this should use |Identity| once we go
// through an intermediate frame.
return OrthogonalMap<FromFrame, ToFrame>(
determinant_, Rotation<FromFrame, ToFrame>(Quaternion(1)));
}
// The signature is neither +++ nor ---, so dividing it by its determinant
// yields a 180° rotation around one of the axes (+--, -+-, or --+).
R3Element<double> const axis = (determinant_ * x_).is_positive()
? R3Element<double>{1, 0, 0}
: (determinant_ * y_).is_positive()
? R3Element<double>{0, 1, 0}
: R3Element<double>{0, 0, 1};
// TODO(phl): unsound rotation.
return OrthogonalMap<FromFrame, ToFrame>(
determinant_, Rotation<FromFrame, ToFrame>(Quaternion(0, axis)));
}

template<typename FromFrame, typename ToFrame>
void Signature<FromFrame, ToFrame>::WriteToMessage(
not_null<serialization::LinearMap*> const message) const {
LinearMap<FromFrame, ToFrame>::WriteToMessage(message);
WriteToMessage(
message->MutableExtension(serialization::Signature::extension));
}

template<typename FromFrame, typename ToFrame>
template<typename, typename, typename>
Signature<FromFrame, ToFrame> Signature<FromFrame, ToFrame>::ReadFromMessage(
serialization::LinearMap const& message) {
LinearMap<FromFrame, ToFrame>::ReadFromMessage(message);
CHECK(message.HasExtension(serialization::Signature::extension));
return ReadFromMessage(
message.GetExtension(serialization::Signature::extension));
}

template<typename FromFrame, typename ToFrame>
void Signature<FromFrame, ToFrame>::WriteToMessage(
not_null<serialization::Signature*> const message) const {
x_.WriteToMessage(message->mutable_x());
y_.WriteToMessage(message->mutable_y());
z_.WriteToMessage(message->mutable_z());
}

template<typename FromFrame, typename ToFrame>
template<typename, typename, typename>
Signature<FromFrame, ToFrame> Signature<FromFrame, ToFrame>::ReadFromMessage(
serialization::Signature const& message) {
auto const x = Sign::ReadFromMessage(message.x());
auto const y = Sign::ReadFromMessage(message.y());
auto const z = Sign::ReadFromMessage(message.z());
CHECK_EQ(x * y * z, determinant_) << message.DebugString();
return Signature(x, y, z);
}

template<typename FromFrame, typename ToFrame>
constexpr Signature<FromFrame, ToFrame>::Signature(Sign const x,
Sign const y,
Sign const z)
: x_(x), y_(y), z_(z) {}

template<typename FromFrame, typename ThroughFrame, typename ToFrame>
Signature<FromFrame, ToFrame> operator*(
Signature<ThroughFrame, ToFrame> const& left,
Signature<FromFrame, ThroughFrame> const& right) {
return Signature<FromFrame, ToFrame>(
left.x_ * right.x_, left.y_ * right.y_, left.z_ * right.z_);
}

template<typename FromFrame, typename ToFrame>
std::ostream& operator<<(std::ostream& out,
Signature<FromFrame, ToFrame> const& signature) {
return out << "{" << signature.x_ << ", " << signature.y_ << ", "
<< signature.z_ << "}";
}

} // namespace internal_signature
} // namespace geometry
} // namespace principia
218 changes: 218 additions & 0 deletions geometry/signature_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@

#include "geometry/signature.hpp"

#include <vector>

#include "geometry/frame.hpp"
#include "geometry/identity.hpp"
#include "geometry/orthogonal_map.hpp"
#include "geometry/r3_element.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/si.hpp"
#include "serialization/geometry.pb.h"
#include "testing_utilities/componentwise.hpp"

namespace principia {

using quantities::Length;
using quantities::si::Metre;
using ::testing::Eq;
using testing_utilities::Componentwise;

namespace geometry {

class SignatureTest : public testing::Test {
protected:
using R1 = Frame<serialization::Frame::TestTag,
Inertial,
Handedness::Right,
serialization::Frame::TEST1>;
using R2 = Frame<serialization::Frame::TestTag,
Inertial,
Handedness::Right,
serialization::Frame::TEST2>;
using L = Frame<enum class LTag, Inertial, Handedness::Left>;

using PositiveSignature = Signature<R1, R2>;
using NegativeSignature = Signature<R1, L>;

SignatureTest()
: vector_({1 * Metre, 2 * Metre, 3 * Metre}),
bivector_({1 * Metre, 2 * Metre, 3 * Metre}),
trivector_(4 * Metre) {}

Vector<Length, R1> vector_;
Bivector<Length, R1> bivector_;
Trivector<Length, R1> trivector_;
};

using SignatureDeathTest = SignatureTest;

TEST_F(SignatureTest, Forget) {
std::array<PositiveSignature, 4> all_positive_signatures{
{PositiveSignature::Identity(),
PositiveSignature(Sign::Positive(),
Sign::Negative(),
DeduceSignPreservingOrientation{}),
PositiveSignature(Sign::Negative(),
Sign::Positive(),
DeduceSignPreservingOrientation{}),
PositiveSignature(Sign::Negative(),
Sign::Negative(),
DeduceSignPreservingOrientation{})}};
std::array<NegativeSignature, 4> all_negative_signatures{
{NegativeSignature::CentralInversion(),
NegativeSignature(Sign::Negative(),
Sign::Positive(),
DeduceSignReversingOrientation{}),
NegativeSignature(Sign::Positive(),
Sign::Negative(),
DeduceSignReversingOrientation{}),
NegativeSignature(Sign::Positive(),
Sign::Positive(),
DeduceSignReversingOrientation{})}};
auto const test_forget_for = [this](auto const& signatures) {
for (auto const& signature : signatures) {
EXPECT_THAT(signature.Forget()(vector_), Eq(signature(vector_)))
<< signature;
EXPECT_THAT(signature.Forget()(bivector_), Eq(signature(bivector_)))
<< signature;
EXPECT_THAT(signature.Forget()(trivector_), Eq(signature(trivector_)))
<< signature;
}
};
test_forget_for(all_positive_signatures);
test_forget_for(all_negative_signatures);
}

TEST_F(SignatureTest, Identity) {
EXPECT_THAT(PositiveSignature::Identity()(vector_).coordinates(),
Eq(vector_.coordinates()));
EXPECT_THAT(PositiveSignature::Identity()(bivector_).coordinates(),
Eq(bivector_.coordinates()));
EXPECT_THAT(PositiveSignature::Identity()(trivector_).coordinates(),
Eq(trivector_.coordinates()));
}

TEST_F(SignatureTest, CentralInversion) {
EXPECT_THAT(NegativeSignature::CentralInversion()(vector_).coordinates(),
Eq(-vector_.coordinates()));
EXPECT_THAT(NegativeSignature::CentralInversion()(bivector_).coordinates(),
Eq(bivector_.coordinates()));
EXPECT_THAT(NegativeSignature::CentralInversion()(trivector_).coordinates(),
Eq(-trivector_.coordinates()));
}

TEST_F(SignatureTest, XYPlaneReflection) {
NegativeSignature const reflection(
Sign::Positive(), Sign::Positive(), DeduceSignReversingOrientation{});
EXPECT_THAT(reflection(vector_).coordinates(),
Componentwise(1 * Metre, 2 * Metre, -3 * Metre));
EXPECT_THAT(reflection(bivector_).coordinates(),
Componentwise(-1 * Metre, -2 * Metre, 3 * Metre));
EXPECT_THAT(reflection(trivector_).coordinates(),
Eq(-trivector_.coordinates()));
}

TEST_F(SignatureTest, YZPlaneReflection) {
NegativeSignature const reflection(
DeduceSignReversingOrientation{}, Sign::Positive(), Sign::Positive());
EXPECT_THAT(reflection(vector_).coordinates(),
Componentwise(-1 * Metre, 2 * Metre, 3 * Metre));
EXPECT_THAT(reflection(bivector_).coordinates(),
Componentwise(1 * Metre, -2 * Metre, -3 * Metre));
EXPECT_THAT(reflection(trivector_).coordinates(),
Eq(-trivector_.coordinates()));
}

TEST_F(SignatureTest, XZPlaneReflection) {
NegativeSignature rotation(
Sign::Positive(), DeduceSignReversingOrientation{}, Sign::Positive());
EXPECT_THAT(rotation(vector_).coordinates(),
Componentwise(1 * Metre, -2 * Metre, 3 * Metre));
EXPECT_THAT(rotation(bivector_).coordinates(),
Componentwise(-1 * Metre, 2 * Metre, -3 * Metre));
EXPECT_THAT(rotation(trivector_).coordinates(),
Eq(-trivector_.coordinates()));
}

TEST_F(SignatureTest, XAxisRotation) {
PositiveSignature const rotation(
Sign::Positive(), Sign::Negative(), DeduceSignPreservingOrientation{});
EXPECT_THAT(rotation(vector_).coordinates(),
Componentwise(1 * Metre, -2 * Metre, -3 * Metre));
EXPECT_THAT(rotation(bivector_).coordinates(),
Componentwise(1 * Metre, -2 * Metre, -3 * Metre));
EXPECT_THAT(rotation(trivector_).coordinates(), Eq(trivector_.coordinates()));
}

TEST_F(SignatureTest, YAxisRotation) {
PositiveSignature const rotation(
Sign::Negative(), Sign::Positive(), DeduceSignPreservingOrientation{});
EXPECT_THAT(rotation(vector_).coordinates(),
Componentwise(-1 * Metre, 2 * Metre, -3 * Metre));
EXPECT_THAT(rotation(bivector_).coordinates(),
Componentwise(-1 * Metre, 2 * Metre, -3 * Metre));
EXPECT_THAT(rotation(trivector_).coordinates(), Eq(trivector_.coordinates()));
}

TEST_F(SignatureTest, ZAxisRotation) {
PositiveSignature const rotation(
Sign::Negative(), Sign::Negative(), DeduceSignPreservingOrientation{});
EXPECT_THAT(rotation(vector_).coordinates(),
Componentwise(-1 * Metre, -2 * Metre, 3 * Metre));
EXPECT_THAT(rotation(bivector_).coordinates(),
Componentwise(-1 * Metre, -2 * Metre, 3 * Metre));
EXPECT_THAT(rotation(trivector_).coordinates(), Eq(trivector_.coordinates()));
}

TEST_F(SignatureTest, Inversion) {
NegativeSignature const reflection(
Sign::Negative(), Sign::Positive(), DeduceSignReversingOrientation{});
PositiveSignature const rotation(
Sign::Negative(), Sign::Positive(), DeduceSignPreservingOrientation{});
EXPECT_THAT(rotation.Inverse()(rotation(vector_)), Eq(vector_));
EXPECT_THAT(reflection.Inverse()(reflection(vector_)), Eq(vector_));
EXPECT_THAT(rotation.Inverse()(rotation(bivector_)), Eq(bivector_));
EXPECT_THAT(reflection.Inverse()(reflection(bivector_)), Eq(bivector_));
EXPECT_THAT(rotation.Inverse()(rotation(trivector_)), Eq(trivector_));
EXPECT_THAT(reflection.Inverse()(reflection(trivector_)), Eq(trivector_));
}

TEST_F(SignatureTest, Composition) {
Signature<R2, L> reflection(
Sign::Negative(), Sign::Positive(), DeduceSignReversingOrientation{});
Signature<R1, R2> rotation(
Sign::Negative(), Sign::Positive(), DeduceSignPreservingOrientation{});

EXPECT_THAT((reflection * rotation)(vector_),
Eq(reflection(rotation(vector_))));
EXPECT_THAT((reflection * rotation)(bivector_),
Eq(reflection(rotation(bivector_))));
EXPECT_THAT((reflection * rotation)(trivector_),
Eq(reflection(rotation(trivector_))));
}

TEST_F(SignatureTest, Serialization) {
serialization::Signature message;

PositiveSignature signature(
Sign::Positive(), Sign::Negative(), DeduceSignPreservingOrientation{});
signature.WriteToMessage(&message);
EXPECT_THAT(PositiveSignature::ReadFromMessage(message)(vector_),
Eq(signature(vector_)));
}

TEST_F(SignatureTest, Output) {
EXPECT_THAT((std::stringstream{}
<< PositiveSignature(Sign::Positive(),
Sign::Negative(),
DeduceSignPreservingOrientation{}))
.str(),
Eq("{+, -, -}"));
}

} // namespace geometry
} // namespace principia
11 changes: 10 additions & 1 deletion serialization/geometry.proto
Original file line number Diff line number Diff line change
@@ -78,7 +78,7 @@ message SymmetricBilinearForm {
message LinearMap {
required Frame from_frame = 1;
required Frame to_frame = 2;
extensions 1000 to 1999; // Last used: 1003.
extensions 1000 to 1999; // Last used: 1004.
}

message Identity {
@@ -132,6 +132,15 @@ message Rotation {
required Quaternion quaternion = 1;
}

message Signature {
extend LinearMap {
optional Signature extension = 1004;
}
required Sign x = 1;
required Sign y = 2;
required Sign z = 3;
}

// Frames follow. Note that the names of the enum types are part of the
// serialized representation (|tag_type_fingerprint| field).