gmio_support: iterate on all StlMesh_Mesh's domains
This commit is contained in:
parent
9cf0ac13df
commit
706fc26b0e
@ -61,7 +61,7 @@ static void stl_read(const void* filepath)
|
|||||||
stlMesh = new StlMesh_Mesh;
|
stlMesh = new StlMesh_Mesh;
|
||||||
const int error = gmio_stl_read_file(
|
const int error = gmio_stl_read_file(
|
||||||
static_cast<const char*>(filepath),
|
static_cast<const char*>(filepath),
|
||||||
gmio_stl_hnd_occmesh_creator(stlMesh),
|
gmio_stl_occmesh_creator(stlMesh),
|
||||||
NULL);
|
NULL);
|
||||||
if (error != GMIO_ERROR_OK)
|
if (error != GMIO_ERROR_OK)
|
||||||
printf("gmio error: 0x%X\n", error);
|
printf("gmio error: 0x%X\n", error);
|
||||||
@ -69,7 +69,6 @@ static void stl_read(const void* filepath)
|
|||||||
|
|
||||||
static void stl_write(const char* filepath, gmio_stl_format format)
|
static void stl_write(const char* filepath, gmio_stl_format format)
|
||||||
{
|
{
|
||||||
const gmio_occ_stl_mesh_domain occ_mesh_domain(stlMesh);
|
|
||||||
gmio_stl_write_options options = {};
|
gmio_stl_write_options options = {};
|
||||||
options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE;
|
options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE;
|
||||||
options.stla_float32_prec = 7;
|
options.stla_float32_prec = 7;
|
||||||
@ -77,7 +76,7 @@ static void stl_write(const char* filepath, gmio_stl_format format)
|
|||||||
gmio_stl_write_file(
|
gmio_stl_write_file(
|
||||||
format,
|
format,
|
||||||
static_cast<const char*>(filepath),
|
static_cast<const char*>(filepath),
|
||||||
gmio_stl_occmesh(&occ_mesh_domain),
|
gmio_stl_occmesh(stlMesh),
|
||||||
&options);
|
&options);
|
||||||
if (error != GMIO_ERROR_OK)
|
if (error != GMIO_ERROR_OK)
|
||||||
printf("gmio error: 0x%X\n", error);
|
printf("gmio error: 0x%X\n", error);
|
||||||
|
@ -31,7 +31,7 @@ static StlMesh_Mesh* occMeshPtr(const Handle_StlMesh_Mesh& mesh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void occmesh_add_triangle(
|
static void occmesh_add_triangle(
|
||||||
void* cookie, uint32_t tri_id, const struct gmio_stl_triangle* tri)
|
void* cookie, uint32_t tri_id, const gmio_stl_triangle* tri)
|
||||||
{
|
{
|
||||||
StlMesh_Mesh* mesh = static_cast<StlMesh_Mesh*>(cookie);
|
StlMesh_Mesh* mesh = static_cast<StlMesh_Mesh*>(cookie);
|
||||||
if (tri_id == 0)
|
if (tri_id == 0)
|
||||||
@ -46,94 +46,113 @@ static void occmesh_add_triangle(
|
|||||||
n.x, n.y, n.z);
|
n.x, n.y, n.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void occmesh_get_triangle(
|
static inline void gmio_stl_occ_copy_xyz(
|
||||||
const void* cookie, uint32_t tri_id, struct gmio_stl_triangle* tri)
|
gmio_stl_coords* stl_coords, const gp_XYZ& coords)
|
||||||
{
|
{
|
||||||
const struct gmio_occ_stl_mesh_domain* mesh_domain =
|
stl_coords->x = static_cast<float>(coords.X());
|
||||||
static_cast<const struct gmio_occ_stl_mesh_domain*>(cookie);
|
stl_coords->y = static_cast<float>(coords.Y());
|
||||||
const Handle_StlMesh_MeshTriangle& occTri =
|
stl_coords->z = static_cast<float>(coords.Z());
|
||||||
mesh_domain->triangles()->Value(tri_id + 1);
|
}
|
||||||
int idV1;
|
|
||||||
int idV2;
|
static void occmesh_get_triangle(
|
||||||
int idV3;
|
const void* cookie, uint32_t tri_id, gmio_stl_triangle* tri)
|
||||||
double xN;
|
{
|
||||||
double yN;
|
void* wcookie = const_cast<void*>(cookie);
|
||||||
double zN;
|
gmio_stl_occmesh_iterator* it =
|
||||||
|
static_cast<gmio_stl_occmesh_iterator*>(wcookie);
|
||||||
|
|
||||||
|
if (it->move_to_next_tri(tri_id)) {
|
||||||
|
const Handle_StlMesh_MeshTriangle& occTri = it->domain_tri(tri_id);
|
||||||
|
int idV1, idV2, idV3;
|
||||||
|
double xN, yN, zN;
|
||||||
occTri->GetVertexAndOrientation(idV1, idV2, idV3, xN, yN, zN);
|
occTri->GetVertexAndOrientation(idV1, idV2, idV3, xN, yN, zN);
|
||||||
gmio_stl_coords& n = tri->n;
|
gmio_stl_coords& n = tri->n;
|
||||||
n.x = static_cast<float>(xN);
|
n.x = static_cast<float>(xN);
|
||||||
n.y = static_cast<float>(yN);
|
n.y = static_cast<float>(yN);
|
||||||
n.z = static_cast<float>(zN);
|
n.z = static_cast<float>(zN);
|
||||||
|
|
||||||
const TColgp_SequenceOfXYZ& vertices = *mesh_domain->vertices();
|
const TColgp_SequenceOfXYZ& vertices = it->domain_vertices();
|
||||||
const gp_XYZ& coordsV1 = vertices.Value(idV1);
|
gmio_stl_occ_copy_xyz(&tri->v1, vertices.Value(idV1));
|
||||||
gmio_stl_coords& v1 = tri->v1;
|
gmio_stl_occ_copy_xyz(&tri->v2, vertices.Value(idV2));
|
||||||
v1.x = static_cast<float>(coordsV1.X());
|
gmio_stl_occ_copy_xyz(&tri->v3, vertices.Value(idV3));
|
||||||
v1.y = static_cast<float>(coordsV1.Y());
|
}
|
||||||
v1.z = static_cast<float>(coordsV1.Z());
|
|
||||||
|
|
||||||
const gp_XYZ& coordsV2 = vertices.Value(idV2);
|
|
||||||
gmio_stl_coords& v2 = tri->v2;
|
|
||||||
v2.x = static_cast<float>(coordsV2.X());
|
|
||||||
v2.y = static_cast<float>(coordsV2.Y());
|
|
||||||
v2.z = static_cast<float>(coordsV2.Z());
|
|
||||||
|
|
||||||
const gp_XYZ& coordsV3 = vertices.Value(idV3);
|
|
||||||
gmio_stl_coords& v3 = tri->v3;
|
|
||||||
v3.x = static_cast<float>(coordsV3.X());
|
|
||||||
v3.y = static_cast<float>(coordsV3.Y());
|
|
||||||
v3.z = static_cast<float>(coordsV3.Z());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
struct gmio_stl_mesh gmio_stl_occmesh(const struct gmio_occ_stl_mesh_domain* mesh_domain)
|
gmio_stl_mesh gmio_stl_occmesh(const gmio_stl_occmesh_iterator& it)
|
||||||
{
|
{
|
||||||
struct gmio_stl_mesh mesh = {0};
|
gmio_stl_mesh mesh = {};
|
||||||
mesh.cookie = mesh_domain;
|
mesh.cookie = ⁢
|
||||||
if (mesh_domain != NULL && mesh_domain->mesh() != NULL) {
|
const int domain_count = it.mesh()->NbDomains();
|
||||||
mesh.triangle_count =
|
for (int dom_id = 1; dom_id <= domain_count; ++dom_id)
|
||||||
mesh_domain->mesh()->NbTriangles(mesh_domain->domain_id());
|
mesh.triangle_count += it.mesh()->NbTriangles(dom_id);
|
||||||
}
|
|
||||||
mesh.func_get_triangle = internal::occmesh_get_triangle;
|
mesh.func_get_triangle = internal::occmesh_get_triangle;
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gmio_stl_mesh_creator gmio_stl_occmesh_creator(StlMesh_Mesh* mesh)
|
gmio_stl_mesh_creator gmio_stl_occmesh_creator(StlMesh_Mesh* mesh)
|
||||||
{
|
{
|
||||||
struct gmio_stl_mesh_creator creator = {0};
|
gmio_stl_mesh_creator creator = {};
|
||||||
creator.cookie = mesh;
|
creator.cookie = mesh;
|
||||||
creator.func_add_triangle = internal::occmesh_add_triangle;
|
creator.func_add_triangle = internal::occmesh_add_triangle;
|
||||||
return creator;
|
return creator;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gmio_stl_mesh_creator gmio_stl_hnd_occmesh_creator(const Handle_StlMesh_Mesh &hnd)
|
gmio_stl_mesh_creator gmio_stl_occmesh_creator(const Handle_StlMesh_Mesh &hnd)
|
||||||
{
|
{
|
||||||
return gmio_stl_occmesh_creator(internal::occMeshPtr(hnd));
|
return gmio_stl_occmesh_creator(internal::occMeshPtr(hnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
gmio_occ_stl_mesh_domain::gmio_occ_stl_mesh_domain()
|
gmio_stl_occmesh_iterator::gmio_stl_occmesh_iterator()
|
||||||
: m_mesh(NULL),
|
|
||||||
m_domain_id(0),
|
|
||||||
m_triangles(NULL),
|
|
||||||
m_vertices(NULL)
|
|
||||||
{
|
{
|
||||||
|
this->init(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
gmio_occ_stl_mesh_domain::gmio_occ_stl_mesh_domain(
|
gmio_stl_occmesh_iterator::gmio_stl_occmesh_iterator(const StlMesh_Mesh *mesh)
|
||||||
const StlMesh_Mesh *msh, int dom_id)
|
|
||||||
: m_mesh(msh),
|
|
||||||
m_domain_id(dom_id),
|
|
||||||
m_triangles(&msh->Triangles(dom_id)),
|
|
||||||
m_vertices(&msh->Vertices(dom_id))
|
|
||||||
{
|
{
|
||||||
|
this->init(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
gmio_occ_stl_mesh_domain::gmio_occ_stl_mesh_domain(
|
gmio_stl_occmesh_iterator::gmio_stl_occmesh_iterator(const Handle_StlMesh_Mesh &hnd)
|
||||||
const Handle_StlMesh_Mesh &hnd, int dom_id)
|
|
||||||
: m_mesh(internal::occMeshPtr(hnd)),
|
|
||||||
m_domain_id(dom_id),
|
|
||||||
m_triangles(&m_mesh->Triangles(dom_id)),
|
|
||||||
m_vertices(&m_mesh->Vertices(dom_id))
|
|
||||||
{
|
{
|
||||||
|
this->init(internal::occMeshPtr(hnd));
|
||||||
|
}
|
||||||
|
|
||||||
|
void gmio_stl_occmesh_iterator::init(const StlMesh_Mesh* mesh)
|
||||||
|
{
|
||||||
|
m_mesh = mesh;
|
||||||
|
m_domain_id = 0;
|
||||||
|
m_domain_count = m_mesh != NULL ? m_mesh->NbDomains() : 0;
|
||||||
|
m_domain_triangles = NULL;
|
||||||
|
m_domain_vertices = NULL;
|
||||||
|
m_domain_first_tri_id = 0;
|
||||||
|
m_domain_last_tri_id = 0;
|
||||||
|
if (m_domain_count > 0)
|
||||||
|
this->cache_domain(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gmio_stl_occmesh_iterator::cache_domain(int dom_id)
|
||||||
|
{
|
||||||
|
m_domain_id = dom_id;
|
||||||
|
m_domain_triangles = &m_mesh->Triangles(dom_id);
|
||||||
|
m_domain_vertices = &m_mesh->Vertices(dom_id);
|
||||||
|
const int dom_tricnt = m_domain_triangles->Length();
|
||||||
|
m_domain_first_tri_id =
|
||||||
|
dom_tricnt > 0 ? m_domain_last_tri_id : m_domain_first_tri_id;
|
||||||
|
m_domain_last_tri_id +=
|
||||||
|
dom_tricnt > 0 ? dom_tricnt - 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gmio_stl_occmesh_iterator::move_to_next_tri(uint32_t tri_id)
|
||||||
|
{
|
||||||
|
if (tri_id > m_domain_last_tri_id) {
|
||||||
|
if (m_domain_id < m_domain_count) {
|
||||||
|
++m_domain_id;
|
||||||
|
this->cache_domain(m_domain_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -31,39 +31,70 @@
|
|||||||
#include "../gmio_stl/stl_mesh.h"
|
#include "../gmio_stl/stl_mesh.h"
|
||||||
#include "../gmio_stl/stl_mesh_creator.h"
|
#include "../gmio_stl/stl_mesh_creator.h"
|
||||||
|
|
||||||
|
#include <StlMesh_SequenceOfMeshTriangle.hxx>
|
||||||
|
|
||||||
class Handle_StlMesh_Mesh;
|
class Handle_StlMesh_Mesh;
|
||||||
class StlMesh_Mesh;
|
class StlMesh_Mesh;
|
||||||
class StlMesh_SequenceOfMeshTriangle;
|
class Handle_StlMesh_MeshTriangle;
|
||||||
class TColgp_SequenceOfXYZ;
|
class TColgp_SequenceOfXYZ;
|
||||||
|
|
||||||
/*! Domain in a OpenCascade \c StlMesh_Mesh object
|
/*! Forward iterator over the triangles of OpenCascade's StlMesh_Mesh
|
||||||
*
|
*
|
||||||
* The domain is indicated with its index within the STL mesh
|
* It is used to iterate efficiently over the triangles of all domains within
|
||||||
|
* a StlMesh_Mesh object.
|
||||||
|
*
|
||||||
|
* You don't have to use API of this class, it's intended to gmio_stl_mesh()
|
||||||
*/
|
*/
|
||||||
struct GMIO_LIBSUPPORT_EXPORT gmio_occ_stl_mesh_domain
|
struct GMIO_LIBSUPPORT_EXPORT gmio_stl_occmesh_iterator
|
||||||
{
|
{
|
||||||
gmio_occ_stl_mesh_domain();
|
gmio_stl_occmesh_iterator();
|
||||||
gmio_occ_stl_mesh_domain(const StlMesh_Mesh* mesh, int dom_id = 1);
|
gmio_stl_occmesh_iterator(const StlMesh_Mesh* mesh);
|
||||||
gmio_occ_stl_mesh_domain(const Handle_StlMesh_Mesh& hnd, int dom_id = 1);
|
gmio_stl_occmesh_iterator(const Handle_StlMesh_Mesh& hnd);
|
||||||
|
|
||||||
|
bool move_to_next_tri(uint32_t tri_id);
|
||||||
|
inline const Handle_StlMesh_MeshTriangle& domain_tri(uint32_t tri_id) const;
|
||||||
|
inline const TColgp_SequenceOfXYZ& domain_vertices() const;
|
||||||
inline const StlMesh_Mesh* mesh() const;
|
inline const StlMesh_Mesh* mesh() const;
|
||||||
inline int domain_id() const;
|
|
||||||
inline const StlMesh_SequenceOfMeshTriangle* triangles() const;
|
|
||||||
inline const TColgp_SequenceOfXYZ* vertices() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void init(const StlMesh_Mesh* mesh);
|
||||||
|
void cache_domain(int dom_id);
|
||||||
|
|
||||||
const StlMesh_Mesh* m_mesh;
|
const StlMesh_Mesh* m_mesh;
|
||||||
|
int m_domain_count;
|
||||||
int m_domain_id;
|
int m_domain_id;
|
||||||
const StlMesh_SequenceOfMeshTriangle* m_triangles;
|
const StlMesh_SequenceOfMeshTriangle* m_domain_triangles;
|
||||||
const TColgp_SequenceOfXYZ* m_vertices;
|
const TColgp_SequenceOfXYZ* m_domain_vertices;
|
||||||
|
uint32_t m_domain_first_tri_id;
|
||||||
|
uint32_t m_domain_last_tri_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Returns a gmio_stl_mesh mapped to domain in StlMesh_Mesh
|
/*! Returns a gmio_stl_mesh mapped to the OCC mesh in iterator \p it
|
||||||
*
|
*
|
||||||
* The mesh's cookie will point to \p mesh_domain
|
* The mesh's cookie will point to \c &it so the lifescope of the corresponding
|
||||||
|
* object must be at least as longer as the returned gmio_stl_mesh.
|
||||||
|
* Example of use:
|
||||||
|
* \code
|
||||||
|
* Handle_StlMesh_Mesh occmesh = ...;
|
||||||
|
* gmio_stl_write_file(
|
||||||
|
* stl_format,
|
||||||
|
* filepath,
|
||||||
|
* gmio_stl_occmesh(occmesh), // Implicit temporary iterator
|
||||||
|
* &options);
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* Dangerous use:
|
||||||
|
* \code
|
||||||
|
* Handle_StlMesh_Mesh occmesh = ...;
|
||||||
|
* const gmio_stl_mesh mesh =
|
||||||
|
* gmio_stl_occmesh(gmio_stl_occmesh_iterator(occmesh));
|
||||||
|
* // At this point the iterator object is destroyed, mesh.cookie points to
|
||||||
|
* // some garbage. The following line may cause a crash.
|
||||||
|
* gmio_stl_write_file(stl_format, filepath, mesh, &options);
|
||||||
|
* \endcode
|
||||||
*/
|
*/
|
||||||
GMIO_LIBSUPPORT_EXPORT
|
GMIO_LIBSUPPORT_EXPORT
|
||||||
struct gmio_stl_mesh gmio_stl_occmesh(const struct gmio_occ_stl_mesh_domain* mesh_domain);
|
gmio_stl_mesh gmio_stl_occmesh(const gmio_stl_occmesh_iterator& it);
|
||||||
|
|
||||||
/*! Returns a gmio_stl_mesh_creator that will build a new domain in a
|
/*! Returns a gmio_stl_mesh_creator that will build a new domain in a
|
||||||
* StlMesh_Mesh object
|
* StlMesh_Mesh object
|
||||||
@ -71,7 +102,7 @@ struct gmio_stl_mesh gmio_stl_occmesh(const struct gmio_occ_stl_mesh_domain* mes
|
|||||||
* The creator's cookie will point \p mesh
|
* The creator's cookie will point \p mesh
|
||||||
*/
|
*/
|
||||||
GMIO_LIBSUPPORT_EXPORT
|
GMIO_LIBSUPPORT_EXPORT
|
||||||
struct gmio_stl_mesh_creator gmio_stl_occmesh_creator(StlMesh_Mesh* mesh);
|
gmio_stl_mesh_creator gmio_stl_occmesh_creator(StlMesh_Mesh* mesh);
|
||||||
|
|
||||||
/*! Same as gmio_stl_occmesh_creator(StlMesh_Mesh*) but takes a handle
|
/*! Same as gmio_stl_occmesh_creator(StlMesh_Mesh*) but takes a handle
|
||||||
*
|
*
|
||||||
@ -79,25 +110,26 @@ struct gmio_stl_mesh_creator gmio_stl_occmesh_creator(StlMesh_Mesh* mesh);
|
|||||||
* handle \p hnd
|
* handle \p hnd
|
||||||
*/
|
*/
|
||||||
GMIO_LIBSUPPORT_EXPORT
|
GMIO_LIBSUPPORT_EXPORT
|
||||||
struct gmio_stl_mesh_creator gmio_stl_hnd_occmesh_creator(const Handle_StlMesh_Mesh& hnd);
|
gmio_stl_mesh_creator gmio_stl_occmesh_creator(const Handle_StlMesh_Mesh& hnd);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --
|
/*
|
||||||
// -- Implementation
|
* Implementation
|
||||||
// --
|
*/
|
||||||
|
|
||||||
const StlMesh_Mesh* gmio_occ_stl_mesh_domain::mesh() const
|
const Handle_StlMesh_MeshTriangle&
|
||||||
|
gmio_stl_occmesh_iterator::domain_tri(uint32_t tri_id) const
|
||||||
|
{
|
||||||
|
const int dom_tri_id = tri_id - m_domain_first_tri_id + 1;
|
||||||
|
return m_domain_triangles->Value(dom_tri_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TColgp_SequenceOfXYZ &gmio_stl_occmesh_iterator::domain_vertices() const
|
||||||
|
{ return *m_domain_vertices; }
|
||||||
|
|
||||||
|
const StlMesh_Mesh *gmio_stl_occmesh_iterator::mesh() const
|
||||||
{ return m_mesh; }
|
{ return m_mesh; }
|
||||||
|
|
||||||
int gmio_occ_stl_mesh_domain::domain_id() const
|
|
||||||
{ return m_domain_id; }
|
|
||||||
|
|
||||||
const StlMesh_SequenceOfMeshTriangle* gmio_occ_stl_mesh_domain::triangles() const
|
|
||||||
{ return m_triangles; }
|
|
||||||
|
|
||||||
const TColgp_SequenceOfXYZ* gmio_occ_stl_mesh_domain::vertices() const
|
|
||||||
{ return m_vertices; }
|
|
||||||
|
|
||||||
#endif /* GMIO_SUPPORT_STL_OCC_H */
|
#endif /* GMIO_SUPPORT_STL_OCC_H */
|
||||||
/*! @} */
|
/*! @} */
|
||||||
|
@ -11,7 +11,7 @@ int main()
|
|||||||
{
|
{
|
||||||
// OpenCascade
|
// OpenCascade
|
||||||
Handle_StlMesh_Mesh stlMesh;
|
Handle_StlMesh_Mesh stlMesh;
|
||||||
gmio_stl_hnd_occmesh_creator(stlMesh);
|
gmio_stl_occmesh_creator(stlMesh);
|
||||||
|
|
||||||
// Qt
|
// Qt
|
||||||
QFile file;
|
QFile file;
|
||||||
|
@ -23,7 +23,10 @@ public:
|
|||||||
const double /*X*/, const double /*Y*/, const double /*Z*/)
|
const double /*X*/, const double /*Y*/, const double /*Z*/)
|
||||||
{ return -1; }
|
{ return -1; }
|
||||||
|
|
||||||
virtual int NbTriangles(const int DomainIndex) const
|
virtual int NbTriangles(const int /*DomainIndex*/) const
|
||||||
|
{ return 0; }
|
||||||
|
|
||||||
|
virtual int NbDomains() const
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
|
|
||||||
virtual const StlMesh_SequenceOfMeshTriangle& Triangles(
|
virtual const StlMesh_SequenceOfMeshTriangle& Triangles(
|
||||||
|
@ -11,6 +11,9 @@ public:
|
|||||||
static Handle_StlMesh_MeshTriangle meshTri;
|
static Handle_StlMesh_MeshTriangle meshTri;
|
||||||
return meshTri;
|
return meshTri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Length() const
|
||||||
|
{ return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _StlMesh_SequenceOfMeshTriangle_HeaderFile
|
#endif // _StlMesh_SequenceOfMeshTriangle_HeaderFile
|
||||||
|
Loading…
Reference in New Issue
Block a user