forked from StudioInfinity/dyna3
Sketch a 5-5-4 near miss test assembly
This commit is contained in:
parent
8a0d81d707
commit
8bedb0baf7
1 changed files with 433 additions and 1 deletions
|
@ -1,5 +1,5 @@
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
use std::{f64::consts::{FRAC_1_SQRT_2, PI}, rc::Rc};
|
use std::{f64::consts::{FRAC_1_SQRT_2, PI, SQRT_2}, rc::Rc};
|
||||||
use nalgebra::Vector3;
|
use nalgebra::Vector3;
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
use web_sys::{console, wasm_bindgen::JsValue};
|
use web_sys::{console, wasm_bindgen::JsValue};
|
||||||
|
@ -882,6 +882,436 @@ fn load_irisawa_hexlet(assembly: &Assembly) {
|
||||||
assembly.insert_regulator(Rc::new(outer_moon_tangency));
|
assembly.insert_regulator(Rc::new(outer_moon_tangency));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_554a(assembly: &Assembly) {
|
||||||
|
// create the vertices
|
||||||
|
const COLOR_A: ElementColor = [0.75_f32, 0.00_f32, 0.75_f32];
|
||||||
|
const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32];
|
||||||
|
const COLOR_C: ElementColor = [0.75_f32, 0.50_f32, 0.00_f32];
|
||||||
|
const COLOR_D: ElementColor = [0.90_f32, 0.90_f32, 0.00_f32];
|
||||||
|
const COLOR_E: ElementColor = [0.25_f32, 0.75_f32, 0.00_f32];
|
||||||
|
const COLOR_F: ElementColor = [0.00_f32, 0.50_f32, 0.75_f32];
|
||||||
|
const COLOR_G: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32];
|
||||||
|
let vertices = [
|
||||||
|
Point::new(
|
||||||
|
"a_NE".to_string(),
|
||||||
|
"A-NE".to_string(),
|
||||||
|
COLOR_A,
|
||||||
|
engine::point(0.5, 0.5, 0.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"a_NW".to_string(),
|
||||||
|
"A-NW".to_string(),
|
||||||
|
COLOR_A,
|
||||||
|
engine::point(-0.5, 0.5, 0.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"a_SW".to_string(),
|
||||||
|
"A-SW".to_string(),
|
||||||
|
COLOR_A,
|
||||||
|
engine::point(-0.5, -0.5, 0.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"a_SE".to_string(),
|
||||||
|
"A-SE".to_string(),
|
||||||
|
COLOR_A,
|
||||||
|
engine::point(0.5, -0.5, 0.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"b_NE".to_string(),
|
||||||
|
"B-NE".to_string(),
|
||||||
|
COLOR_B,
|
||||||
|
engine::point(0.7, 0.7, 0.9),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"b_NW".to_string(),
|
||||||
|
"B-NW".to_string(),
|
||||||
|
COLOR_B,
|
||||||
|
engine::point(-0.7, 0.7, 0.9),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"b_SW".to_string(),
|
||||||
|
"B-SW".to_string(),
|
||||||
|
COLOR_B,
|
||||||
|
engine::point(-0.7, -0.7, 0.9),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"b_SE".to_string(),
|
||||||
|
"B-SE".to_string(),
|
||||||
|
COLOR_B,
|
||||||
|
engine::point(0.7, -0.7, 0.9),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"c_N".to_string(),
|
||||||
|
"C-N".to_string(),
|
||||||
|
COLOR_C,
|
||||||
|
engine::point(0.0, 0.8, 1.4),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"c_W".to_string(),
|
||||||
|
"C-W".to_string(),
|
||||||
|
COLOR_C,
|
||||||
|
engine::point(-0.8, 0.0, 1.4),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"c_S".to_string(),
|
||||||
|
"C-S".to_string(),
|
||||||
|
COLOR_C,
|
||||||
|
engine::point(0.0, -0.8, 1.4),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"c_E".to_string(),
|
||||||
|
"C-E".to_string(),
|
||||||
|
COLOR_C,
|
||||||
|
engine::point(0.8, 0.0, 1.4),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"d_NE".to_string(),
|
||||||
|
"D-NE".to_string(),
|
||||||
|
COLOR_D,
|
||||||
|
engine::point(0.1, 0.1, 1.8),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"d_NW".to_string(),
|
||||||
|
"D-NW".to_string(),
|
||||||
|
COLOR_D,
|
||||||
|
engine::point(-0.1, 0.1, 1.8),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"d_SW".to_string(),
|
||||||
|
"D-SW".to_string(),
|
||||||
|
COLOR_D,
|
||||||
|
engine::point(-0.1, -0.1, 1.8),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"d_SE".to_string(),
|
||||||
|
"D-SE".to_string(),
|
||||||
|
COLOR_D,
|
||||||
|
engine::point(0.1, -0.1, 1.8),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"e_N".to_string(),
|
||||||
|
"E-N".to_string(),
|
||||||
|
COLOR_E,
|
||||||
|
engine::point(0.0, 0.7, 2.3),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"e_W".to_string(),
|
||||||
|
"E-W".to_string(),
|
||||||
|
COLOR_E,
|
||||||
|
engine::point(-0.7, 0.0, 2.3),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"e_S".to_string(),
|
||||||
|
"E-S".to_string(),
|
||||||
|
COLOR_E,
|
||||||
|
engine::point(0.0, -0.7, 2.3),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"e_E".to_string(),
|
||||||
|
"E-E".to_string(),
|
||||||
|
COLOR_E,
|
||||||
|
engine::point(0.7, 0.0, 2.3),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"f_NE".to_string(),
|
||||||
|
"F-NE".to_string(),
|
||||||
|
COLOR_F,
|
||||||
|
engine::point(0.2, 0.2, 2.7),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"f_NW".to_string(),
|
||||||
|
"F-NW".to_string(),
|
||||||
|
COLOR_F,
|
||||||
|
engine::point(-0.2, 0.2, 2.7),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"f_SW".to_string(),
|
||||||
|
"F-SW".to_string(),
|
||||||
|
COLOR_F,
|
||||||
|
engine::point(-0.2, -0.2, 2.7),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"f_SE".to_string(),
|
||||||
|
"F-SE".to_string(),
|
||||||
|
COLOR_F,
|
||||||
|
engine::point(0.2, -0.2, 2.7),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"g_NNE".to_string(),
|
||||||
|
"G-NNE".to_string(),
|
||||||
|
COLOR_G,
|
||||||
|
engine::point(0.5, 1.2, 3.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"g_NNW".to_string(),
|
||||||
|
"G-NNW".to_string(),
|
||||||
|
COLOR_G,
|
||||||
|
engine::point(-0.5, 1.2, 3.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"g_WNW".to_string(),
|
||||||
|
"G-WNW".to_string(),
|
||||||
|
COLOR_G,
|
||||||
|
engine::point(-1.2, 0.5, 3.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"g_WSW".to_string(),
|
||||||
|
"G-WSW".to_string(),
|
||||||
|
COLOR_G,
|
||||||
|
engine::point(-1.2, -0.5, 3.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"g_SSW".to_string(),
|
||||||
|
"G-SSW".to_string(),
|
||||||
|
COLOR_G,
|
||||||
|
engine::point(-0.5, -1.2, 3.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"g_SSE".to_string(),
|
||||||
|
"G-SSE".to_string(),
|
||||||
|
COLOR_G,
|
||||||
|
engine::point(0.5, -1.2, 3.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"g_ESE".to_string(),
|
||||||
|
"G-ESE".to_string(),
|
||||||
|
COLOR_G,
|
||||||
|
engine::point(1.2, -0.5, 3.0),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
"g_ENE".to_string(),
|
||||||
|
"G-ENE".to_string(),
|
||||||
|
COLOR_G,
|
||||||
|
engine::point(1.2, 0.5, 3.0),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
for vertex in vertices {
|
||||||
|
let _ = assembly.try_insert_element(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix the distances between adjacent vertices
|
||||||
|
let square_in_octagon = (2.0 + SQRT_2).sqrt();
|
||||||
|
let struts = [
|
||||||
|
(1.0, vec![
|
||||||
|
["a_NE", "a_NW"],
|
||||||
|
["a_NW", "a_SW"],
|
||||||
|
["a_SW", "a_SE"],
|
||||||
|
["a_SE", "a_NE"],
|
||||||
|
["a_NE", "b_NE"],
|
||||||
|
["a_NW", "b_NW"],
|
||||||
|
["a_SW", "b_SW"],
|
||||||
|
["a_SE", "b_SE"],
|
||||||
|
["b_NE", "c_N"],
|
||||||
|
["b_NW", "c_N"],
|
||||||
|
["b_NW", "c_W"],
|
||||||
|
["b_SW", "c_W"],
|
||||||
|
["b_SW", "c_S"],
|
||||||
|
["b_SE", "c_S"],
|
||||||
|
["b_SE", "c_E"],
|
||||||
|
["b_NE", "c_E"],
|
||||||
|
["c_N", "d_NE"],
|
||||||
|
["c_N", "d_NW"],
|
||||||
|
["c_W", "d_NW"],
|
||||||
|
["c_W", "d_SW"],
|
||||||
|
["c_S", "d_SW"],
|
||||||
|
["c_S", "d_SE"],
|
||||||
|
["c_E", "d_SE"],
|
||||||
|
["c_E", "d_NE"],
|
||||||
|
["d_NE", "e_N"],
|
||||||
|
["d_NW", "e_N"],
|
||||||
|
["d_NW", "e_W"],
|
||||||
|
["d_SW", "e_W"],
|
||||||
|
["d_SW", "e_S"],
|
||||||
|
["d_SE", "e_S"],
|
||||||
|
["d_SE", "e_E"],
|
||||||
|
["d_NE", "e_E"],
|
||||||
|
["c_N", "e_N"],
|
||||||
|
["c_W", "e_W"],
|
||||||
|
["c_S", "e_S"],
|
||||||
|
["c_E", "e_E"],
|
||||||
|
["e_N", "f_NE"],
|
||||||
|
["e_N", "f_NW"],
|
||||||
|
["e_W", "f_NW"],
|
||||||
|
["e_W", "f_SW"],
|
||||||
|
["e_S", "f_SW"],
|
||||||
|
["e_S", "f_SE"],
|
||||||
|
["e_E", "f_SE"],
|
||||||
|
["e_E", "f_NE"],
|
||||||
|
["d_NE", "f_NE"],
|
||||||
|
["d_NW", "f_NW"],
|
||||||
|
["d_SW", "f_SW"],
|
||||||
|
["d_SE", "f_SE"],
|
||||||
|
["f_NE", "g_ENE"],
|
||||||
|
["f_NE", "g_NNE"],
|
||||||
|
["f_NW", "g_NNW"],
|
||||||
|
["f_NW", "g_WNW"],
|
||||||
|
["f_SW", "g_WSW"],
|
||||||
|
["f_SW", "g_SSW"],
|
||||||
|
["f_SE", "g_SSE"],
|
||||||
|
["f_SE", "g_ESE"],
|
||||||
|
["e_N", "g_NNE"],
|
||||||
|
["e_N", "g_NNW"],
|
||||||
|
["e_W", "g_WNW"],
|
||||||
|
["e_W", "g_WSW"],
|
||||||
|
["e_S", "g_SSW"],
|
||||||
|
["e_S", "g_SSE"],
|
||||||
|
["e_E", "g_ESE"],
|
||||||
|
["e_E", "g_ENE"],
|
||||||
|
["g_NNE", "g_NNW"],
|
||||||
|
["g_NNW", "g_WNW"],
|
||||||
|
["g_WNW", "g_WSW"],
|
||||||
|
["g_WSW", "g_SSW"],
|
||||||
|
["g_SSW", "g_SSE"],
|
||||||
|
["g_SSE", "g_ESE"],
|
||||||
|
["g_ESE", "g_ENE"],
|
||||||
|
["g_ENE", "g_NNE"],
|
||||||
|
]),
|
||||||
|
(SQRT_2, vec![
|
||||||
|
["a_NE", "a_SW"],
|
||||||
|
["a_NW", "a_SE"],
|
||||||
|
["b_NE", "d_NE"],
|
||||||
|
["b_NW", "d_NW"],
|
||||||
|
["b_SW", "d_SW"],
|
||||||
|
["b_SE", "d_SE"],
|
||||||
|
]),
|
||||||
|
(0.5*(1.0 + 5.0_f64.sqrt()), vec![
|
||||||
|
["a_NE", "c_N"],
|
||||||
|
["a_NW", "c_N"],
|
||||||
|
["a_NW", "c_W"],
|
||||||
|
["a_SW", "c_W"],
|
||||||
|
["a_SW", "c_S"],
|
||||||
|
["a_SE", "c_S"],
|
||||||
|
["a_SE", "c_E"],
|
||||||
|
["a_NE", "c_E"],
|
||||||
|
]),
|
||||||
|
(square_in_octagon, vec![
|
||||||
|
["g_NNE", "g_WNW"],
|
||||||
|
["g_WNW", "g_SSW"],
|
||||||
|
["g_SSW", "g_ESE"],
|
||||||
|
["g_ESE", "g_NNE"],
|
||||||
|
]),
|
||||||
|
(SQRT_2 * square_in_octagon, vec![
|
||||||
|
["g_NNE", "g_SSW"],
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
for (length, vertex_pairs) in struts {
|
||||||
|
let inv_dist = Some(-0.5 * length * length);
|
||||||
|
for pair in vertex_pairs {
|
||||||
|
let adjacent_vertices = pair.map(
|
||||||
|
|id| assembly.elements_by_id.with_untracked(
|
||||||
|
|elts_by_id| elts_by_id[id].clone()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let distance = InversiveDistanceRegulator::new(adjacent_vertices);
|
||||||
|
distance.set_point.set(SpecifiedValue::from(inv_dist));
|
||||||
|
assembly.insert_regulator(Rc::new(distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the faces
|
||||||
|
const COLOR_FACE: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32];
|
||||||
|
let faces = [
|
||||||
|
Sphere::new(
|
||||||
|
"f_a".to_string(),
|
||||||
|
"Face A".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(0.0, 0.0, -1.0, 0.0, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_abc_N".to_string(),
|
||||||
|
"Face ABC-N".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(0.0, 1.0, 0.0, 0.5, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_abc_W".to_string(),
|
||||||
|
"Face ABC-W".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(-1.0, 0.0, 0.0, 0.5, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_abc_S".to_string(),
|
||||||
|
"Face ABC-S".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(0.0, -1.0, 0.0, 0.5, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_abc_E".to_string(),
|
||||||
|
"Face ABC-E".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(1.0, 0.0, 0.0, 0.5, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_bcd_NE".to_string(),
|
||||||
|
"Face BCD-NE".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_bcd_NW".to_string(),
|
||||||
|
"Face BCD-NW".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(-FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_bcd_SW".to_string(),
|
||||||
|
"Face BCD-SW".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_bcd_SE".to_string(),
|
||||||
|
"Face BCD-SE".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"f_g".to_string(),
|
||||||
|
"Face G".to_string(),
|
||||||
|
COLOR_FACE,
|
||||||
|
engine::sphere_with_offset(0.0, 0.0, 1.0, 3.0, 0.0),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
for face in faces {
|
||||||
|
face.ghost().set(true);
|
||||||
|
let _ = assembly.try_insert_element(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make the faces planar and make them pass through their vertices
|
||||||
|
let face_incidences = [
|
||||||
|
("f_a", vec!["a_NE", "a_NW", "a_SW", "a_SE"]),
|
||||||
|
("f_abc_N", vec!["a_NE", "a_NW", "b_NE", "b_NW", "c_N"]),
|
||||||
|
("f_abc_W", vec!["a_NW", "a_SW", "b_NW", "b_SW", "c_W"]),
|
||||||
|
("f_abc_S", vec!["a_SW", "a_SE", "b_SW", "b_SE", "c_S"]),
|
||||||
|
("f_abc_E", vec!["a_SE", "a_NE", "b_SE", "b_NE", "c_E"]),
|
||||||
|
("f_bcd_NE", vec!["b_NE", "c_N", "c_E", "d_NE"]),
|
||||||
|
("f_bcd_NW", vec!["b_NW", "c_N", "c_W", "d_NW"]),
|
||||||
|
("f_bcd_SW", vec!["b_SW", "c_S", "c_W", "d_SW"]),
|
||||||
|
("f_bcd_SE", vec!["b_SE", "c_S", "c_E", "d_SE"]),
|
||||||
|
("f_g", vec!["g_NNE", "g_NNW", "g_WNW", "g_WSW", "g_SSW", "g_SSE", "g_ESE", "g_ENE"]),
|
||||||
|
];
|
||||||
|
for (face_id, vertex_ids) in face_incidences {
|
||||||
|
// make the face planar
|
||||||
|
let face = assembly.elements_by_id.with_untracked(
|
||||||
|
|elts_by_id| elts_by_id[face_id].clone()
|
||||||
|
);
|
||||||
|
let curvature_regulator = face.regulators().with_untracked(
|
||||||
|
|regs| regs.first().unwrap().clone()
|
||||||
|
);
|
||||||
|
curvature_regulator.set_point().set(SpecifiedValue::from(Some(0.0)));
|
||||||
|
|
||||||
|
// make the face pass through its vertices
|
||||||
|
for v_id in vertex_ids {
|
||||||
|
let vertex = assembly.elements_by_id.with_untracked(
|
||||||
|
|elts_by_id| elts_by_id[v_id].clone()
|
||||||
|
);
|
||||||
|
let incidence = InversiveDistanceRegulator::new([face.clone(), vertex]);
|
||||||
|
incidence.set_point.set(SpecifiedValue::from(Some(0.0)));
|
||||||
|
assembly.insert_regulator(Rc::new(incidence));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- chooser ---
|
// --- chooser ---
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
|
@ -918,6 +1348,7 @@ pub fn TestAssemblyChooser() -> View {
|
||||||
"off-center" => load_off_center(assembly),
|
"off-center" => load_off_center(assembly),
|
||||||
"radius-ratio" => load_radius_ratio(assembly),
|
"radius-ratio" => load_radius_ratio(assembly),
|
||||||
"irisawa-hexlet" => load_irisawa_hexlet(assembly),
|
"irisawa-hexlet" => load_irisawa_hexlet(assembly),
|
||||||
|
"554a" => load_554a(assembly),
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -935,6 +1366,7 @@ pub fn TestAssemblyChooser() -> View {
|
||||||
option(value = "off-center") { "Off-center" }
|
option(value = "off-center") { "Off-center" }
|
||||||
option(value = "radius-ratio") { "Radius ratio" }
|
option(value = "radius-ratio") { "Radius ratio" }
|
||||||
option(value = "irisawa-hexlet") { "Irisawa hexlet" }
|
option(value = "irisawa-hexlet") { "Irisawa hexlet" }
|
||||||
|
option(value = "554a") { "5-5-4 near miss A" }
|
||||||
option(value = "empty") { "Empty" }
|
option(value = "empty") { "Empty" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue