mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-31 05:20:52 +01:00
feat(editor,track): a cant ramp of its own, off the transition curve: each end of a curve may have its ramp moved and of a length of its own (ST-T1-A6 9.6 pkt 5 and 11), on the straight before the curve or on the arc, the transition keeping the full cant all along, as a switch laid on it wants it; set in the plan under "Cant ramps", stored with the vertex as "ramps", reversed with the curve, marked across the track in the view. The cant of a line is a profile along it now, the paths cut where a ramp of its own starts and ends, their rolls following it, warnings where a ramp is shorter than its cant asks for or reaches into the curve before, past the line or over the other ramp. A switch on a cant ramp is drawn following it: its rails, blades, frogs and bed made at the mean of its rolls, as before, are turned along each path to the roll of the path there, so it no longer steps at its ends against the tracks either side. The checks of a switch laid into a curve warn of the values of table 11.9 in brackets as of the others, with no word of a consent. Unit test of a ramp moved onto the straight
This commit is contained in:
@@ -228,6 +228,25 @@ element const &element_at(result const &Result, double const Chainage)
|
||||
return lookup == Result.elements.begin() ? Result.elements.front() : *std::prev(lookup);
|
||||
}
|
||||
|
||||
// the cant of the line at the chainage, signed with the turn of its curve; where it changes all at once there, the one before the
|
||||
// chainage or the one after it
|
||||
double signed_cant(result const &Result, element const &Element, double const Distance, bool const Before)
|
||||
{
|
||||
auto const &cants{Result.cants};
|
||||
if (cants.empty())
|
||||
return Element.turn * element_cant(Element, Distance);
|
||||
auto const chainage{Element.chainage + Distance};
|
||||
auto const next{Before ? std::lower_bound(cants.begin(), cants.end(), chainage, [](glm::dvec2 const &Point, double const Value) { return Point.x < Value; })
|
||||
: std::upper_bound(cants.begin(), cants.end(), chainage, [](double const Value, glm::dvec2 const &Point) { return Value < Point.x; })};
|
||||
if (next == cants.begin())
|
||||
return cants.front().y;
|
||||
if (next == cants.end())
|
||||
return cants.back().y;
|
||||
auto const previous{std::prev(next)};
|
||||
auto const span{next->x - previous->x};
|
||||
return span < 1e-9 ? next->y : previous->y + (next->y - previous->y) * (chainage - previous->x) / span;
|
||||
}
|
||||
|
||||
glm::dvec2 blossom(std::array<glm::dvec2, 4> const &Points, double const A, double const B, double const C)
|
||||
{
|
||||
glm::dvec2 r[3], s[2];
|
||||
@@ -377,6 +396,126 @@ bool meets_norms(curve_report const &Curve, design const &Design)
|
||||
namespace
|
||||
{
|
||||
|
||||
// the length of a straight cant ramp the norms ask for to raise the cant at the speed: slow enough in time and along the track
|
||||
double ramp_length(double const Speed, double const Cant, limits const &Limits)
|
||||
{
|
||||
if (Cant <= 0.0)
|
||||
return 0.0;
|
||||
return ceil_to(std::max(Speed * Cant / (3.6 * Limits.cant_rate), Cant / 1000.0 * Limits.ramp_factor * Speed), 5.0);
|
||||
}
|
||||
|
||||
// the cant along the line: in each curve as its elements carry it, raised from 0 and lowered back to it on its ramps, which go along
|
||||
// its transition curves unless the vertex gives them a place and a length of their own
|
||||
void make_cants(design const &Design, result &R)
|
||||
{
|
||||
R.cants.clear();
|
||||
R.ramps.clear();
|
||||
auto const push = [&](double const Chainage, double const Cant) {
|
||||
if (false == R.cants.empty() && std::abs(R.cants.back().x - Chainage) < 1e-6 && std::abs(R.cants.back().y - Cant) < 1e-6)
|
||||
return;
|
||||
R.cants.push_back({Chainage, Cant});
|
||||
};
|
||||
double reached{0.0}; // where the cant of the curve before is down to 0 again
|
||||
for (std::size_t k = 0; k < Design.vertices.size(); ++k)
|
||||
{
|
||||
auto const &vertex{Design.vertices[k]};
|
||||
int first{-1};
|
||||
int last{-1};
|
||||
for (std::size_t i = 0; i < R.elements.size(); ++i)
|
||||
if (R.elements[i].vertex == static_cast<int>(k) && R.elements[i].length > 1e-6)
|
||||
{
|
||||
first = first < 0 ? static_cast<int>(i) : first;
|
||||
last = static_cast<int>(i);
|
||||
}
|
||||
if (first < 0)
|
||||
continue;
|
||||
auto const &head{R.elements[first]};
|
||||
auto const &tail{R.elements[last]};
|
||||
auto const start{head.chainage};
|
||||
auto const end{tail.chainage + tail.length};
|
||||
auto const turn{static_cast<double>(head.turn)};
|
||||
// the transitions at the ends: the first and the last element, where the curvature grows from 0 and goes back to it
|
||||
auto const entry{head.kind == element_kind::transition_in || (head.kind == element_kind::spiral && head.curvature_start == 0.0 && head.curvature_end > 0.0) ? head.length : 0.0};
|
||||
auto const exit{tail.kind == element_kind::transition_out || (tail.kind == element_kind::spiral && tail.curvature_end == 0.0 && tail.curvature_start > 0.0) ? tail.length : 0.0};
|
||||
auto const cantin{element_cant(head, entry)};
|
||||
auto const cantout{element_cant(tail, tail.length - exit)};
|
||||
if (std::max(cantin, cantout) < 0.5)
|
||||
{
|
||||
reached = end;
|
||||
continue;
|
||||
}
|
||||
result::cant_ramp in{static_cast<int>(k), start, start + entry, cantin, vertex.ramp_in_length >= 0.0};
|
||||
result::cant_ramp out{static_cast<int>(k), end - exit, end, cantout, vertex.ramp_out_length >= 0.0};
|
||||
if (in.own)
|
||||
{
|
||||
in.from = start + vertex.ramp_in_offset;
|
||||
in.to = in.from + vertex.ramp_in_length;
|
||||
}
|
||||
if (out.own)
|
||||
{
|
||||
out.to = end - vertex.ramp_out_offset;
|
||||
out.from = out.to - vertex.ramp_out_length;
|
||||
}
|
||||
// the ramps kept off the curve before, within the line and off each other
|
||||
if (in.from < reached - 1e-6)
|
||||
{
|
||||
R.warnings.emplace_back(R.ramps.empty() ? format(STR_C("Vertex %zu: the cant ramp in reaches back past the start of the line, cut %.1f m shorter"), k + 1, reached - in.from) :
|
||||
format(STR_C("Vertex %zu: the cant ramp in reaches back into the curve before it, cut %.1f m shorter"), k + 1, reached - in.from));
|
||||
in.from = std::min(reached, in.to);
|
||||
}
|
||||
if (out.to > R.length + 1e-6)
|
||||
{
|
||||
R.warnings.emplace_back(format(STR_C("Vertex %zu: the cant ramp out reaches past the end of the line, cut %.1f m shorter"), k + 1, out.to - R.length));
|
||||
out.to = std::max(R.length, out.from);
|
||||
}
|
||||
if (in.to > out.from + 1e-6)
|
||||
{
|
||||
R.warnings.emplace_back(format(STR_C("Vertex %zu: the cant ramps in and out of the curve overlap by %.1f m"), k + 1, in.to - out.from));
|
||||
auto const middle{(in.to + out.from) * 0.5};
|
||||
in.to = std::max(in.from, middle);
|
||||
out.from = std::min(out.to, middle);
|
||||
}
|
||||
auto const speed{std::max(1.0, Design.speed)};
|
||||
for (auto const *ramp : {&in, &out})
|
||||
if (ramp->own && Design.norms.checked)
|
||||
if (auto const required{ramp_length(speed, ramp->cant, Design.norms)}; ramp->to - ramp->from + 1e-6 < required)
|
||||
R.warnings.emplace_back(format(STR_C("Vertex %zu: the cant ramp of %.1f m is shorter than the %.0f m a cant of %.0f mm asks for"), k + 1, ramp->to - ramp->from, required, ramp->cant));
|
||||
// the curve between its ramps as its elements carry the cant, the transitions at its ends with the full cant of the curve where
|
||||
// the ramps went off them
|
||||
push(in.from, 0.0);
|
||||
push(in.to, turn * cantin);
|
||||
auto const low{std::max(in.to, start + entry)};
|
||||
auto const high{std::min(out.from, end - exit)};
|
||||
if (high > low + 1e-6)
|
||||
{
|
||||
push(low, turn * cantin);
|
||||
for (auto i{first}; i <= last; ++i)
|
||||
{
|
||||
auto const &e{R.elements[i]};
|
||||
for (auto const distance : {0.0, e.length})
|
||||
if (auto const chainage{e.chainage + distance}; chainage > low + 1e-6 && chainage < high - 1e-6)
|
||||
push(chainage, turn * element_cant(e, distance));
|
||||
}
|
||||
push(high, turn * cantout);
|
||||
}
|
||||
push(out.from, turn * cantout);
|
||||
push(out.to, 0.0);
|
||||
R.ramps.push_back(in);
|
||||
R.ramps.push_back(out);
|
||||
reached = out.to;
|
||||
// the time the cant takes to rise along the ramps of their own
|
||||
for (auto &curve : R.curves)
|
||||
if (curve.vertex == static_cast<int>(k))
|
||||
{
|
||||
if (in.own && in.to > in.from)
|
||||
curve.cant_rate_in = cantin * speed / 3.6 / (in.to - in.from);
|
||||
if (out.own && out.to > out.from)
|
||||
curve.cant_rate_out = cantout * speed / 3.6 / (out.to - out.from);
|
||||
}
|
||||
}
|
||||
std::stable_sort(R.cants.begin(), R.cants.end(), [](glm::dvec2 const &A, glm::dvec2 const &B) { return A.x < B.x; });
|
||||
}
|
||||
|
||||
result fit_between(design const &Design)
|
||||
{
|
||||
result r;
|
||||
@@ -953,6 +1092,7 @@ result fit_between(design const &Design)
|
||||
r.warnings.emplace_back(format(STR_C("The adjoining track at the start is curved (R %.0f m), the curvature changes abruptly"), Design.start_radius));
|
||||
if (Design.end_radius > 0.0 && Design.end_radius < 10000.0)
|
||||
r.warnings.emplace_back(format(STR_C("The adjoining track at the end is curved (R %.0f m), the curvature changes abruptly"), Design.end_radius));
|
||||
make_cants(Design, r);
|
||||
|
||||
r.valid = r.errors.empty() && r.length > 1e-3;
|
||||
return r;
|
||||
@@ -1828,8 +1968,10 @@ station evaluate(result const &Result, double const Chainage)
|
||||
element_point(e, Result.shape, distance, position, direction);
|
||||
result.position = {position.x, elevation(Result, s), position.y};
|
||||
result.direction = glm::normalize(glm::dvec3{direction.x, grade(Result, s), direction.y});
|
||||
result.cant = element_cant(e, distance);
|
||||
result.turn = e.turn;
|
||||
// a cant ramp of its own may lie on the straight before the curve, its cant turned the way the curve turns
|
||||
auto const cant{signed_cant(Result, e, distance, false)};
|
||||
result.cant = std::abs(cant);
|
||||
result.turn = e.turn != 0 ? e.turn : (cant > 0.0) - (cant < 0.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2435,6 +2577,12 @@ std::vector<segment_data> pieces(result const &Result, design const &Design, std
|
||||
spans.insert(std::next(Span), second);
|
||||
};
|
||||
double constexpr margin{0.01};
|
||||
// the ends of the cant ramps of their own, where the cant starts or stops changing along a straight, a transition or an arc
|
||||
for (auto const &ramp : Result.ramps)
|
||||
for (auto const chainage : {ramp.from, ramp.to})
|
||||
if (auto inside{std::find_if(spans.begin(), spans.end(), [&](span const &Span) { return chainage_from(Span) < chainage - margin && chainage_to(Span) > chainage + margin; })};
|
||||
ramp.own && inside != spans.end())
|
||||
split(inside, chainage);
|
||||
for (auto const chainage : Breaks)
|
||||
{
|
||||
auto inside{std::find_if(spans.begin(), spans.end(), [&](span const &Span) { return chainage_from(Span) < chainage - margin && chainage_to(Span) > chainage + margin; })};
|
||||
@@ -2566,15 +2714,16 @@ std::vector<segment_data> pieces(result const &Result, design const &Design, std
|
||||
auto const y1{y0 + grade(Result, s0) * glm::distance(p0, p1)};
|
||||
auto const y2{y3 - grade(Result, s3) * glm::distance(p2, p3)};
|
||||
|
||||
auto const roll = [&](double const Cant) { return static_cast<float>(e.turn * glm::degrees(std::asin(std::clamp(Cant / Design.norms.gauge, -1.0, 1.0)))); };
|
||||
// the cant signed with the turn, a ramp of its own on a straight too; where it changes all at once, the one of the piece itself
|
||||
auto const roll = [&](double const Cant) { return static_cast<float>(glm::degrees(std::asin(std::clamp(Cant / Design.norms.gauge, -1.0, 1.0)))); };
|
||||
|
||||
segment_data path;
|
||||
path.points[segment_data::point::start] = {p0.x, y0, p0.y};
|
||||
path.points[segment_data::point::end] = {p3.x, y3, p3.y};
|
||||
path.points[segment_data::point::control1] = glm::dvec3{p1.x, y1, p1.y} - path.points[segment_data::point::start];
|
||||
path.points[segment_data::point::control2] = glm::dvec3{p2.x, y2, p2.y} - path.points[segment_data::point::end];
|
||||
path.rolls[0] = roll(element_cant(e, piece.from));
|
||||
path.rolls[1] = roll(element_cant(e, piece.to));
|
||||
path.rolls[0] = roll(signed_cant(Result, e, piece.from, false));
|
||||
path.rolls[1] = roll(signed_cant(Result, e, piece.to, true));
|
||||
path.radius = e.kind == element_kind::straight ? 0.f : static_cast<float>(e.radius);
|
||||
if (e.kind == element_kind::spiral)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,14 @@ struct vertex
|
||||
// two transition curves meeting at the curvature of the radius, with no arc between them and no cant: the bend of two straights
|
||||
// eased the way the lines are drawn. Their lengths follow from the radius and the deflection, the ones given are left out
|
||||
bool spirals{false};
|
||||
// the cant ramp at each end of the curve where it doesn't go along the transition curve (ST-T1-A6 9.6 pkt 5 and 11): how far its
|
||||
// outer end lies into the curve from the start of the curve, or from its end, below 0 out along the straight, and its length; a
|
||||
// length below 0 keeps the ramp along the transition, as the norms recommend
|
||||
double ramp_in_offset{0.0};
|
||||
double ramp_in_length{-1.0};
|
||||
double ramp_out_offset{0.0};
|
||||
double ramp_out_length{-1.0};
|
||||
bool own_ramps() const { return ramp_in_length >= 0.0 || ramp_out_length >= 0.0; }
|
||||
};
|
||||
|
||||
struct design
|
||||
@@ -187,6 +195,19 @@ struct result
|
||||
std::vector<element> elements;
|
||||
std::vector<curve_report> curves;
|
||||
std::vector<profile_point> profile;
|
||||
// the cant along the line, mm at the chainages, between them changing evenly; signed with the turn of the curve it belongs to,
|
||||
// as the rolls of the paths are
|
||||
std::vector<glm::dvec2> cants;
|
||||
// the cant ramps at the ends of the curves, where the cant goes from 0 to the one of the curve
|
||||
struct cant_ramp
|
||||
{
|
||||
int vertex{-1};
|
||||
double from{0.0};
|
||||
double to{0.0};
|
||||
double cant{0.0}; // mm, at the end of the ramp in the curve
|
||||
bool own{false}; // not along the transition curve
|
||||
};
|
||||
std::vector<cant_ramp> ramps;
|
||||
double length{0.0};
|
||||
glm::dvec3 start{0.0};
|
||||
glm::dvec3 end{0.0};
|
||||
|
||||
@@ -48,6 +48,8 @@ bool g_loaded{false};
|
||||
// vertex <x> <z> <offset> <R> <L in> <L out> <cant> <reverse 0/1> <compound 0/1> <share> <arcs> [<R> <L> <share>]... [spirals 1]
|
||||
// spirals: two transition curves with no arc between them, their lengths following from R
|
||||
// [cants <first> <arc>...]: the cants of the radii of a compound curve set one by one, below 0 the cant in proportion to the curvature
|
||||
// [ramps <in offset> <in length> <out offset> <out length>]: the cant ramps of the curve where they don't go along its transitions,
|
||||
// their outer ends from the start and from the end of the curve, into it above 0; a length below 0 keeps the ramp along the transition
|
||||
// click <x y z>: a point clicked when the line was laid, its curves fitted to them
|
||||
// element <straight|transition|arc> <vertex> <chainage> <length> <R at the start> <R at the end> <cant> <start x> <start z> <direction x> <direction z>
|
||||
// radii signed with the turn, 0 for no curvature
|
||||
@@ -147,6 +149,8 @@ void mark(scene::layer_handle const Layer)
|
||||
for (auto const &arc : vertex.arcs)
|
||||
text += format(" %.1f", arc.cant);
|
||||
}
|
||||
if (vertex.own_ramps())
|
||||
text += format(" ramps %.3f %.3f %.3f %.3f", vertex.ramp_in_offset, vertex.ramp_in_length, vertex.ramp_out_offset, vertex.ramp_out_length);
|
||||
lines.push_back(text);
|
||||
}
|
||||
for (auto const &click : entry.clicks)
|
||||
@@ -168,6 +172,8 @@ void mark(scene::layer_handle const Layer)
|
||||
alignment::vertex reversed_vertex(alignment::vertex Vertex)
|
||||
{
|
||||
std::swap(Vertex.transition_in, Vertex.transition_out);
|
||||
std::swap(Vertex.ramp_in_offset, Vertex.ramp_out_offset);
|
||||
std::swap(Vertex.ramp_in_length, Vertex.ramp_out_length);
|
||||
if (Vertex.compound && false == Vertex.arcs.empty())
|
||||
{
|
||||
struct part
|
||||
@@ -343,6 +349,8 @@ std::optional<alignment::vertex> read_vertex(std::istringstream &Line, scene::la
|
||||
for (auto &arc : vertex.arcs)
|
||||
Line >> arc.cant;
|
||||
}
|
||||
if (word == "ramps")
|
||||
Line >> vertex.ramp_in_offset >> vertex.ramp_in_length >> vertex.ramp_out_offset >> vertex.ramp_out_length;
|
||||
}
|
||||
position = Context.to_world(position);
|
||||
vertex.position = {position.x, position.z};
|
||||
|
||||
@@ -30,17 +30,15 @@ constexpr double kAtEnd{0.3};
|
||||
constexpr double kRampClear{6.0};
|
||||
|
||||
// the curved switches bent from the basic ones, table 11.9: the radius of the basic switch, the limiting curvature of the
|
||||
// diverging track and the least radius of the main track, the values allowed by way of exception in the brackets, 0 for none
|
||||
// diverging track and the least radius of the main track; the values in its brackets, allowed only by way of exception, are past
|
||||
// them as well
|
||||
struct bent_limit
|
||||
{
|
||||
double radius;
|
||||
double branch;
|
||||
double branch_exception;
|
||||
double main;
|
||||
double main_exception;
|
||||
};
|
||||
constexpr bent_limit kBent[]{
|
||||
{300.0, 4.000e-3, 4.673e-3, 1505.522, 749.722}, {500.0, 4.000e-3, 4.673e-3, 501.730, 375.638}, {760.0, 3.333e-3, 0.0, 497.250, 0.0}, {1200.0, 2.169e-3, 0.0, 750.001, 0.0}, {2500.0, 1.063e-3, 0.0, 1510.0, 0.0}};
|
||||
constexpr bent_limit kBent[]{{300.0, 4.000e-3, 1505.522}, {500.0, 4.000e-3, 501.730}, {760.0, 3.333e-3, 497.250}, {1200.0, 2.169e-3, 750.001}, {2500.0, 1.063e-3, 1510.0}};
|
||||
|
||||
double heading_at(std::vector<track_point> const &Track, double const Station)
|
||||
{
|
||||
@@ -147,8 +145,6 @@ switch_report check(std::vector<track_point> const &Main, std::vector<track_poin
|
||||
report.curved = main.sharpest > kStraight;
|
||||
report.transition = report.curved && std::abs(main.last - main.first) > std::max(2e-6, 0.05 * std::max(std::abs(main.first), std::abs(main.last)));
|
||||
|
||||
if (report.transition)
|
||||
report.notes.emplace_back(STR_C("On a transition curve: only by way of exception, by consent of the Zaklad Linii Kolejowych (9.7 pkt 12); the plan gives the radii of both tracks at the start and at the end of the switch"));
|
||||
// the curvature of each track of one sign all along it, the cases KZW I and KZW III of 9.7 pkt 12, not KZW II
|
||||
if (branch.low < -kStraight && branch.high > kStraight)
|
||||
report.warnings.emplace_back(STR_C("The curvature of the diverging track turns from one side to the other within the switch (KZW II), which isn't allowed (9.7 pkt 12)"));
|
||||
@@ -162,14 +158,9 @@ switch_report check(std::vector<track_point> const &Main, std::vector<track_poin
|
||||
else
|
||||
{
|
||||
if (branch.sharpest > limit->branch + 1e-9)
|
||||
report.warnings.emplace_back(limit->branch_exception > 0.0 && branch.sharpest <= limit->branch_exception + 1e-9 ?
|
||||
format(STR_C("The curvature of the diverging track %.3f 1/km is over the limiting %.3f 1/km of table 11.9, up to %.3f 1/km only by consent of the Zaklad Linii Kolejowych"), branch.sharpest * 1000.0,
|
||||
limit->branch * 1000.0, limit->branch_exception * 1000.0) :
|
||||
format(STR_C("The curvature of the diverging track %.3f 1/km is over the limiting %.3f 1/km for the switch of R %.0f m (table 11.9)"), branch.sharpest * 1000.0, limit->branch * 1000.0, Radius));
|
||||
report.warnings.emplace_back(format(STR_C("The curvature of the diverging track %.3f 1/km is over the limiting %.3f 1/km for the switch of R %.0f m (table 11.9)"), branch.sharpest * 1000.0, limit->branch * 1000.0, Radius));
|
||||
if (main.sharpest > kStraight && 1.0 / main.sharpest < limit->main - 1e-6)
|
||||
report.warnings.emplace_back(limit->main_exception > 0.0 && 1.0 / main.sharpest >= limit->main_exception - 1e-6 ?
|
||||
format(STR_C("The main track bent to R %.0f m, below the least %.0f m of table 11.9, down to %.0f m only by consent of the Zaklad Linii Kolejowych"), 1.0 / main.sharpest, limit->main, limit->main_exception) :
|
||||
format(STR_C("The main track bent to R %.0f m, below the least %.0f m for the switch of R %.0f m (table 11.9)"), 1.0 / main.sharpest, limit->main, Radius));
|
||||
report.warnings.emplace_back(format(STR_C("The main track bent to R %.0f m, below the least %.0f m for the switch of R %.0f m (table 11.9)"), 1.0 / main.sharpest, limit->main, Radius));
|
||||
}
|
||||
if (Ratio > 0.0 && Ratio < 9.41)
|
||||
report.warnings.emplace_back(format(STR_C("A switch of 1:%.3f isn't bent in the running and the main tracks: none of 1:9.403 nor steeper than 1:9 (11.4 pkt 4)"), Ratio));
|
||||
@@ -177,8 +168,7 @@ switch_report check(std::vector<track_point> const &Main, std::vector<track_poin
|
||||
|
||||
// the cant: the same all along the switch, a ramp off it by 6 m at least or ending at its ends (11.4 pkt 7, 9.6 pkt 16-17)
|
||||
if (std::abs(report.cant_end - report.cant_start) > 1.0)
|
||||
report.warnings.emplace_back(format(STR_C("The cant changes within the switch, %.0f to %.0f mm: it's to stay the same over it (11.4 pkt 7); a straight ramp through the switch only by consent of the Zaklad Linii Kolejowych"),
|
||||
report.cant_start, report.cant_end));
|
||||
report.warnings.emplace_back(format(STR_C("The cant changes within the switch, %.0f to %.0f mm: it's to stay the same over it (11.4 pkt 7)"), report.cant_start, report.cant_end));
|
||||
auto const low{std::min(From, To)};
|
||||
auto const high{std::max(From, To)};
|
||||
for (auto const end : ramp_ends(Line))
|
||||
|
||||
Reference in New Issue
Block a user