Commit 6ecc126d authored by Stefan Pfeifer's avatar Stefan Pfeifer

Replace profile curve offsets with handle dimensions

Closes #104
parent 395ba060
......@@ -167,8 +167,8 @@ add_executable(
source/gui/input/dialogs/LayerDialog.hpp
source/gui/input/dialogs/MassesDialog.cpp
source/gui/input/dialogs/MassesDialog.hpp
source/gui/input/dialogs/OperationDialog.cpp
source/gui/input/dialogs/OperationDialog.hpp
source/gui/input/dialogs/DimensionsDialog.cpp
source/gui/input/dialogs/DimensionsDialog.hpp
source/gui/input/dialogs/ProfileDialog.cpp
source/gui/input/dialogs/ProfileDialog.hpp
source/gui/input/dialogs/SettingsDialog.cpp
......
......@@ -28,6 +28,6 @@
<file>icons/model-tree/layers.png</file>
<file>icons/model-tree/string.png</file>
<file>icons/model-tree/masses.png</file>
<file>icons/model-tree/operation.png</file>
<file>icons/model-tree/dimensions.png</file>
</qresource>
</RCC>
......@@ -68,7 +68,7 @@ void BowModel::check_input(const InputData& input)
// Check Profile
for(double arg: input.profile.segments.args())
for(double arg: input.profile.args())
{
if(arg <= 0.0)
throw std::runtime_error("Profile: Segment lengths must be positive");
......@@ -131,10 +131,10 @@ void BowModel::check_input(const InputData& input)
// Check Operation
if(input.operation.brace_height >= input.operation.draw_length)
if(input.dimensions.brace_height >= input.dimensions.draw_length)
throw std::runtime_error("Operation: Draw length must be greater than brace height");
if(input.operation.arrow_mass <= 0.0)
if(input.masses.arrow <= 0.0)
throw std::runtime_error("Operation: Arrow mass must be positive");
}
......@@ -185,7 +185,7 @@ void BowModel::init_string(const Callback& callback)
// Calculate curve tangential to the limb na d calculate string node positions by equipartition
std::vector<Vector<2>> points;
points.push_back({0.0, -input.operation.brace_height});
points.push_back({0.0, -input.dimensions.brace_height});
for(size_t i = 0; i < nodes_limb.size(); ++i)
{
points.push_back({
......@@ -237,7 +237,7 @@ void BowModel::init_string(const Callback& callback)
for(auto& element: system.mut_elements().group<BarElement>("string"))
element.set_length(l);
info = solver.solve(-input.operation.brace_height);
info = solver.solve(-input.dimensions.brace_height);
return system.get_angle(nodes_string[0], nodes_string[1]);
};
......@@ -291,7 +291,7 @@ void BowModel::init_masses(const Callback& callback)
MassElement mass_limb_tip(system, nodes_limb.back(), input.masses.limb_tip);
MassElement mass_string_tip(system, nodes_string.back(), input.masses.string_tip);
MassElement mass_string_center(system, nodes_string.front(), 0.5*input.masses.string_center); // 0.5 because of symmetry
MassElement arrow_mass(system, node_arrow, 0.5*input.operation.arrow_mass); // 0.5 because of symmetry
MassElement arrow_mass(system, node_arrow, 0.5*input.masses.arrow); // 0.5 because of symmetry
system.mut_elements().add(mass_limb_tip, "limb tip");
system.mut_elements().add(mass_string_tip, "string tip");
......@@ -306,7 +306,7 @@ void BowModel::simulate_statics(const Callback& callback)
for(unsigned i = 0; i < input.settings.n_draw_steps; ++i)
{
double eta = double(i)/(input.settings.n_draw_steps - 1);
double draw_length = (1.0 - eta)*input.operation.brace_height + eta*input.operation.draw_length;
double draw_length = (1.0 - eta)*input.dimensions.brace_height + eta*input.dimensions.draw_length;
solver.solve(-draw_length);
add_state(output.statics.states);
......@@ -343,13 +343,15 @@ void BowModel::simulate_dynamics(const Callback& callback)
DynamicSolver solver1(system, dt, input.settings.sampling_rate, [&]{
double ut = system.get_u(node_arrow.y);
if(ut < input.operation.brace_height)
if(ut < input.dimensions.brace_height)
{
double u0 = -input.operation.draw_length;
double u1 = -input.operation.brace_height;
double u0 = -input.dimensions.draw_length;
double u1 = -input.dimensions.brace_height;
if(ut != u0)
{
T = system.get_t()*std::acos(u1/u0)/std::acos(ut/u0);
}
}
return system.get_a(node_arrow.y) <= 0;
......
......@@ -21,10 +21,10 @@ LimbProperties::LimbProperties(const InputData& input, unsigned n)
rhoA(VectorXd::Zero(n))
{
// 1. Nodes
Curve2D curve = ArcCurve::sample(input.profile.segments,
input.profile.x_pos,
input.profile.y_pos,
input.profile.angle,
Curve2D curve = ArcCurve::sample(input.profile,
input.dimensions.handle_length/2.0,
input.dimensions.handle_setback,
input.dimensions.handle_angle,
n-1);
// Todo: Is there a more elegant way? Maybe have a Curve2D member? C++17 structured bindings?
......
......@@ -3,35 +3,43 @@
using nlohmann::json;
struct Operation
struct Dimensions
{
double brace_height = 0.2;
double draw_length = 0.7;
double arrow_mass = 0.025;
double handle_length = 0.0;
double handle_setback = 0.0;
double handle_angle = 0.0;
};
static bool operator==(const Operation& lhs, const Operation& rhs)
static bool operator==(const Dimensions& lhs, const Dimensions& rhs)
{
return lhs.brace_height == rhs.brace_height
&& lhs.draw_length == rhs.draw_length
&& lhs.arrow_mass == rhs.arrow_mass;
&& lhs.handle_length == rhs.handle_length
&& lhs.handle_setback == rhs.handle_setback
&& lhs.handle_angle == rhs.handle_angle;
}
static bool operator!=(const Operation& lhs, const Operation& rhs)
static bool operator!=(const Dimensions& lhs, const Dimensions& rhs)
{
return !operator==(lhs, rhs);
}
static void to_json(json& obj, const Operation& value)
static void to_json(json& obj, const Dimensions& value)
{
obj["brace_height"] = value.brace_height;
obj["draw_length"] = value.draw_length;
obj["arrow_mass"] = value.arrow_mass;
obj["handle_length"] = value.handle_length;
obj["handle_setback"] = value.handle_setback;
obj["handle_angle"] = value.handle_angle;
}
static void from_json(const json& obj, Operation& value)
static void from_json(const json& obj, Dimensions& value)
{
value.brace_height = obj.at("brace_height");
value.draw_length = obj.at("draw_length");
value.arrow_mass = obj.at("arrow_mass");
value.handle_length = obj.at("handle_length");
value.handle_setback = obj.at("handle_setback");
value.handle_angle = obj.at("handle_angle");
}
#pragma once
#include "Meta.hpp"
#include "Settings.hpp"
#include "Profile.hpp"
#include "Layers.hpp"
#include "String.hpp"
#include "Masses.hpp"
#include "Operation.hpp"
#include "Dimensions.hpp"
#include <json.hpp>
#include <vector>
......@@ -15,12 +14,12 @@ struct InputData
{
Meta meta;
Settings settings;
Profile profile;
Series profile = {{0.8}, {0.0}};
Series width = {{0.0, 1.0}, {0.06, 0.01}};
Layers layers = {Layer()};
String string;
Masses masses;
Operation operation;
Dimensions dimensions;
// Todo: Move those somewhere else, maybe in Application class. Interface of InputData can be just json.
void load(const std::string& path);
......@@ -37,7 +36,7 @@ static bool operator==(const InputData& lhs, const InputData& rhs)
&& lhs.layers == rhs.layers
&& lhs.string == rhs.string
&& lhs.masses == rhs.masses
&& lhs.operation == rhs.operation;
&& lhs.dimensions == rhs.dimensions;
}
static bool operator!=(const InputData& lhs, const InputData& rhs)
......@@ -54,7 +53,7 @@ static void to_json(json& obj, const InputData& value)
obj["layers"] = value.layers;
obj["string"] = value.string;
obj["masses"] = value.masses;
obj["operation"] = value.operation;
obj["dimensions"] = value.dimensions;
}
static void from_json(const json& obj, InputData& value)
......@@ -66,5 +65,5 @@ static void from_json(const json& obj, InputData& value)
value.layers = obj.at("layers").get<Layers>();
value.string = obj.at("string");
value.masses = obj.at("masses");
value.operation = obj.at("operation");
value.dimensions = obj.at("dimensions");
}
......@@ -5,6 +5,7 @@ using nlohmann::json;
struct Masses
{
double arrow = 0.025;
double string_center = 0.005;
double string_tip = 0.005;
double limb_tip = 0.005;
......@@ -12,7 +13,8 @@ struct Masses
static bool operator==(const Masses& lhs, const Masses& rhs)
{
return lhs.string_center == rhs.string_center
return lhs.arrow == rhs.arrow
&& lhs.string_center == rhs.string_center
&& lhs.string_tip == rhs.string_tip
&& lhs.limb_tip == rhs.limb_tip;
}
......@@ -24,6 +26,7 @@ static bool operator!=(const Masses& lhs, const Masses& rhs)
static void to_json(json& obj, const Masses& value)
{
obj["arrow"] = value.arrow;
obj["string_center"] = value.string_center;
obj["string_tip"] = value.string_tip;
obj["limb_tip"] = value.limb_tip;
......@@ -31,6 +34,7 @@ static void to_json(json& obj, const Masses& value)
static void from_json(const json& obj, Masses& value)
{
value.arrow = obj.at("arrow");
value.string_center = obj.at("string_center");
value.string_tip = obj.at("string_tip");
value.limb_tip = obj.at("limb_tip");
......
#pragma once
#include "numerics/Series.hpp"
#include <json.hpp>
using nlohmann::json;
struct Profile
{
Series segments = {{0.8}, {0.0}};
double x_pos = 0.0;
double y_pos = 0.0;
double angle = 0.0;
};
static bool operator==(const Profile& lhs, const Profile& rhs)
{
return lhs.segments == rhs.segments
&& lhs.x_pos == rhs.x_pos
&& lhs.y_pos == rhs.y_pos
&& lhs.angle == rhs.angle;
}
static bool operator!=(const Profile& lhs, const Profile& rhs)
{
return !operator==(lhs, rhs);
}
static void to_json(json& obj, const Profile& value)
{
obj["segments"] = value.segments;
obj["x_pos"] = value.x_pos;
obj["y_pos"] = value.y_pos;
obj["angle"] = value.angle;
}
static void from_json(const json& obj, Profile& value)
{
value.segments = obj.at("segments");
value.x_pos = obj.at("x_pos");
value.y_pos = obj.at("y_pos");
value.angle = obj.at("angle");
}
#include "DimensionsDialog.hpp"
DimensionsDialog::DimensionsDialog(QWidget* parent)
: GroupDialog(parent, "Dimensions", false)
{
this->addGroup("Draw");
this->addWidget(edit0);
this->addWidget(edit1);
this->addGroup("Handle");
this->addWidget(edit2);
this->addWidget(edit3);
this->addWidget(edit4);
QObject::connect(edit0, &DoubleEditor::modified, this, &DimensionsDialog::modified);
QObject::connect(edit1, &DoubleEditor::modified, this, &DimensionsDialog::modified);
QObject::connect(edit2, &DoubleEditor::modified, this, &DimensionsDialog::modified);
QObject::connect(edit3, &DoubleEditor::modified, this, &DimensionsDialog::modified);
QObject::connect(edit4, &DoubleEditor::modified, this, &DimensionsDialog::modified);
}
Dimensions DimensionsDialog::getData() const
{
Dimensions data;
data.brace_height = edit0->getData();
data.draw_length = edit1->getData();
data.handle_length = edit2->getData();
data.handle_setback = edit3->getData();
data.handle_angle = edit4->getData();
return data;
}
void DimensionsDialog::setData(const Dimensions& data)
{
edit0->setData(data.brace_height);
edit1->setData(data.draw_length);
edit2->setData(data.handle_length);
edit3->setData(data.handle_setback);
edit4->setData(data.handle_angle);
}
#pragma once
#include "bow/input/Operation.hpp"
#include "bow/input/Dimensions.hpp"
#include "gui/input/editors/DoubleEditor.hpp"
#include "GroupDialog.hpp"
class OperationDialog: public GroupDialog
class DimensionsDialog: public GroupDialog
{
Q_OBJECT
public:
OperationDialog(QWidget* parent);
DimensionsDialog(QWidget* parent);
Operation getData() const;
void setData(const Operation& data);
Dimensions getData() const;
void setData(const Dimensions& data);
signals:
void modified();
......@@ -19,5 +19,7 @@ signals:
private:
DoubleEditor* edit0 = new DoubleEditor("Brace height [m]");
DoubleEditor* edit1 = new DoubleEditor("Draw length [m]");
DoubleEditor* edit2 = new DoubleEditor("Arrow mass [kg]");
DoubleEditor* edit2 = new DoubleEditor("Length [m]");
DoubleEditor* edit3 = new DoubleEditor("Setback [m]");
DoubleEditor* edit4 = new DoubleEditor("Angle [m]");
};
......@@ -6,25 +6,29 @@ MassesDialog::MassesDialog(QWidget* parent)
this->addWidget(edit0);
this->addWidget(edit1);
this->addWidget(edit2);
this->addWidget(edit3);
QObject::connect(edit0, &DoubleEditor::modified, this, &MassesDialog::modified);
QObject::connect(edit1, &DoubleEditor::modified, this, &MassesDialog::modified);
QObject::connect(edit2, &DoubleEditor::modified, this, &MassesDialog::modified);
QObject::connect(edit3, &DoubleEditor::modified, this, &MassesDialog::modified);
}
Masses MassesDialog::getData() const
{
Masses data;
data.string_center = edit0->getData();
data.string_tip = edit1->getData();
data.limb_tip = edit2->getData();
data.arrow = edit0->getData();
data.string_center = edit1->getData();
data.string_tip = edit2->getData();
data.limb_tip = edit3->getData();
return data;
}
void MassesDialog::setData(const Masses& data)
{
edit0->setData(data.string_center);
edit1->setData(data.string_tip);
edit2->setData(data.limb_tip);
edit0->setData(data.arrow);
edit1->setData(data.string_center);
edit2->setData(data.string_tip);
edit3->setData(data.limb_tip);
}
......@@ -17,7 +17,8 @@ signals:
void modified();
private:
DoubleEditor* edit0 = new DoubleEditor("String center [kg]");
DoubleEditor* edit1 = new DoubleEditor("String tip [kg]");
DoubleEditor* edit2 = new DoubleEditor("Limb tip [kg]");
DoubleEditor* edit0 = new DoubleEditor("Arrow [kg]");
DoubleEditor* edit1 = new DoubleEditor("String center [kg]");
DoubleEditor* edit2 = new DoubleEditor("String tip [kg]");
DoubleEditor* edit3 = new DoubleEditor("Limb tip [kg]");
};
#include "OperationDialog.hpp"
OperationDialog::OperationDialog(QWidget* parent)
: GroupDialog(parent, "Operation", false)
{
this->addWidget(edit0);
this->addWidget(edit1);
this->addWidget(edit2);
QObject::connect(edit0, &DoubleEditor::modified, this, &OperationDialog::modified);
QObject::connect(edit1, &DoubleEditor::modified, this, &OperationDialog::modified);
QObject::connect(edit2, &DoubleEditor::modified, this, &OperationDialog::modified);
}
Operation OperationDialog::getData() const
{
Operation data;
data.brace_height = edit0->getData();
data.draw_length = edit1->getData();
data.arrow_mass = edit2->getData();
return data;
}
void OperationDialog::setData(const Operation& data)
{
edit0->setData(data.brace_height);
edit1->setData(data.draw_length);
edit2->setData(data.arrow_mass);
}
......@@ -2,71 +2,40 @@
ProfileDialog::ProfileDialog(QWidget* parent)
: PersistentDialog(parent, "ProfileDialog", {800, 400}), // Magic numbers
table(new SeriesEditor("Length [m]", "Curvature [1/m]", 25)),
view(new ProfileView()),
edit_x_pos(new DoubleEditor("X [m]")),
edit_y_pos(new DoubleEditor("Y [m]")),
edit_angle(new DoubleEditor("Angle [rad]"))
edit(new SeriesEditor("Length [m]", "Curvature [1/m]", 25)),
view(new ProfileView())
{
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
QObject::connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
QObject::connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
auto hbox1 = new QHBoxLayout();
hbox1->setContentsMargins(10, 5, 10, 5);
hbox1->addWidget(new QLabel("Offsets:"));
hbox1->addSpacing(10);
hbox1->addWidget(edit_x_pos);
hbox1->addWidget(edit_y_pos);
hbox1->addWidget(edit_angle);
hbox1->addStretch(1);
auto hbox = new QHBoxLayout();
hbox->addWidget(edit, 0);
hbox->addWidget(view, 1);
auto group = new QGroupBox();
group->setLayout(hbox1);
auto vbox1 = new QVBoxLayout();
vbox1->addWidget(group, 0);
vbox1->addWidget(view, 1);
auto hbox2 = new QHBoxLayout();
hbox2->addWidget(table, 0);
hbox2->addLayout(vbox1, 1);
auto vbox2 = new QVBoxLayout();
vbox2->addLayout(hbox2, 1);
vbox2->addWidget(buttons, 0);
auto vbox = new QVBoxLayout();
vbox->addLayout(hbox, 1);
vbox->addWidget(buttons, 0);
this->setWindowTitle("Profile");
this->setLayout(vbox2);
this->setLayout(vbox);
// Event handling
QObject::connect(table, &SeriesEditor::modified, this, &ProfileDialog::modified);
QObject::connect(edit_x_pos, &DoubleEditor::modified, this, &ProfileDialog::modified);
QObject::connect(edit_y_pos, &DoubleEditor::modified, this, &ProfileDialog::modified);
QObject::connect(edit_angle, &DoubleEditor::modified, this, &ProfileDialog::modified);
QObject::connect(edit, &SeriesEditor::modified, this, &ProfileDialog::modified);
QObject::connect(this, &ProfileDialog::modified, [&]{
view->setData(this->getData());
});
}
Profile ProfileDialog::getData() const
Series ProfileDialog::getData() const
{
Profile profile;
profile.segments = table->getData();
profile.x_pos = edit_x_pos->getData();
profile.y_pos = edit_y_pos->getData();
profile.angle = edit_angle->getData();
return profile;
return edit->getData();
}
void ProfileDialog::setData(const Profile& profile)
void ProfileDialog::setData(const Series& data)
{
view->setData(profile);
table->setData(profile.segments);
edit_x_pos->setData(profile.x_pos);
edit_y_pos->setData(profile.y_pos);
edit_angle->setData(profile.angle);
view->setData(data);
edit->setData(data);
}
#pragma once
#include "bow/input/Profile.hpp"
#include "gui/PersistentDialog.hpp"
#include "gui/input/views/ProfileView.hpp"
#include "gui/input/editors/LayerEditor.hpp"
......@@ -11,16 +10,13 @@ class ProfileDialog: public PersistentDialog
public:
ProfileDialog(QWidget* parent);
Profile getData() const;
void setData(const Profile& profile);
Series getData() const;
void setData(const Series& data);
signals:
void modified();
private:
SeriesEditor* table;
SeriesEditor* edit;
ProfileView* view;
DoubleEditor* edit_x_pos;
DoubleEditor* edit_y_pos;
DoubleEditor* edit_angle;
};
......@@ -7,18 +7,18 @@
#include "gui/input/dialogs/SettingsDialog.hpp"
#include "gui/input/dialogs/StringDialog.hpp"
#include "gui/input/dialogs/MassesDialog.hpp"
#include "gui/input/dialogs/OperationDialog.hpp"
#include "gui/input/dialogs/DimensionsDialog.hpp"
TreeEditor::TreeEditor()
{
new TreeItem<CommentDialog, std::string>(this, data.meta.comments, "Comments", QIcon(":/icons/model-tree/comments"));
new TreeItem<SettingsDialog, Settings>(this, data.settings, "Settings", QIcon(":/icons/model-tree/settings"));
new TreeItem<ProfileDialog, Profile>(this, data.profile, "Profile", QIcon(":/icons/model-tree/profile"));
new TreeItem<ProfileDialog, Series>(this, data.profile, "Profile", QIcon(":/icons/model-tree/profile"));
new TreeItem<WidthDialog, Series>(this, data.width, "Width", QIcon(":/icons/model-tree/width"));
new TreeItem<LayerDialog, Layers>(this, data.layers, "Layers", QIcon(":/icons/model-tree/layers"));
new TreeItem<StringDialog, String>(this, data.string, "String", QIcon(":/icons/model-tree/string"));
new TreeItem<MassesDialog, Masses>(this, data.masses, "Masses", QIcon(":/icons/model-tree/masses"));
new TreeItem<OperationDialog, Operation>(this, data.operation, "Operation", QIcon(":/icons/model-tree/operation"));
new TreeItem<DimensionsDialog, Dimensions>(this, data.dimensions, "Dimensions", QIcon(":/icons/model-tree/dimensions"));
QObject::connect(this, &QTreeWidget::itemActivated, [](QTreeWidgetItem* item, int column)
{
......
......@@ -18,12 +18,12 @@ ProfileView::ProfileView()
curve1->setScatterSkip(0); // Todo: Having to explicitly state this is retarded
}
void ProfileView::setData(Profile profile)
void ProfileView::setData(Series data)
{
try
{
Curve2D curve = ArcCurve::sample(profile.segments, profile.x_pos, profile.y_pos, profile.angle, 150); // Magic number
Curve2D nodes = ArcCurve::nodes(profile.segments, profile.x_pos, profile.y_pos, profile.angle);
Curve2D curve = ArcCurve::sample(data, 0.0, 0.0, 0.0, 150); // Magic number
Curve2D nodes = ArcCurve::nodes(data, 0.0, 0.0, 0.0);
curve0->setData(curve.x, curve.y);
curve1->setData(nodes.x, nodes.y);
......
#pragma once
#include "bow/input/Profile.hpp"
#include "gui/PlotWidget.hpp"
#include "numerics/ArcCurve.hpp"
......@@ -7,7 +6,7 @@ class ProfileView: public PlotWidget
{
public:
ProfileView();
void setData(Profile profile);
void setData(Series profile);
private:
QCPCurve* curve0;
......
/*
#include <iostream>
#include "numerics/CubicSpline.hpp"
int main()
{
std::vector<double> x = {0.0, 1.0, 2.0, 3.0};
std::vector<double> y = {1.0, 1.1, -1.0, -1.1};
Series data = CubicSpline(Series(x, y)).sample(6);
for(auto& val: data.vals())
std::cout << val << "\n";
return 0;
}
*/
#include "gui/Application.hpp"
int main(int argc, char* argv[])
......
......@@ -28,7 +28,9 @@ CubicSpline::CubicSpline(const Series& data)
double dy = ys[i+1] - ys[i];
if(dx == 0.0)
{
throw std::runtime_error("argument values must be unique");
}
dxs.push_back(dx);
dys.push_back(dy);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment