Skip to content

Commit cc313a3

Browse files
authored
Merge pull request #68 from PartStackerCommunity/save
Save and open PartStacker project files
2 parents 04c04ef + ab9a3f7 commit cc313a3

22 files changed

Lines changed: 655 additions & 106 deletions

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enable_testing()
2020

2121
find_package(Catch2 CONFIG REQUIRED)
2222
find_package(GLEW REQUIRED)
23+
find_package(jsoncons CONFIG REQUIRED)
2324
find_package(mdspan CONFIG REQUIRED)
2425
find_package(wxWidgets CONFIG REQUIRED COMPONENTS core base net gl)
2526

src/pstack/calc/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_library(pstack_calc STATIC
22
mesh.cpp
3+
part.cpp
34
rotations.cpp
45
sinterbox.cpp
56
stacker.cpp
@@ -20,6 +21,6 @@ set_target_properties(pstack_calc PROPERTIES
2021
PROJECT_LABEL "calc"
2122
)
2223
target_link_libraries(pstack_calc
23-
PUBLIC pstack_geo pstack_util
24+
PUBLIC pstack_files pstack_geo pstack_util
2425
)
2526
target_include_directories(pstack_calc PUBLIC "${PROJECT_SOURCE_DIR}/src")

src/pstack/calc/part.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include "pstack/calc/part.hpp"
2+
#include "pstack/files/stl.hpp"
3+
#include <charconv>
4+
#include <cmath>
5+
#include <filesystem>
6+
#include <optional>
7+
#include <string>
8+
9+
namespace pstack::calc {
10+
11+
namespace {
12+
13+
std::optional<int> get_base_quantity(std::string name) {
14+
char looking_for = '.';
15+
if (name.ends_with(')')) {
16+
name.pop_back();
17+
looking_for = '(';
18+
}
19+
std::size_t number_length = 0;
20+
while (name.size() > number_length and std::isdigit(name[name.size() - number_length - 1])) {
21+
++number_length;
22+
}
23+
if (number_length == 0 or not (name.size() > number_length and name[name.size() - number_length - 1] == looking_for)) {
24+
return std::nullopt;
25+
}
26+
std::string_view number{ name.data() + (name.size() - number_length), name.data() + name.size() };
27+
int out{-1};
28+
std::from_chars(number.data(), number.data() + number.size(), out);
29+
return out;
30+
}
31+
32+
} // namespace
33+
34+
part initialize_part(part_base base) {
35+
part result{ std::move(base) };
36+
37+
result.name = std::filesystem::path(result.mesh_file).stem().string();
38+
result.mesh = files::from_stl(result.mesh_file);
39+
result.mesh.set_baseline({ 0, 0, 0 });
40+
41+
result.base_quantity = get_base_quantity(result.name);
42+
43+
auto volume_and_centroid = result.mesh.volume_and_centroid();
44+
result.volume = volume_and_centroid.volume;
45+
result.centroid = volume_and_centroid.centroid;
46+
result.triangle_count = result.mesh.triangles().size();
47+
48+
return result;
49+
}
50+
51+
} // namespace pstack::calc

src/pstack/calc/part.hpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,30 @@
77

88
namespace pstack::calc {
99

10-
struct part {
10+
// `part_base` is the part data needed to save and load
11+
struct part_base {
1112
std::string mesh_file;
13+
int quantity = 1;
14+
bool mirrored = false;
15+
int min_hole = 1;
16+
int rotation_index = 1;
17+
bool rotate_min_box = false;
18+
};
19+
20+
// `part` is everything that can be derived from `part_base`
21+
struct part : part_base {
1222
std::string name;
1323
mesh mesh;
1424

1525
std::optional<int> base_quantity;
16-
int quantity;
1726

1827
double volume;
1928
geo::point3<float> centroid;
2029
int triangle_count;
21-
bool mirrored;
22-
int min_hole;
23-
int rotation_index;
24-
bool rotate_min_box;
2530
};
2631

32+
part initialize_part(part_base base);
33+
2734
} // namespace pstack::calc
2835

2936
#endif // PSTACK_CALC_PART_HPP

src/pstack/calc/sinterbox.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ void append_side(std::vector<geo::triangle>& triangles, const util::mdspan<const
5454
}
5555

5656
std::tuple<std::vector<float>, std::vector<float>, std::vector<float>> make_positions(const sinterbox_parameters& params) {
57-
const auto [min, max, clearance, thickness, width, desired_spacing] = params;
57+
const auto [clearance, thickness, width, desired_spacing] = params.settings;
58+
const auto [min, max] = params.bounding;
5859
const geo::vector3 size = max - min;
5960

6061
// Number of bars in the given direction
@@ -111,8 +112,8 @@ void append_sinterbox(std::vector<geo::triangle>& triangles, const sinterbox_par
111112
upper_xy[x, y] = { positions_x[x], positions_y[y], upper_bound.z };
112113
}
113114
}
114-
append_side(triangles, lower_xy, geo::unit_z<float>, geo::unit_x<float>, geo::unit_y<float>, params.thickness);
115-
append_side(triangles, upper_xy, -geo::unit_z<float>, geo::unit_x<float>, geo::unit_y<float>, params.thickness);
115+
append_side(triangles, lower_xy, geo::unit_z<float>, geo::unit_x<float>, geo::unit_y<float>, params.settings.thickness);
116+
append_side(triangles, upper_xy, -geo::unit_z<float>, geo::unit_x<float>, geo::unit_y<float>, params.settings.thickness);
116117
}
117118

118119
// ZX sides
@@ -126,8 +127,8 @@ void append_sinterbox(std::vector<geo::triangle>& triangles, const sinterbox_par
126127
upper_zx[z, x] = { positions_x[x], upper_bound.y, positions_z[z] };
127128
}
128129
}
129-
append_side(triangles, lower_zx, geo::unit_y<float>, geo::unit_z<float>, geo::unit_x<float>, params.thickness);
130-
append_side(triangles, upper_zx, -geo::unit_y<float>, geo::unit_z<float>, geo::unit_x<float>, params.thickness);
130+
append_side(triangles, lower_zx, geo::unit_y<float>, geo::unit_z<float>, geo::unit_x<float>, params.settings.thickness);
131+
append_side(triangles, upper_zx, -geo::unit_y<float>, geo::unit_z<float>, geo::unit_x<float>, params.settings.thickness);
131132
}
132133

