mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-10 08:17:49 -06:00
Working quite nicely
This commit is contained in:
parent
47e204038f
commit
c6d56b60f6
5 changed files with 108 additions and 134 deletions
|
@ -57,6 +57,7 @@ class LayerDataBuilder(MeshBuilder):
|
||||||
|
|
||||||
vertices = numpy.empty((vertex_count, 3), numpy.float32)
|
vertices = numpy.empty((vertex_count, 3), numpy.float32)
|
||||||
normals = numpy.empty((vertex_count, 3), numpy.float32)
|
normals = numpy.empty((vertex_count, 3), numpy.float32)
|
||||||
|
# line_widths = numpy.empty((vertex_count, 3), numpy.float32) # strictly taken you need 1 less
|
||||||
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
||||||
indices = numpy.empty((index_count, 2), numpy.int32)
|
indices = numpy.empty((index_count, 2), numpy.int32)
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,6 @@ class ProcessSlicedLayersJob(Job):
|
||||||
min_layer_number = layer.id
|
min_layer_number = layer.id
|
||||||
|
|
||||||
current_layer = 0
|
current_layer = 0
|
||||||
all_normals = []
|
|
||||||
|
|
||||||
for layer in self._layers:
|
for layer in self._layers:
|
||||||
abs_layer_number = layer.id + abs(min_layer_number)
|
abs_layer_number = layer.id + abs(min_layer_number)
|
||||||
|
@ -128,12 +127,6 @@ class ProcessSlicedLayersJob(Job):
|
||||||
this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths)
|
this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths)
|
||||||
this_poly.buildCache()
|
this_poly.buildCache()
|
||||||
|
|
||||||
normals = this_poly.getNormals()
|
|
||||||
# normals = this_poly.getNormals()[numpy.where(numpy.logical_not(this_poly.jumpMask))]
|
|
||||||
# all_normals.append(normals)
|
|
||||||
# insert last element twice - fake converting line normals to vertex normals
|
|
||||||
# all_normals.append(normals[-1:])
|
|
||||||
|
|
||||||
this_layer.polygons.append(this_poly)
|
this_layer.polygons.append(this_poly)
|
||||||
|
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
@ -150,26 +143,8 @@ class ProcessSlicedLayersJob(Job):
|
||||||
if self._progress:
|
if self._progress:
|
||||||
self._progress.setProgress(progress)
|
self._progress.setProgress(progress)
|
||||||
|
|
||||||
# layer_data.calculateNormals()
|
|
||||||
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
||||||
# layer_data._normals = numpy.concatenate(all_normals)
|
|
||||||
layer_mesh = layer_data.build()
|
layer_mesh = layer_data.build()
|
||||||
# normals = []
|
|
||||||
# # quick and dirty normals calculation for 2d lines
|
|
||||||
# for line_idx in range(len(layer_mesh._indices) // 2):
|
|
||||||
# idx0 = layer_mesh._indices[line_idx]
|
|
||||||
# idx1 = layer_mesh._indices[line_idx + 1]
|
|
||||||
# x0 = layer_mesh._vertices[idx0][0]
|
|
||||||
# y0 = layer_mesh._vertices[idx0][2]
|
|
||||||
# x1 = layer_mesh._vertices[idx1][0]
|
|
||||||
# y1 = layer_mesh._vertices[idx1][2]
|
|
||||||
# dx = x1 - x0;
|
|
||||||
# dy = y1 - y0;
|
|
||||||
# normals.append([dy, 0, -dx])
|
|
||||||
# normals.append([dy, 0, -dx])
|
|
||||||
# layer_mesh._normals = numpy.array(normals)
|
|
||||||
#from UM.Mesh.MeshData import calculateNormalsFromIndexedVertices
|
|
||||||
#layer_mesh._normals = calculateNormalsFromIndexedVertices(layer_mesh._vertices, layer_mesh._indices, layer_mesh._face_count)
|
|
||||||
|
|
||||||
if self._abort_requested:
|
if self._abort_requested:
|
||||||
if self._progress:
|
if self._progress:
|
||||||
|
|
|
@ -54,12 +54,12 @@ class LayerPass(RenderPass):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Render all layers below a certain number as line mesh instead of vertices.
|
# Render all layers below a certain number as line mesh instead of vertices.
|
||||||
if self._layerview._current_layer_num - self._layerview._solid_layers > -1 and not self._layerview._only_show_top_layers:
|
if self._layerview._current_layer_num > -1 and not self._layerview._only_show_top_layers:
|
||||||
start = 0
|
start = 0
|
||||||
end = 0
|
end = 0
|
||||||
element_counts = layer_data.getElementCounts()
|
element_counts = layer_data.getElementCounts()
|
||||||
for layer, counts in element_counts.items():
|
for layer, counts in element_counts.items():
|
||||||
if layer + self._layerview._solid_layers > self._layerview._current_layer_num:
|
if layer > self._layerview._current_layer_num:
|
||||||
break
|
break
|
||||||
end += counts
|
end += counts
|
||||||
|
|
||||||
|
|
|
@ -236,9 +236,9 @@ class LayerView(View):
|
||||||
|
|
||||||
self.setBusy(True)
|
self.setBusy(True)
|
||||||
|
|
||||||
#self._top_layers_job = _CreateTopLayersJob(self._controller.getScene(), self._current_layer_num, self._solid_layers)
|
self._top_layers_job = _CreateTopLayersJob(self._controller.getScene(), self._current_layer_num, self._solid_layers)
|
||||||
#self._top_layers_job.finished.connect(self._updateCurrentLayerMesh)
|
self._top_layers_job.finished.connect(self._updateCurrentLayerMesh)
|
||||||
#self._top_layers_job.start()
|
self._top_layers_job.start()
|
||||||
|
|
||||||
def _updateCurrentLayerMesh(self, job):
|
def _updateCurrentLayerMesh(self, job):
|
||||||
self.setBusy(False)
|
self.setBusy(False)
|
||||||
|
|
|
@ -16,8 +16,6 @@ vertex =
|
||||||
varying highp vec3 v_vertex;
|
varying highp vec3 v_vertex;
|
||||||
varying highp vec3 v_normal;
|
varying highp vec3 v_normal;
|
||||||
|
|
||||||
varying highp vec4 v_orig_vertex;
|
|
||||||
|
|
||||||
varying lowp vec4 f_color;
|
varying lowp vec4 f_color;
|
||||||
varying highp vec3 f_vertex;
|
varying highp vec3 f_vertex;
|
||||||
varying highp vec3 f_normal;
|
varying highp vec3 f_normal;
|
||||||
|
@ -29,14 +27,12 @@ vertex =
|
||||||
gl_Position = world_space_vert;
|
gl_Position = world_space_vert;
|
||||||
// gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
// gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||||
// shade the color depending on the extruder index stored in the alpha component of the color
|
// shade the color depending on the extruder index stored in the alpha component of the color
|
||||||
v_color = (a_color.a == u_active_extruder) ? a_color : a_color * u_shade_factor;
|
v_color = (a_color.a == u_active_extruder) ? a_color : vec4(0.4, 0.4, 0.4, 1.0); //a_color * u_shade_factor;
|
||||||
v_color.a = 1.0;
|
v_color.a = 1.0;
|
||||||
|
|
||||||
v_vertex = world_space_vert.xyz;
|
v_vertex = world_space_vert.xyz;
|
||||||
v_normal = (u_normalMatrix * normalize(a_normal)).xyz;
|
v_normal = (u_normalMatrix * normalize(a_normal)).xyz;
|
||||||
|
|
||||||
v_orig_vertex = a_vertex;
|
|
||||||
|
|
||||||
// for testing without geometry shader
|
// for testing without geometry shader
|
||||||
f_color = v_color;
|
f_color = v_color;
|
||||||
f_vertex = v_vertex;
|
f_vertex = v_vertex;
|
||||||
|
@ -51,7 +47,7 @@ geometry =
|
||||||
//uniform highp mat4 u_modelViewProjectionMatrix;
|
//uniform highp mat4 u_modelViewProjectionMatrix;
|
||||||
|
|
||||||
layout(lines) in;
|
layout(lines) in;
|
||||||
layout(triangle_strip, max_vertices = 8) out;
|
layout(triangle_strip, max_vertices = 28) out;
|
||||||
/*layout(std140) uniform Matrices {
|
/*layout(std140) uniform Matrices {
|
||||||
mat4 u_modelViewProjectionMatrix;
|
mat4 u_modelViewProjectionMatrix;
|
||||||
};*/
|
};*/
|
||||||
|
@ -59,7 +55,6 @@ geometry =
|
||||||
in vec4 v_color[];
|
in vec4 v_color[];
|
||||||
in vec3 v_vertex[];
|
in vec3 v_vertex[];
|
||||||
in vec3 v_normal[];
|
in vec3 v_normal[];
|
||||||
in vec3 v_orig_vertex[];
|
|
||||||
|
|
||||||
out vec4 f_color;
|
out vec4 f_color;
|
||||||
out vec3 f_normal;
|
out vec3 f_normal;
|
||||||
|
@ -71,20 +66,28 @@ geometry =
|
||||||
//vec3 g_normal;
|
//vec3 g_normal;
|
||||||
//vec3 g_offset;
|
//vec3 g_offset;
|
||||||
|
|
||||||
//vec4 g_vertex_delta;
|
vec4 g_vertex_delta;
|
||||||
vec3 g_vertex_normal_horz; // horizontal and vertical in respect to layers
|
vec3 g_vertex_normal_horz; // horizontal and vertical in respect to layers
|
||||||
vec4 g_vertex_offset_horz; // vec4 to match gl_in[x].gl_Position
|
vec4 g_vertex_offset_horz; // vec4 to match gl_in[x].gl_Position
|
||||||
vec3 g_vertex_normal_vert;
|
vec3 g_vertex_normal_vert;
|
||||||
vec4 g_vertex_offset_vert;
|
vec4 g_vertex_offset_vert;
|
||||||
|
vec3 g_vertex_normal_horz_head;
|
||||||
|
vec4 g_vertex_offset_horz_head;
|
||||||
|
|
||||||
const float size = 0.5;
|
const float size_x = 0.2;
|
||||||
|
const float size_y = 0.1;
|
||||||
|
|
||||||
//g_vertex_delta = gl_in[1].gl_Position - gl_in[0].gl_Position;
|
//g_vertex_normal_horz = normalize(v_normal[0]); //vec3(g_vertex_delta.z, g_vertex_delta.y, -g_vertex_delta.x);
|
||||||
g_vertex_normal_horz = normalize(v_normal[0]); //vec3(g_vertex_delta.z, g_vertex_delta.y, -g_vertex_delta.x);
|
g_vertex_delta = gl_in[1].gl_Position - gl_in[0].gl_Position;
|
||||||
g_vertex_offset_horz = vec4(g_vertex_normal_horz * size, 0.0); //size * g_vertex_normal_horz;
|
g_vertex_normal_horz_head = normalize(vec3(-g_vertex_delta.x, -g_vertex_delta.y, -g_vertex_delta.z));
|
||||||
|
g_vertex_offset_horz_head = vec4(g_vertex_normal_horz_head * size_x, 0.0);
|
||||||
|
|
||||||
|
g_vertex_normal_horz = normalize(vec3(g_vertex_delta.z, g_vertex_delta.y, -g_vertex_delta.x));
|
||||||
|
|
||||||
|
g_vertex_offset_horz = vec4(g_vertex_normal_horz * size_x, 0.0); //size * g_vertex_normal_horz;
|
||||||
g_vertex_normal_vert = vec3(0.0, 1.0, 0.0);
|
g_vertex_normal_vert = vec3(0.0, 1.0, 0.0);
|
||||||
//g_vertex_offset_vert = vec3(g_vertex_normal_vert.x * 0.5f, g_vertex_normal_vert.y * 0.5f, g_vertex_normal_vert.z * 0.5f); //size * g_vertex_normal_vert;
|
//g_vertex_offset_vert = vec3(g_vertex_normal_vert.x * 0.5f, g_vertex_normal_vert.y * 0.5f, g_vertex_normal_vert.z * 0.5f); //size * g_vertex_normal_vert;
|
||||||
g_vertex_offset_vert = vec4(g_vertex_normal_vert * size, 0.0);
|
g_vertex_offset_vert = vec4(g_vertex_normal_vert * size_y, 0.0);
|
||||||
|
|
||||||
f_vertex = v_vertex[0];
|
f_vertex = v_vertex[0];
|
||||||
f_normal = g_vertex_normal_horz;
|
f_normal = g_vertex_normal_horz;
|
||||||
|
@ -134,108 +137,101 @@ geometry =
|
||||||
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert);
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
|
|
||||||
EndPrimitive();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
poep =
|
|
||||||
#version 410
|
|
||||||
|
|
||||||
uniform highp mat4 u_modelMatrix;
|
|
||||||
uniform highp mat4 u_viewProjectionMatrix;
|
|
||||||
uniform highp mat4 u_modelViewProjectionMatrix;
|
|
||||||
|
|
||||||
layout(lines) in;
|
|
||||||
layout(triangle_strip, max_vertices = 3) out;
|
|
||||||
|
|
||||||
in vec4 v_color[];
|
|
||||||
in vec3 v_vertex[];
|
|
||||||
in vec3 v_normal[];
|
|
||||||
in vec4 v_orig_vertex[];
|
|
||||||
|
|
||||||
out vec4 f_color;
|
|
||||||
out vec3 f_normal;
|
|
||||||
out vec3 f_vertex;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
vec4 delta;
|
|
||||||
vec3 g_normal;
|
|
||||||
vec3 g_offset;
|
|
||||||
|
|
||||||
vec4 g_vertex_delta;
|
|
||||||
vec4 g_vertex_normal_horz; // horizontal and vertical in respect to layers
|
|
||||||
vec3 g_vertex_normal_vert;
|
|
||||||
vec3 g_vertex_offset_horz;
|
|
||||||
vec3 g_vertex_offset_vert;
|
|
||||||
|
|
||||||
float size = 3;
|
|
||||||
|
|
||||||
g_vertex_delta = v_orig_vertex[1] - v_orig_vertex[0];
|
|
||||||
g_vertex_normal_horz = vec4(g_vertex_delta.z, 0.0, -g_vertex_delta.x, g_vertex_delta.w);
|
|
||||||
if (length(g_vertex_normal_horz) < 0.1) {
|
|
||||||
g_vertex_normal_horz = vec4(1.0, 0.0, 0.0, 0.0);
|
|
||||||
g_vertex_offset_horz = vec3(0.0, 0.0, 0.0);
|
|
||||||
g_vertex_offset_vert = vec3(0.0, 0.0, 0.0);
|
|
||||||
} else {
|
|
||||||
g_vertex_normal_horz = normalize(g_vertex_normal_horz);
|
|
||||||
g_vertex_offset_horz = (u_viewProjectionMatrix * u_modelMatrix * size * g_vertex_normal_horz).xyz;
|
|
||||||
g_vertex_normal_vert = vec3(0.0, 0.0, 1.0);
|
|
||||||
g_vertex_offset_vert = (u_viewProjectionMatrix * u_modelMatrix * size * g_vertex_normal_vert).xyz;
|
|
||||||
}
|
|
||||||
|
|
||||||
f_vertex = v_vertex[0];
|
f_vertex = v_vertex[0];
|
||||||
f_color = v_color[0];
|
|
||||||
f_normal = g_vertex_normal_horz;
|
f_normal = g_vertex_normal_horz;
|
||||||
gl_Position = gl_in[0].gl_Position + g_vertex_offset_horz;
|
f_color = v_color[0];
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
|
|
||||||
f_vertex = v_vertex[1];
|
f_vertex = v_vertex[1];
|
||||||
f_color = v_color[1];
|
f_color = v_color[1];
|
||||||
f_normal = g_vertex_normal_horz;
|
f_normal = g_vertex_normal_horz;
|
||||||
gl_Position = gl_in[1].gl_Position + g_vertex_offset_horz;
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
|
|
||||||
f_vertex = v_vertex[0];
|
|
||||||
f_color = v_color[0];
|
|
||||||
f_normal = g_vertex_offset_vert;
|
|
||||||
gl_Position = gl_in[0].gl_Position + g_vertex_offset_vert;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
f_vertex = v_vertex[1];
|
|
||||||
f_color = v_color[1];
|
|
||||||
f_normal = g_vertex_offset_vert;
|
|
||||||
gl_Position = gl_in[1].gl_Position + g_vertex_offset_vert;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
f_vertex = v_vertex[0];
|
|
||||||
f_color = v_color[0];
|
|
||||||
f_normal = -g_vertex_normal_horz;
|
|
||||||
gl_Position = gl_in[0].gl_Position - g_vertex_offset_horz;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
f_vertex = v_vertex[1];
|
|
||||||
f_color = v_color[1];
|
|
||||||
f_normal = -g_vertex_normal_horz;
|
|
||||||
gl_Position = gl_in[1].gl_Position - g_vertex_offset_horz;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
f_vertex = v_vertex[0];
|
|
||||||
f_color = v_color[0];
|
|
||||||
f_normal = -g_vertex_offset_vert;
|
|
||||||
gl_Position = gl_in[0].gl_Position - g_vertex_offset_vert;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
f_vertex = v_vertex[1];
|
|
||||||
f_color = v_color[1];
|
|
||||||
f_normal = -g_vertex_offset_vert;
|
|
||||||
gl_Position = gl_in[1].gl_Position - g_vertex_offset_vert;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
|
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
|
// left side
|
||||||
|
f_vertex = v_vertex[0];
|
||||||
|
f_color = v_color[0];
|
||||||
|
|
||||||
|
f_normal = g_vertex_normal_horz;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = g_vertex_normal_vert;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = g_vertex_normal_horz_head;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = -g_vertex_normal_horz;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
f_normal = -g_vertex_normal_horz;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = -g_vertex_normal_vert;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = g_vertex_normal_horz_head;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = g_vertex_normal_horz;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
// right side
|
||||||
|
f_vertex = v_vertex[1];
|
||||||
|
f_color = v_color[1];
|
||||||
|
|
||||||
|
f_normal = g_vertex_normal_horz;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = g_vertex_normal_vert;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = -g_vertex_normal_horz_head;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = -g_vertex_normal_horz;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
f_normal = -g_vertex_normal_horz;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = -g_vertex_normal_vert;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = -g_vertex_normal_horz_head;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
f_normal = g_vertex_normal_horz;
|
||||||
|
gl_Position = u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -271,6 +267,8 @@ fragment =
|
||||||
|
|
||||||
//gl_FrontFacing = ..
|
//gl_FrontFacing = ..
|
||||||
|
|
||||||
|
//if ((f_normal).z < 0) {discard; }
|
||||||
|
|
||||||
mediump vec4 finalColor = vec4(0.0);
|
mediump vec4 finalColor = vec4(0.0);
|
||||||
|
|
||||||
finalColor += u_ambientColor;
|
finalColor += u_ambientColor;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue