mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 15:49:01 +08:00
bug fixes, raycasting to find good support spot
This commit is contained in:
parent
bef26fee2b
commit
8dc3956b64
@ -423,27 +423,29 @@ void PrintObject::find_supportable_issues()
|
|||||||
SupportableIssues::Issues issues = SupportableIssues::full_search(this);
|
SupportableIssues::Issues issues = SupportableIssues::full_search(this);
|
||||||
//TODO fix
|
//TODO fix
|
||||||
// if (!issues.supports_nedded.empty()) {
|
// if (!issues.supports_nedded.empty()) {
|
||||||
auto obj_transform = this->trafo_centered();
|
auto obj_transform = this->trafo_centered();
|
||||||
for (ModelVolume *model_volume : this->model_object()->volumes) {
|
for (ModelVolume *model_volume : this->model_object()->volumes) {
|
||||||
if (model_volume->type() == ModelVolumeType::MODEL_PART) {
|
if (model_volume->type() == ModelVolumeType::MODEL_PART) {
|
||||||
Transform3d model_transformation = model_volume->get_matrix();
|
Transform3d model_transformation = model_volume->get_matrix();
|
||||||
Transform3d inv_transform = (obj_transform * model_transformation).inverse();
|
Transform3f inv_transform = (obj_transform * model_transformation).inverse().cast<float>();
|
||||||
TriangleSelectorWrapper selector { model_volume->mesh() };
|
TriangleSelectorWrapper selector { model_volume->mesh() };
|
||||||
|
|
||||||
for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) {
|
for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) {
|
||||||
selector.enforce_spot(Vec3f(inv_transform.cast<float>() * support_point.position), 0.5f);
|
Vec3f point = Vec3f(inv_transform * support_point.position);
|
||||||
}
|
Vec3f origin = Vec3f(
|
||||||
|
inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f));
|
||||||
model_volume->supported_facets.set(selector.selector);
|
selector.enforce_spot(point, origin, 0.5f);
|
||||||
|
|
||||||
#if 1
|
|
||||||
indexed_triangle_set copy = model_volume->mesh().its;
|
|
||||||
its_transform(copy, obj_transform * model_transformation);
|
|
||||||
its_write_obj(copy,
|
|
||||||
debug_out_path("model.obj").c_str());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model_volume->supported_facets.set(selector.selector);
|
||||||
|
#if 1
|
||||||
|
indexed_triangle_set copy = model_volume->mesh().its;
|
||||||
|
its_transform(copy, obj_transform * model_transformation);
|
||||||
|
its_write_obj(copy,
|
||||||
|
debug_out_path("model.obj").c_str());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ struct StabilityAccumulators {
|
|||||||
private:
|
private:
|
||||||
size_t next_id = 0;
|
size_t next_id = 0;
|
||||||
std::unordered_map<size_t, size_t> mapping;
|
std::unordered_map<size_t, size_t> mapping;
|
||||||
std::vector<StabilityAccumulator> acccumulators;
|
std::vector<StabilityAccumulator> accumulators;
|
||||||
|
|
||||||
void merge_to(size_t from_id, size_t to_id) {
|
void merge_to(size_t from_id, size_t to_id) {
|
||||||
StabilityAccumulator &from_acc = this->access(from_id);
|
StabilityAccumulator &from_acc = this->access(from_id);
|
||||||
@ -211,13 +211,13 @@ public:
|
|||||||
int create_accumulator(float base_height) {
|
int create_accumulator(float base_height) {
|
||||||
size_t id = next_id;
|
size_t id = next_id;
|
||||||
next_id++;
|
next_id++;
|
||||||
mapping[id] = acccumulators.size();
|
mapping[id] = accumulators.size();
|
||||||
acccumulators.push_back(StabilityAccumulator { base_height });
|
accumulators.push_back(StabilityAccumulator { base_height });
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
StabilityAccumulator& access(size_t id) {
|
StabilityAccumulator& access(size_t id) {
|
||||||
return acccumulators[mapping[id]];
|
return accumulators[mapping[id]];
|
||||||
}
|
}
|
||||||
|
|
||||||
void merge_accumulators(size_t from_id, size_t to_id) {
|
void merge_accumulators(size_t from_id, size_t to_id) {
|
||||||
@ -235,24 +235,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_FILES
|
#ifdef DEBUG_FILES
|
||||||
Vec3f get_emerging_color(size_t id) {
|
Vec3f get_accumulator_color(size_t id) {
|
||||||
if (mapping.find(id) == mapping.end()) {
|
if (mapping.find(id) == mapping.end()) {
|
||||||
std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl;
|
std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl;
|
||||||
return Vec3f(1.0f, 1.0f, 1.0f);
|
return Vec3f(1.0f, 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t pseudornd = ((id + 127) * 33331 + 6907) % 13;
|
size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 987;
|
||||||
return value_to_rgbf(0.0f, 13.0f, float(pseudornd));
|
return value_to_rgbf(0.0f, float(987), float(pseudornd));
|
||||||
}
|
|
||||||
|
|
||||||
Vec3f get_final_color(size_t id) {
|
|
||||||
if (mapping.find(id) == mapping.end()) {
|
|
||||||
std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl;
|
|
||||||
return Vec3f(1.0f, 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 13;
|
|
||||||
return value_to_rgbf(0.0f, 13.0f, float(pseudornd));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@ -345,10 +335,12 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity,
|
|||||||
// -> it prevents extruding perimeter start and short loops into air.
|
// -> it prevents extruding perimeter start and short loops into air.
|
||||||
const float flow_width = get_flow_width(layer_region, entity->role());
|
const float flow_width = get_flow_width(layer_region, entity->role());
|
||||||
const float max_allowed_dist_from_prev_layer = flow_width;
|
const float max_allowed_dist_from_prev_layer = flow_width;
|
||||||
|
float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f;
|
||||||
|
|
||||||
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
|
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
|
||||||
ExtrusionLine ¤t_line = lines[line_idx];
|
ExtrusionLine ¤t_line = lines[line_idx];
|
||||||
Point current = Point::new_scale(current_line.b);
|
Point current = Point::new_scale(current_line.b);
|
||||||
|
distance_from_last_support_point += current_line.len;
|
||||||
float mm3_per_mm = float(entity->min_mm3_per_mm());
|
float mm3_per_mm = float(entity->min_mm3_per_mm());
|
||||||
|
|
||||||
float curr_angle = 0;
|
float curr_angle = 0;
|
||||||
@ -381,13 +373,15 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity,
|
|||||||
StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc);
|
StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc);
|
||||||
current_line.stability_accumulator_id = current_stability_acc;
|
current_line.stability_accumulator_id = current_stability_acc;
|
||||||
current_segment.add_extrusion(current_line, print_z, mm3_per_mm);
|
current_segment.add_extrusion(current_line, print_z, mm3_per_mm);
|
||||||
if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports.
|
if (distance_from_last_support_point > params.min_distance_between_support_points &&
|
||||||
> params.bridge_distance
|
bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports.
|
||||||
/ (1.0f + (bridging_acc.max_curvature
|
> params.bridge_distance
|
||||||
* params.bridge_distance_decrease_by_curvature_factor / PI))) {
|
/ (1.0f + (bridging_acc.max_curvature
|
||||||
current_segment.add_support_point(current, params.support_points_interface_area);
|
* params.bridge_distance_decrease_by_curvature_factor / PI))) {
|
||||||
|
current_segment.add_support_point(current, params.extrusion_support_points_area);
|
||||||
issues.supports_nedded.emplace_back(to_vec3f(current), 1.0);
|
issues.supports_nedded.emplace_back(to_vec3f(current), 1.0);
|
||||||
bridging_acc.reset();
|
bridging_acc.reset();
|
||||||
|
distance_from_last_support_point = 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,10 +424,13 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs,
|
|||||||
|
|
||||||
float sticking_force = acc->get_base_area()
|
float sticking_force = acc->get_base_area()
|
||||||
* (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion);
|
* (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion);
|
||||||
float weight = acc->get_accumulated_volume() * params.filament_density * params.gravity_constant;
|
float mass = acc->get_accumulated_volume() * params.filament_density;
|
||||||
|
float weight = mass * params.gravity_constant;
|
||||||
|
|
||||||
|
float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f;
|
||||||
for (size_t line_idx : acc_lines.second) {
|
for (size_t line_idx : acc_lines.second) {
|
||||||
const ExtrusionLine &line = checked_lines[line_idx];
|
const ExtrusionLine &line = checked_lines[line_idx];
|
||||||
|
distance_from_last_support_point += line.len;
|
||||||
|
|
||||||
Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized();
|
Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized();
|
||||||
Vec2f pivot_site_search = line.b + extruder_pressure_direction.head<2>() * 1000.0f;
|
Vec2f pivot_site_search = line.b + extruder_pressure_direction.head<2>() * 1000.0f;
|
||||||
@ -457,7 +454,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs,
|
|||||||
std::cout << "weight_torque: " << weight_torque << std::endl;
|
std::cout << "weight_torque: " << weight_torque << std::endl;
|
||||||
|
|
||||||
float bed_movement_arm = centroid.z() - acc->get_base_height();
|
float bed_movement_arm = centroid.z() - acc->get_base_height();
|
||||||
float bed_movement_force = params.max_acceleration * weight;
|
float bed_movement_force = params.max_acceleration * mass;
|
||||||
float bed_movement_torque = bed_movement_force * bed_movement_arm;
|
float bed_movement_torque = bed_movement_force * bed_movement_arm;
|
||||||
|
|
||||||
std::cout << "bed_movement_arm: " << bed_movement_arm << std::endl;
|
std::cout << "bed_movement_arm: " << bed_movement_arm << std::endl;
|
||||||
@ -475,8 +472,9 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs,
|
|||||||
std::cout << "total_torque: " << total_torque << " printz: " << print_z << std::endl;
|
std::cout << "total_torque: " << total_torque << " printz: " << print_z << std::endl;
|
||||||
|
|
||||||
if (total_torque > 0) {
|
if (total_torque > 0) {
|
||||||
acc->add_support_point(Point::new_scale(line.b), params.support_points_interface_area);
|
acc->add_support_point(Point::new_scale(line.b), std::min(params.support_points_interface_area, (pivot - line.b).squaredNorm()));
|
||||||
issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque);
|
issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque);
|
||||||
|
distance_from_last_support_point = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -485,8 +483,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs,
|
|||||||
|
|
||||||
Issues check_object_stability(const PrintObject *po, const Params ¶ms) {
|
Issues check_object_stability(const PrintObject *po, const Params ¶ms) {
|
||||||
#ifdef DEBUG_FILES
|
#ifdef DEBUG_FILES
|
||||||
FILE *eacc = boost::nowide::fopen(debug_out_path("emerging_accumulators.obj").c_str(), "w");
|
FILE *debug_acc = boost::nowide::fopen(debug_out_path("accumulators.obj").c_str(), "w");
|
||||||
FILE *facc = boost::nowide::fopen(debug_out_path("final_accumulators.obj").c_str(), "w");
|
|
||||||
#endif
|
#endif
|
||||||
StabilityAccumulators stability_accs;
|
StabilityAccumulators stability_accs;
|
||||||
LayerLinesDistancer prev_layer_lines { { } };
|
LayerLinesDistancer prev_layer_lines { { } };
|
||||||
@ -548,15 +545,11 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) {
|
|||||||
|
|
||||||
#ifdef DEBUG_FILES
|
#ifdef DEBUG_FILES
|
||||||
for (const auto &line : checked_lines) {
|
for (const auto &line : checked_lines) {
|
||||||
Vec3f ecolor = stability_accs.get_emerging_color(line.stability_accumulator_id);
|
Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id);
|
||||||
fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0],
|
fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0],
|
||||||
line.b[1], base_print_z, ecolor[0], ecolor[1], ecolor[2]);
|
line.b[1], base_print_z, color[0], color[1], color[2]);
|
||||||
|
|
||||||
Vec3f fcolor = stability_accs.get_final_color(line.stability_accumulator_id);
|
|
||||||
fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0],
|
|
||||||
line.b[1], base_print_z, fcolor[0], fcolor[1], fcolor[2]);
|
|
||||||
}
|
}
|
||||||
#endif DEBUG_FILES
|
#endif
|
||||||
|
|
||||||
//MERGE BASE LAYER STABILITY ACCS
|
//MERGE BASE LAYER STABILITY ACCS
|
||||||
prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) };
|
prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) };
|
||||||
@ -564,7 +557,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) {
|
|||||||
size_t nearest_line_idx;
|
size_t nearest_line_idx;
|
||||||
Vec2f nearest_pt;
|
Vec2f nearest_pt;
|
||||||
float dist = prev_layer_lines.signed_distance_from_lines(l.a, nearest_line_idx, nearest_pt);
|
float dist = prev_layer_lines.signed_distance_from_lines(l.a, nearest_line_idx, nearest_pt);
|
||||||
if (std::abs(dist) < max_flow_width) {
|
if (std::abs(dist) < max_flow_width*1.1f) {
|
||||||
size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id;
|
size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id;
|
||||||
size_t from_id = std::max(other_line_acc_id, l.stability_accumulator_id);
|
size_t from_id = std::max(other_line_acc_id, l.stability_accumulator_id);
|
||||||
size_t to_id = std::min(other_line_acc_id, l.stability_accumulator_id);
|
size_t to_id = std::min(other_line_acc_id, l.stability_accumulator_id);
|
||||||
@ -646,20 +639,15 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) {
|
|||||||
|
|
||||||
#ifdef DEBUG_FILES
|
#ifdef DEBUG_FILES
|
||||||
for (const auto &line : prev_layer_lines.get_lines()) {
|
for (const auto &line : prev_layer_lines.get_lines()) {
|
||||||
Vec3f ecolor = stability_accs.get_emerging_color(line.stability_accumulator_id);
|
Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id);
|
||||||
fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0],
|
fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0],
|
||||||
line.b[1], print_z, ecolor[0], ecolor[1], ecolor[2]);
|
line.b[1], print_z, color[0], color[1], color[2]);
|
||||||
|
|
||||||
Vec3f fcolor = stability_accs.get_final_color(line.stability_accumulator_id);
|
|
||||||
fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0],
|
|
||||||
line.b[1], print_z, fcolor[0], fcolor[1], fcolor[2]);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_FILES
|
#ifdef DEBUG_FILES
|
||||||
fclose(eacc);
|
fclose(debug_acc);
|
||||||
fclose(facc);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl;
|
std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl;
|
||||||
|
@ -10,14 +10,18 @@ namespace SupportableIssues {
|
|||||||
struct Params {
|
struct Params {
|
||||||
const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position.
|
const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position.
|
||||||
|
|
||||||
|
|
||||||
float bridge_distance = 10.0f; //mm
|
float bridge_distance = 10.0f; //mm
|
||||||
float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) )
|
float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) )
|
||||||
|
|
||||||
|
float min_distance_between_support_points = 0.5f;
|
||||||
|
|
||||||
// Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2
|
// Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2
|
||||||
float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer
|
float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer
|
||||||
float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer
|
float support_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of support interface layer
|
||||||
float support_points_interface_area = 2.0f; // mm^2
|
float support_points_interface_area = 5.0f; // mm^2
|
||||||
|
float extrusion_support_points_area = 0.5f; // much lower value, because these support points appear due to unsupported extrusion,
|
||||||
|
// not stability - they can be very densely placed, making the sticking estimation incorrect
|
||||||
|
|
||||||
float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY
|
float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY
|
||||||
float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important
|
float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important
|
||||||
|
|
||||||
|
@ -10,22 +10,26 @@ TriangleSelectorWrapper::TriangleSelectorWrapper(const TriangleMesh &mesh) :
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, float radius) {
|
void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &origin, float radius) {
|
||||||
size_t hit_face_index;
|
std::vector<igl::Hit> hits;
|
||||||
Vec3f hit_point;
|
Vec3f dir = (point - origin).normalized();
|
||||||
auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices,
|
if (AABBTreeIndirect::intersect_ray_all_hits(mesh.its.vertices, mesh.its.indices, triangles_tree,
|
||||||
triangles_tree,
|
Vec3d(origin.cast<double>()),
|
||||||
point, hit_face_index, hit_point);
|
Vec3d(dir.cast<double>()),
|
||||||
if (dist < 0 || dist > radius * radius) {
|
hits)) {
|
||||||
return;
|
for (int hit_idx = hits.size() - 1; hit_idx >= 0; --hit_idx) {
|
||||||
|
const igl::Hit &hit = hits[hit_idx];
|
||||||
|
Vec3f pos = origin + dir * hit.t;
|
||||||
|
Vec3f face_normal = its_face_normal(mesh.its, hit.id);
|
||||||
|
if (point.z() + radius > pos.z() && face_normal.dot(dir) < 0) {
|
||||||
|
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(
|
||||||
|
pos, origin, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { });
|
||||||
|
selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(),
|
||||||
|
true, 0.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(hit_point, point,
|
|
||||||
radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { });
|
|
||||||
|
|
||||||
selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(),
|
|
||||||
true,
|
|
||||||
0.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ public:
|
|||||||
|
|
||||||
TriangleSelectorWrapper(const TriangleMesh &mesh);
|
TriangleSelectorWrapper(const TriangleMesh &mesh);
|
||||||
|
|
||||||
void enforce_spot(const Vec3f &point, float radius);
|
void enforce_spot(const Vec3f &point, const Vec3f& origin, float radius);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user