133134
// YZ sides
@@ -140,8 +141,8 @@ void append_sinterbox(std::vector<geo::triangle>& triangles, const sinterbox_par
140141
upper_yz[y, z] = { upper_bound.x, positions_y[y], positions_z[z] };
141142
}
142143
}
143-
append_side(triangles, lower_yz, geo::unit_x<float>, geo::unit_y<float>, geo::unit_z<float>, params.thickness);
144-
append_side(triangles, upper_yz, -geo::unit_x<float>, geo::unit_y<float>, geo::unit_z<float>, params.thickness);
144+
append_side(triangles, lower_yz, geo::unit_x<float>, geo::unit_y<float>, geo::unit_z<float>, params.settings.thickness);
145+
append_side(triangles, upper_yz, -geo::unit_x<float>, geo::unit_y<float>, geo::unit_z<float>, params.settings.thickness);
145146
}
146147
}
147148

src/pstack/calc/sinterbox.hpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,21 @@
66

77
namespace pstack::calc {
88

9-
struct sinterbox_parameters {
9+
struct sinterbox_settings {
10+
double clearance = 0.8;
11+
double thickness = 0.8;
12+
double width = 1.1;
13+
double spacing = 6.0;
14+
};
15+
16+
struct sinterbox_bounding {
1017
geo::point3<float> min;
1118
geo::point3<float> max;
12-
double clearance;
13-
double thickness;
14-
double width;
15-
double spacing;
19+
};
20+
21+
struct sinterbox_parameters {
22+
sinterbox_settings settings;
23+
sinterbox_bounding bounding;
1624
};
1725

1826
void append_sinterbox(std::vector<geo::triangle>& triangles, const sinterbox_parameters& params);

src/pstack/calc/stacker.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010

1111
namespace pstack::calc {
1212

13+
void stack_result::reload_mesh() {
14+
this->mesh = {};
15+
for (const auto& piece : pieces) {
16+
auto m = piece.part->mesh;
17+
m.rotate(piece.rotation);
18+
this->mesh.add(m, piece.translation);
19+
}
20+
}
21+
1322
namespace {
1423

1524
struct stack_state {
@@ -129,7 +138,7 @@ std::optional<stack_result> stack_impl(const stack_parameters& params, const std
129138
state.voxels.assign(state.ordered_parts.size(), {});
130139

131140
double triangles = 0;
132-
const double scale_factor = 1 / params.resolution;
141+
const double scale_factor = 1 / params.settings.resolution;
133142
state.total_parts = 0;
134143
state.total_placed = 0;
135144
for (const std::shared_ptr<const part> part : state.ordered_parts) {
@@ -227,13 +236,13 @@ std::optional<stack_result> stack_impl(const stack_parameters& params, const std
227236
}
228237
}
229238

230-
int max_x = static_cast<int>(scale_factor * params.x_min);
231-
int max_y = static_cast<int>(scale_factor * params.y_min);
232-
int max_z = static_cast<int>(scale_factor * params.z_min);
239+
int max_x = static_cast<int>(scale_factor * params.settings.x_min);
240+
int max_y = static_cast<int>(scale_factor * params.settings.y_min);
241+
int max_z = static_cast<int>(scale_factor * params.settings.z_min);
233242
state.space = {
234-
std::max(max_x, static_cast<int>(scale_factor * params.x_max)),
235-
std::max(max_y, static_cast<int>(scale_factor * params.y_max)),
236-
std::max(max_z, static_cast<int>(scale_factor * params.z_max))
243+
std::max(max_x, static_cast<int>(scale_factor * params.settings.x_max)),
244+
std::max(max_y, static_cast<int>(scale_factor * params.settings.y_max)),
245+
std::max(max_z, static_cast<int>(scale_factor * params.settings.z_max))
237246
};
238247

239248
params.set_progress(0, 1);

src/pstack/calc/stacker.hpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,46 @@
1212

1313
namespace pstack::calc {
1414

15-
struct stack_result {
15+
// `stack_result_base` is the base level information about the result
16+
template <class Part, class Sinterbox>
17+
struct stack_result_base {
1618
struct piece {
17-
std::shared_ptr<const part> part;
19+
Part part;
1820
geo::matrix3<float> rotation;
1921
geo::vector3<float> translation;
2022
};
2123
std::vector<piece> pieces{};
24+
std::optional<Sinterbox> sinterbox{};
25+
};
2226

27+
// `stack_result` is everything that can be calculated/derived from the `stack_result_base`
28+
struct stack_result : stack_result_base<std::shared_ptr<const part>, sinterbox_parameters> {
2329
mesh mesh{};
2430
geo::vector3<float> size{};
2531
double density{};
26-
std::optional<sinterbox_parameters> sinterbox{};
32+
33+
void reload_mesh();
34+
};
35+
36+
struct stack_settings {
37+
double resolution = 1.0;
38+
int x_min = 150;
39+
int x_max = 156;
40+
int y_min = 150;
41+
int y_max = 156;
42+
int z_min = 30;
43+
int z_max = 90;
2744
};
2845

2946
struct stack_parameters {
3047
std::vector<std::shared_ptr<const part>> parts;
48+
stack_settings settings;
3149

3250
std::function<void(double, double)> set_progress;
3351
std::function<void(const mesh&, const geo::point3<int>)> display_mesh;
3452
std::function<void(stack_result, std::chrono::system_clock::duration)> on_success;
3553
std::function<void()> on_failure;
3654
std::function<void()> on_finish;
37-
38-
double resolution;
39-
int x_min, x_max;
40-
int y_min, y_max;
41-
int z_min, z_max;
4255
};
4356

4457
class stacker {

src/pstack/files/read.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
#include "pstack/files/read.hpp"
2+
#include <expected>
23
#include <filesystem>
34
#include <fstream>
45
#include <string>
56

67
namespace pstack::files {
78

89
// From StackOverflow https://stackoverflow.com/a/40903508
9-
std::string read_file(const std::string& file_path) {
10+
std::expected<std::string, std::string> read_file(const std::string& file_path) {
1011
std::ifstream file(file_path, std::ios::in | std::ios::binary);
1112
if (not file.is_open()) {
12-
return {};
13+
return std::unexpected("Could not read file: " + file_path);
1314
}
1415
const auto size = std::filesystem::file_size(file_path);
1516
std::string result(size, '\0');

src/pstack/files/read.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
#ifndef PSTACK_FILES_READ_HPP
22
#define PSTACK_FILES_READ_HPP
33

4+
#include <expected>
45
#include <string>
56

67
namespace pstack::files {
78

8-
std::string read_file(const std::string& file_path);
9+
std::expected<std::string, std::string> read_file(const std::string& file_path);
910

1011
} // namespace pstack::files
1112

0 commit comments

Comments
 (0)