add Node/Network

This commit is contained in:
falsycat 2022-05-29 12:00:58 +09:00
parent 3dfad9768b
commit 3275e6280d
14 changed files with 2124 additions and 0 deletions

View File

@ -44,19 +44,30 @@ target_sources(nf7
nf7.cc
nf7.hh
common/aggregate_command.hh
common/dir.hh
common/dir_item.hh
common/file_ref.hh
common/generic_context.hh
common/generic_history.hh
common/generic_memento.hh
common/generic_type_info.hh
common/gui_file.hh
common/gui_node.hh
common/gui_window.hh
common/history.hh
common/lambda.hh
common/memento.hh
common/node.hh
common/node_link_store.hh
common/logger.hh
common/logger_pool.hh
common/ptr_selector.hh
common/queue.hh
common/value.hh
common/yas.hh
file/node_network.cc
file/system_dir.cc
file/system_imgui_config.cc
file/system_logger.cc

View File

@ -0,0 +1,76 @@
#pragma once
#include <memory>
#include <span>
#include <vector>
#include "common/history.hh"
namespace nf7 {
template <typename T = History::Command>
class AggregateCommand : public T {
public:
using CommandList = std::vector<std::unique_ptr<T>>;
AggregateCommand(CommandList&& commands) noexcept :
commands_(std::move(commands)) {
}
void Apply() override {
auto itr = commands_.begin();
try {
try {
while (itr < commands_.end()) {
(*itr)->Apply();
++itr;
}
} catch (History::CorruptException&) {
throw History::CorruptException("failed to apply AggregateCommand");
}
} catch (History::CorruptException&) {
try {
while (itr > commands_.begin()) {
--itr;
(*itr)->Revert();
}
} catch (History::CorruptException&) {
throw History::CorruptException(
"AggregateCommand gave up recovering from failure of apply");
}
throw;
}
}
void Revert() override {
auto itr = commands_.rbegin();
try {
try {
while (itr < commands_.rend()) {
(*itr)->Revert();
++itr;
}
} catch (History::CorruptException&) {
throw History::CorruptException("failed to revert AggregateCommand");
}
} catch (History::CorruptException&) {
try {
while (itr > commands_.rbegin()) {
--itr;
(*itr)->Apply();
}
} catch (History::CorruptException&) {
throw History::CorruptException(
"AggregateCommand gave up recovering from failure of revert");
}
throw;
}
}
std::span<const std::unique_ptr<T>> commands() const noexcept { return commands_; }
private:
CommandList commands_;
};
} // namespace nf7

59
common/generic_history.hh Normal file
View File

@ -0,0 +1,59 @@
#pragma once
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
#include "common/history.hh"
namespace nf7 {
template <typename T>
class GenericHistory : public History {
public:
GenericHistory() = delete;
GenericHistory(Env& env) noexcept : env_(&env) {
}
GenericHistory(const GenericHistory&) = delete;
GenericHistory(GenericHistory&&) = default;
GenericHistory& operator=(const GenericHistory&) = delete;
GenericHistory& operator=(GenericHistory&&) = default;
void Add(std::unique_ptr<T>&& cmd) noexcept {
cmds_.erase(cmds_.begin()+static_cast<intmax_t>(cursor_), cmds_.end());
cmds_.push_back(std::move(cmd));
cursor_++;
}
void Clear() noexcept {
cmds_.clear();
}
void UnDo() {
if (cursor_ <= 0) return;
cmds_[cursor_-1]->Revert();
--cursor_;
}
void ReDo() {
if (cursor_ >= cmds_.size()) return;
cmds_[cursor_]->Apply();
++cursor_;
}
T* prev() const noexcept {
return cursor_ > 0? cmds_[cursor_-1].get(): nullptr;
}
T* next() const noexcept {
return cursor_ < cmds_.size()? cmds_[cursor_].get(): nullptr;
}
private:
Env* const env_;
std::vector<std::unique_ptr<T>> cmds_;
size_t cursor_ = 0;
};
} // namespace nf7

90
common/generic_memento.hh Normal file
View File

@ -0,0 +1,90 @@
#pragma once
#include <cassert>
#include <memory>
#include <unordered_map>
#include <utility>
#include "common/memento.hh"
namespace nf7 {
template <typename T>
class GenericMemento : public Memento {
public:
class CustomTag;
GenericMemento(File& owner, T&& data) noexcept :
owner_(&owner), data_(std::move(data)) {
}
~GenericMemento() noexcept {
tag_ = nullptr;
assert(map_.empty());
}
std::shared_ptr<Tag> Save() noexcept override {
if (tag_) return tag_;
auto [itr, emplaced] = map_.emplace(next_++, data_);
assert(emplaced);
return tag_ = std::make_shared<CustomTag>(*this, itr->first);
}
void Restore(const std::shared_ptr<Tag>& tag) override {
assert(tag);
auto itr = map_.find(tag->id());
assert(itr != map_.end());
data_ = itr->second;
tag_ = tag;
assert(tag_);
if (owner_->id()) {
owner_->env().Handle(
{.id = owner_->id(), .type = File::Event::kUpdate});
}
}
void Commit() noexcept {
tag_ = nullptr;
NotifyUpdate();
}
void CommitAmend() noexcept {
if (!tag_) return;
auto itr = map_.find(tag_->id());
assert(itr != map_.end());
itr->second = data_;
NotifyUpdate();
}
T& data() noexcept { return data_; }
const T& data() const noexcept { return data_; }
private:
File* const owner_;
T data_;
Tag::Id next_ = 0;
std::unordered_map<Tag::Id, T> map_;
std::shared_ptr<nf7::Memento::Tag> tag_;
void NotifyUpdate() noexcept {
if (owner_->id()) {
owner_->env().Handle(
{.id = owner_->id(), .type = File::Event::kUpdate});
}
}
};
template <typename T>
class GenericMemento<T>::CustomTag final : public Tag {
public:
CustomTag(GenericMemento& owner, Id id) noexcept : Tag(id), owner_(&owner) {
}
~CustomTag() noexcept {
owner_->map_.erase(id());
}
private:
GenericMemento* owner_;
};
} // namespace nf7

29
common/gui_node.hh Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <algorithm>
#include <imgui.h>
#include <imgui_internal.h>
namespace nf7::gui {
static inline void NodeSocket() noexcept {
auto win = ImGui::GetCurrentWindow();
const auto em = ImGui::GetFontSize();
const auto lh = std::max(win->DC.CurrLineSize.y, em);
const auto rad = em/2 / ImNodes::CanvasState().Zoom;
const auto sz = ImVec2(rad*2, lh);
const auto pos = ImGui::GetCursorScreenPos() + sz/2;
auto dlist = ImGui::GetWindowDrawList();
dlist->AddCircleFilled(
pos, rad, IM_COL32(100, 100, 100, 100));
dlist->AddCircleFilled(
pos, rad*.8f, IM_COL32(200, 200, 200, 200));
ImGui::Dummy(sz);
}
} // namespacce nf7::gui

41
common/history.hh Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include "nf7.hh"
namespace nf7 {
class History {
public:
class Command;
class CorruptException;
History() = default;
virtual ~History() = default;
History(const History&) = delete;
History(History&&) = delete;
History& operator=(const History&) = delete;
History& operator=(History&&) = delete;
virtual void UnDo() = 0;
virtual void ReDo() = 0;
};
class History::Command {
public:
Command() = default;
virtual ~Command() = default;
Command(const Command&) = delete;
Command(Command&&) = delete;
Command& operator=(const Command&) = delete;
Command& operator=(Command&&) = delete;
virtual void Apply() = 0;
virtual void Revert() = 0;
};
class History::CorruptException : public Exception {
public:
using Exception::Exception;
};
} // namespace nf7

31
common/lambda.hh Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <cassert>
#include <memory>
#include "nf7.hh"
#include "common/value.hh"
namespace nf7 {
class Lambda : public nf7::Context {
public:
using nf7::Context::Context;
virtual void Initialize(const std::shared_ptr<Lambda>& self) {
assert(self.get() == this);
self_ = self;
}
virtual void Handle(
size_t idx, Value&&, const std::shared_ptr<Lambda>& sender) = 0;
std::shared_ptr<Lambda> self() const noexcept { return self_.lock(); }
private:
std::weak_ptr<Lambda> self_;
};
} // namespace nf7

51
common/memento.hh Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <algorithm>
#include <memory>
#include <vector>
#include "nf7.hh"
namespace nf7 {
class Memento : public File::Interface {
public:
class Tag;
class CorruptException;
Memento() = default;
Memento(const Memento&) = delete;
Memento(Memento&&) = delete;
Memento& operator=(const Memento&) = delete;
Memento& operator=(Memento&&) = delete;
virtual std::shared_ptr<Tag> Save() noexcept = 0;
virtual void Restore(const std::shared_ptr<Tag>&) = 0;
};
class Memento::Tag {
public:
using Id = uint64_t;
Tag() = delete;
Tag(Id id) noexcept : id_(id) {
}
virtual ~Tag() = default;
Tag(const Tag&) = default;
Tag(Tag&&) = default;
Tag& operator=(const Tag&) = delete;
Tag& operator=(Tag&&) = delete;
Id id() const noexcept { return id_; }
private:
Id id_;
};
class Memento::CorruptException : public Exception {
public:
using Exception::Exception;
};
} // namespace nf7

80
common/node.hh Normal file
View File

@ -0,0 +1,80 @@
#pragma once
#include <algorithm>
#include <cstdint>
#include <memory>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "nf7.hh"
#include "common/lambda.hh"
namespace nf7 {
class Node : public File::Interface {
public:
class Editor;
using Id = uint64_t;
Node() = default;
Node(const Node&) = default;
Node(Node&&) = default;
Node& operator=(const Node&) = default;
Node& operator=(Node&&) = default;
virtual std::shared_ptr<nf7::Lambda> CreateLambda() noexcept { return nullptr; }
virtual void UpdateNode(Editor&) noexcept = 0;
std::span<const std::string> input() const noexcept { return input_; }
std::span<const std::string> output() const noexcept { return output_; }
const std::string& input(size_t i) const noexcept { return input_[i]; }
const std::string& output(size_t i) const noexcept { return output_[i]; }
size_t input(std::string_view name) const {
auto itr = std::find(input_.begin(), input_.end(), name);
if (itr >= input_.end()) {
throw Exception("missing input socket: "+std::string(name));
}
return static_cast<size_t>(itr - input_.begin());
}
size_t output(std::string_view name) const {
auto itr = std::find(output_.begin(), output_.end(), name);
if (itr >= output_.end()) {
throw Exception("missing output socket: "+std::string(name));
}
return static_cast<size_t>(itr - output_.begin());
}
protected:
std::vector<std::string> input_;
std::vector<std::string> output_;
};
class Node::Editor {
public:
Editor() = default;
virtual ~Editor() = default;
Editor(const Editor&) = delete;
Editor(Editor&&) = delete;
Editor& operator=(const Editor&) = delete;
Editor& operator=(Editor&&) = delete;
virtual void AddLink(Node& src_node, std::string_view src_name,
Node& dst_node, std::string_view dst_name) noexcept = 0;
virtual void RemoveLink(Node& src_node, std::string_view src_name,
Node& dst_node, std::string_view dst_name) noexcept = 0;
virtual std::vector<std::pair<Node*, std::string>> GetSrcOf(Node&, std::string_view) const noexcept = 0;
virtual std::vector<std::pair<Node*, std::string>> GetDstOf(Node&, std::string_view) const noexcept = 0;
virtual std::shared_ptr<nf7::Lambda> lambda() noexcept = 0;
};
} // namespace nf7

120
common/node_link_store.hh Normal file
View File

@ -0,0 +1,120 @@
#pragma once
#include <algorithm>
#include <cstdint>
#include <memory>
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include <yas/serialize.hpp>
#include <yas/types/std/string.hpp>
#include <yas/types/utility/usertype.hpp>
#include "common/aggregate_command.hh"
#include "common/history.hh"
namespace nf7 {
class NodeLinkStore {
public:
class SwapCommand;
struct Link {
public:
uint64_t src_id;
std::string src_name;
uint64_t dst_id;
std::string dst_name;
bool operator==(const Link& other) const noexcept {
if (src_id && other.src_id && src_id != other.src_id) return false;
if (dst_id && other.dst_id && dst_id != other.dst_id) return false;
if (src_name.size() && other.src_name.size() && src_name != other.src_name) return false;
if (dst_name.size() && other.dst_name.size() && dst_name != other.dst_name) return false;
return true;
}
template <typename Ar>
Ar& serialize(Ar& ar) {
ar(src_id, src_name, dst_id, dst_name);
return ar;
}
};
NodeLinkStore() = default;
NodeLinkStore(const NodeLinkStore&) = default;
NodeLinkStore(NodeLinkStore&&) = default;
NodeLinkStore& operator=(const NodeLinkStore&) = default;
NodeLinkStore& operator=(NodeLinkStore&&) = default;
template <typename Ar>
Ar& serialize(Ar& ar) {
ar(links_);
return ar;
}
void AddLink(Link&& lk) noexcept {
links_.push_back(std::move(lk));
}
void RemoveLink(const Link& lk) noexcept {
links_.erase(std::remove(links_.begin(), links_.end(), lk), links_.end());
}
inline std::unique_ptr<History::Command> CreateCommandToRemoveExpired(
uint64_t id, std::span<const std::string> in, std::span<const std::string> out) noexcept;
std::span<const Link> items() const noexcept { return links_; }
private:
std::vector<Link> links_;
};
class NodeLinkStore::SwapCommand : public History::Command {
public:
static std::unique_ptr<SwapCommand> CreateToAdd(
NodeLinkStore& target, Link&& lk) noexcept {
return std::make_unique<SwapCommand>(target, std::move(lk), false);
}
static std::unique_ptr<SwapCommand> CreateToRemove(
NodeLinkStore& target, Link&& lk) noexcept {
return std::make_unique<SwapCommand>(target, std::move(lk), true);
}
SwapCommand(NodeLinkStore& target, Link&& lk, bool added) noexcept :
target_(&target), link_(std::move(lk)), added_(added) {
}
void Apply() noexcept override { Exec(); }
void Revert() noexcept override { Exec(); }
private:
NodeLinkStore* const target_;
Link link_;
bool added_;
void Exec() noexcept {
added_?
target_->RemoveLink(link_):
target_->AddLink(Link(link_));
added_ = !added_;
}
};
std::unique_ptr<History::Command> NodeLinkStore::CreateCommandToRemoveExpired(
uint64_t id, std::span<const std::string> in, std::span<const std::string> out) noexcept {
std::vector<std::unique_ptr<History::Command>> cmds;
for (const auto& lk : links_) {
const bool rm =
(lk.src_id == id && std::find(in .begin(), in .end(), lk.src_name) < in .end()) ||
(lk.dst_id == id && std::find(out.begin(), out.end(), lk.dst_name) < out.end());
if (rm) cmds.push_back(SwapCommand::CreateToRemove(*this, Link(lk)));
}
if (cmds.empty()) return nullptr;
return std::make_unique<AggregateCommand<History::Command>>(std::move(cmds));
}
} // namespace nf7

126
common/value.hh Normal file
View File

@ -0,0 +1,126 @@
#pragma once
#include <cstdint>
#include <memory>
#include <string>
#include <string_view>
#include <variant>
#include <yas/serialize.hpp>
#include <yas/types/std/string.hpp>
#include <yas/types/std/string_view.hpp>
#include <yas/types/utility/usertype.hpp>
#include "nf7.hh"
#include "common/yas.hh"
namespace nf7 {
class Value final {
public:
using IncompatibleException = std::bad_variant_access;
class Pulse final { };
class Data;
using Boolean = bool;
using Integer = int64_t;
using Scalar = double;
using String = std::string;
using DataPtr = std::shared_ptr<Data>;
Value() noexcept {
}
Value(const Value&) = default;
Value(Value&&) = default;
Value& operator=(const Value&) = default;
Value& operator=(Value&&) = default;
bool isPulse() const noexcept { return std::holds_alternative<Pulse>(value_); }
bool isBoolean() const noexcept { return std::holds_alternative<Boolean>(value_); }
bool isInteger() const noexcept { return std::holds_alternative<Integer>(value_); }
bool isScalar() const noexcept { return std::holds_alternative<Scalar>(value_); }
bool isString() const noexcept { return std::holds_alternative<String>(value_); }
bool isData() const noexcept { return std::holds_alternative<DataPtr>(value_); }
Integer integer() const { return std::get<Integer>(value_); }
Boolean boolean() const { return std::get<Boolean>(value_); }
Scalar scalar() const { return std::get<Scalar>(value_); }
const String& string() const { return std::get<String>(value_); }
const DataPtr& data() const { return std::get<DataPtr>(value_); }
const char* typeName() const noexcept {
struct Visitor final {
public:
auto operator()(Pulse) noexcept { return "pulse"; }
auto operator()(Boolean) noexcept { return "boolean"; }
auto operator()(Integer) noexcept { return "integer"; }
auto operator()(Scalar) noexcept { return "scalar"; }
auto operator()(String) noexcept { return "string"; }
auto operator()(DataPtr) noexcept { return "data"; }
};
return std::visit(Visitor{}, value_);
}
template <typename Ar>
Ar& serialize(Ar& ar) noexcept {
ar & value_;
return ar;
}
private:
std::variant<Pulse, Boolean, Integer, Scalar, String, DataPtr> value_;
};
class Value::Data {
public:
Data() = default;
virtual ~Data() = default;
Data(const Data&) = default;
Data(Data&&) = default;
Data& operator=(const Data&) = default;
Data& operator=(Data&&) = default;
};
} // namespace nf7
namespace yas::detail {
template <size_t F>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
nf7::Value::Pulse> {
public:
template <typename Archive>
static Archive& save(Archive& ar, const nf7::Value::Pulse&) {
return ar;
}
template <typename Archive>
static Archive& load(Archive& ar, nf7::Value::Pulse&) {
return ar;
}
};
template <size_t F>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
nf7::Value::DataPtr> {
public:
template <typename Archive>
static Archive& save(Archive& ar, const nf7::Value::DataPtr&) {
throw nf7::Exception("cannot serialize Value::DataPtr");
}
template <typename Archive>
static Archive& load(Archive& ar, nf7::Value::DataPtr&) {
throw nf7::DeserializeException("cannot deserialize Value::DataPtr");
}
};
} // namespace yas::detail

28
common/yas_imgui.hh Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <imgui.h>
#include <yas/serialize.hpp>
namespace yas::detail {
template <size_t F>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
ImVec2> {
public:
template <typename Archive>
static Archive& save(Archive& ar, const ImVec2& v) {
ar(v.x, v.y);
return ar;
}
template <typename Archive>
static Archive& load(Archive& ar, ImVec2& v) {
ar(v.x, v.y);
return ar;
}
};
} // namespace yas::detail

31
common/yas_imnodes.hh Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <ImNodes.h>
#include <yas/serialize.hpp>
#include "common/yas_imgui.hh"
namespace yas::detail {
template <size_t F>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
ImNodes::CanvasState> {
public:
template <typename Archive>
static Archive& save(Archive& ar, const ImNodes::CanvasState& canvas) {
ar(canvas.Zoom, canvas.Offset);
return ar;
}
template <typename Archive>
static Archive& load(Archive& ar, ImNodes::CanvasState& canvas) {
ar(canvas.Zoom, canvas.Offset);
return ar;
}
};
} // namespace yas::detail

1351
file/node_network.cc Normal file

File diff suppressed because it is too large Load Diff