mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-04 03:09:45 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
129e1b60d8
3 changed files with 34 additions and 16 deletions
|
@ -1447,6 +1447,8 @@ function bezier_patch_normals(patch, u, v) =
|
||||||
|
|
||||||
|
|
||||||
// Function: bezier_sheet()
|
// Function: bezier_sheet()
|
||||||
|
// Synopsis: Creates a thin sheet from a bezier patch by extruding in normal to the patch
|
||||||
|
// SynTags: VNF
|
||||||
// Topics: Bezier Patches
|
// Topics: Bezier Patches
|
||||||
// See Also: bezier_patch_normals(), vnf_sheet()
|
// See Also: bezier_patch_normals(), vnf_sheet()
|
||||||
// Description:
|
// Description:
|
||||||
|
@ -1469,7 +1471,7 @@ function bezier_patch_normals(patch, u, v) =
|
||||||
// ---
|
// ---
|
||||||
// splinesteps = Number of segments on the border edges of the bezier surface. You can specify [USTEPS,VSTEPS]. Default: 16
|
// splinesteps = Number of segments on the border edges of the bezier surface. You can specify [USTEPS,VSTEPS]. Default: 16
|
||||||
// style = {{vnf_vertex_array()}} style to use. Default: "default"
|
// style = {{vnf_vertex_array()}} style to use. Default: "default"
|
||||||
// Example:
|
// Example(3D):
|
||||||
// patch = [
|
// patch = [
|
||||||
// // u=0,v=0 u=1,v=0
|
// // u=0,v=0 u=1,v=0
|
||||||
// [[-50,-50, 0], [-16,-50, 20], [ 16,-50, -20], [50,-50, 0]],
|
// [[-50,-50, 0], [-16,-50, 20], [ 16,-50, -20], [50,-50, 0]],
|
||||||
|
|
|
@ -814,7 +814,7 @@ function grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero, p=_
|
||||||
// n = Optional number of evenly distributed copies, rotated around the axis.
|
// n = Optional number of evenly distributed copies, rotated around the axis.
|
||||||
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise. Default: 0
|
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise. Default: 0
|
||||||
// delta = [X,Y,Z] amount to move away from cp before rotating. Makes rings of copies. Default: `[0,0,0]`
|
// delta = [X,Y,Z] amount to move away from cp before rotating. Makes rings of copies. Default: `[0,0,0]`
|
||||||
// subrot = If false, don't sub-rotate children as they are copied around the ring. Only makes sense when used with `delta`. Default: `true`
|
// subrot = If false, don't sub-rotate children as they are copied around the ring. Instead maintain their native orientation. The false setting is only allowed when `delta` is given. Default: `true`
|
||||||
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
|
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
|
||||||
//
|
//
|
||||||
// Side Effects:
|
// Side Effects:
|
||||||
|
@ -853,6 +853,7 @@ function grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero, p=_
|
||||||
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
|
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
|
||||||
module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true)
|
module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true)
|
||||||
{
|
{
|
||||||
|
assert(subrot || norm(delta)>0, "subrot can only be false if delta is not zero");
|
||||||
req_children($children);
|
req_children($children);
|
||||||
sang = sa + offset;
|
sang = sa + offset;
|
||||||
angs = !is_undef(n)?
|
angs = !is_undef(n)?
|
||||||
|
@ -866,8 +867,8 @@ module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subr
|
||||||
$axis = v;
|
$axis = v;
|
||||||
translate(cp) {
|
translate(cp) {
|
||||||
rotate(a=$ang, v=v) {
|
rotate(a=$ang, v=v) {
|
||||||
translate(delta) {
|
translate(delta) {
|
||||||
rot(a=(subrot? sang : $ang), v=v, reverse=true) {
|
rot(a=subrot? 0 : $ang, v=v, reverse=true) {
|
||||||
translate(-cp) {
|
translate(-cp) {
|
||||||
children();
|
children();
|
||||||
}
|
}
|
||||||
|
@ -880,6 +881,7 @@ module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subr
|
||||||
|
|
||||||
|
|
||||||
function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true, p=_NO_ARG) =
|
function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true, p=_NO_ARG) =
|
||||||
|
assert(subrot || norm(delta)>0, "subrot can only be false if delta is not zero")
|
||||||
let(
|
let(
|
||||||
sang = sa + offset,
|
sang = sa + offset,
|
||||||
angs = !is_undef(n)?
|
angs = !is_undef(n)?
|
||||||
|
@ -893,7 +895,7 @@ function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], su
|
||||||
translate(cp) *
|
translate(cp) *
|
||||||
rot(a=ang, v=v) *
|
rot(a=ang, v=v) *
|
||||||
translate(delta) *
|
translate(delta) *
|
||||||
rot(a=(subrot? sang : ang), v=v, reverse=true) *
|
rot(a=subrot? 0 : ang, v=v, reverse=true) *
|
||||||
translate(-cp)
|
translate(-cp)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -935,6 +937,7 @@ function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], su
|
||||||
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from Y+, when facing the origin from X+. First unrotated copy is placed at that angle.
|
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from Y+, when facing the origin from X+. First unrotated copy is placed at that angle.
|
||||||
// r = If given, makes a ring of child copies around the X axis, at the given radius. Default: 0
|
// r = If given, makes a ring of child copies around the X axis, at the given radius. Default: 0
|
||||||
// d = If given, makes a ring of child copies around the X axis, at the given diameter.
|
// d = If given, makes a ring of child copies around the X axis, at the given diameter.
|
||||||
|
// subrot = If false, don't sub-rotate children as they are copied around the ring. Instead maintain their native orientation. The false setting is only allowed when `d` or `r` is given. Default: `true`
|
||||||
// subrot = If false, don't sub-rotate children as they are copied around the ring.
|
// subrot = If false, don't sub-rotate children as they are copied around the ring.
|
||||||
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
|
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
|
||||||
//
|
//
|
||||||
|
@ -972,12 +975,16 @@ module xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
|
||||||
{
|
{
|
||||||
req_children($children);
|
req_children($children);
|
||||||
r = get_radius(r=r, d=d, dflt=0);
|
r = get_radius(r=r, d=d, dflt=0);
|
||||||
|
assert(all_nonnegative([r]), "d/r must be nonnegative");
|
||||||
|
assert(subrot || r>0, "subrot can only be false if d or r is given");
|
||||||
rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot) children();
|
rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot) children();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
|
function xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
|
||||||
let( r = get_radius(r=r, d=d, dflt=0) )
|
let( r = get_radius(r=r, d=d, dflt=0) )
|
||||||
|
assert(all_nonnegative([r]), "d/r must be nonnegative")
|
||||||
|
assert(subrot || r>0, "subrot can only be false if d or r is given")
|
||||||
rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot, p=p);
|
rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot, p=p);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1016,7 +1023,7 @@ function xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG)
|
||||||
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from X-, when facing the origin from Y+.
|
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from X-, when facing the origin from Y+.
|
||||||
// r = If given, makes a ring of child copies around the Y axis, at the given radius. Default: 0
|
// r = If given, makes a ring of child copies around the Y axis, at the given radius. Default: 0
|
||||||
// d = If given, makes a ring of child copies around the Y axis, at the given diameter.
|
// d = If given, makes a ring of child copies around the Y axis, at the given diameter.
|
||||||
// subrot = If false, don't sub-rotate children as they are copied around the ring.
|
// subrot = If false, don't sub-rotate children as they are copied around the ring. Instead maintain their native orientation. The false setting is only allowed when `d` or `r` is given. Default: `true`
|
||||||
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
|
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
|
||||||
//
|
//
|
||||||
// Side Effects:
|
// Side Effects:
|
||||||
|
@ -1053,12 +1060,16 @@ module yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
|
||||||
{
|
{
|
||||||
req_children($children);
|
req_children($children);
|
||||||
r = get_radius(r=r, d=d, dflt=0);
|
r = get_radius(r=r, d=d, dflt=0);
|
||||||
|
assert(all_nonnegative([r]), "d/r must be nonnegative");
|
||||||
|
assert(subrot || r>0, "subrot can only be false if d or r is given");
|
||||||
rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot) children();
|
rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot) children();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
|
function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
|
||||||
let( r = get_radius(r=r, d=d, dflt=0) )
|
let( r = get_radius(r=r, d=d, dflt=0) )
|
||||||
|
assert(all_nonnegative([r]), "d/r must be nonnegative")
|
||||||
|
assert(subrot || r>0, "subrot can only be false if d or r is given")
|
||||||
rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot, p=p);
|
rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot, p=p);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1098,7 +1109,7 @@ function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG)
|
||||||
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from X+, when facing the origin from Z+. Default: 0
|
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from X+, when facing the origin from Z+. Default: 0
|
||||||
// r = If given, makes a ring of child copies around the Z axis, at the given radius. Default: 0
|
// r = If given, makes a ring of child copies around the Z axis, at the given radius. Default: 0
|
||||||
// d = If given, makes a ring of child copies around the Z axis, at the given diameter.
|
// d = If given, makes a ring of child copies around the Z axis, at the given diameter.
|
||||||
// subrot = If false, don't sub-rotate children as they are copied around the ring. Default: true
|
// subrot = If false, don't sub-rotate children as they are copied around the ring. Instead maintain their native orientation. The false setting is only allowed when `d` or `r` is given. Default: `true`
|
||||||
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
|
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
|
||||||
//
|
//
|
||||||
// Side Effects:
|
// Side Effects:
|
||||||
|
@ -1133,13 +1144,18 @@ function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG)
|
||||||
// color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
|
// color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
|
||||||
module zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
|
module zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
|
||||||
{
|
{
|
||||||
|
req_children($children);
|
||||||
r = get_radius(r=r, d=d, dflt=0);
|
r = get_radius(r=r, d=d, dflt=0);
|
||||||
|
assert(all_nonnegative([r]), "d/r must be nonnegative");
|
||||||
|
assert(subrot || r>0, "subrot can only be false if d or r is given");
|
||||||
rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot) children();
|
rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot) children();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
|
function zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
|
||||||
let( r = get_radius(r=r, d=d, dflt=0) )
|
let( r = get_radius(r=r, d=d, dflt=0) )
|
||||||
|
assert(all_nonnegative([r]), "d/r must be nonnegative")
|
||||||
|
assert(subrot || r>0, "subrot can only be false if d or r is given")
|
||||||
rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot, p=p);
|
rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot, p=p);
|
||||||
|
|
||||||
|
|
||||||
|
|
18
vnf.scad
18
vnf.scad
|
@ -1655,7 +1655,7 @@ function _sort_pairs0(arr) =
|
||||||
// ---
|
// ---
|
||||||
// merge = set to false to suppress the automatic invocation of {{vnf_merge_points()}}. Default: true
|
// merge = set to false to suppress the automatic invocation of {{vnf_merge_points()}}. Default: true
|
||||||
// idx = if true, return indices into VNF vertices instead of actual 3D points. Must set `merge=false` to enable this. Default: false
|
// idx = if true, return indices into VNF vertices instead of actual 3D points. Must set `merge=false` to enable this. Default: false
|
||||||
// Example(NoAxes,VPT=[7.06325,-20.8414,20.1803],VPD=292.705,VPR=[55,0,25.7]): In this example we know that the bezier patch VNF has no duplicate vertices, so we do not need to run {{vnf_merge_points()}}.
|
// Example(3D,NoAxes,VPT=[7.06325,-20.8414,20.1803],VPD=292.705,VPR=[55,0,25.7]): In this example we know that the bezier patch VNF has no duplicate vertices, so we do not need to run {{vnf_merge_points()}}.
|
||||||
// include <BOSL2/beziers.scad>
|
// include <BOSL2/beziers.scad>
|
||||||
// patch = [
|
// patch = [
|
||||||
// // u=0,v=0 u=1,v=0
|
// // u=0,v=0 u=1,v=0
|
||||||
|
@ -1669,7 +1669,7 @@ function _sort_pairs0(arr) =
|
||||||
// boundary = vnf_boundary(bezvnf);
|
// boundary = vnf_boundary(bezvnf);
|
||||||
// vnf_polyhedron(bezvnf);
|
// vnf_polyhedron(bezvnf);
|
||||||
// stroke(boundary,color="green");
|
// stroke(boundary,color="green");
|
||||||
// Example(NoAxes,VPT=[-11.1252,-19.7333,8.39927],VPD=82.6686,VPR=[71.8,0,335.3]): An example with two path components on the boundary. The output from {{vnf_halfspace()}} can contain duplicate vertices, so we must invoke {{vnf_merge_points()}}.
|
// Example(3D,NoAxes,VPT=[-11.1252,-19.7333,8.39927],VPD=82.6686,VPR=[71.8,0,335.3]): An example with two path components on the boundary. The output from {{vnf_halfspace()}} can contain duplicate vertices, so we must invoke {{vnf_merge_points()}}.
|
||||||
// vnf = torus(id=20,od=40,$fn=28);
|
// vnf = torus(id=20,od=40,$fn=28);
|
||||||
// cutvnf=vnf_halfspace([0,1,0,0],
|
// cutvnf=vnf_halfspace([0,1,0,0],
|
||||||
// vnf_halfspace([-1,.5,-2.5,-12], vnf, closed=false),
|
// vnf_halfspace([-1,.5,-2.5,-12], vnf, closed=false),
|
||||||
|
@ -1721,14 +1721,14 @@ function vnf_boundary(vnf,merge=true,idx=false) =
|
||||||
// delta = distance of offset, positive to offset out, negative to offset in
|
// delta = distance of offset, positive to offset out, negative to offset in
|
||||||
// ---
|
// ---
|
||||||
// merge = set to false to suppress the automatic invocation of {{vnf_merge_points()}}. Default: true
|
// merge = set to false to suppress the automatic invocation of {{vnf_merge_points()}}. Default: true
|
||||||
// Example: The original sphere is on the left and an offset sphere on the right.
|
// Example(3D): The original sphere is on the left and an offset sphere on the right.
|
||||||
// vnf = sphere(d=100);
|
// vnf = sphere(d=100);
|
||||||
// xdistribute(spacing=125){
|
// xdistribute(spacing=125){
|
||||||
// vnf_polyhedron(vnf);
|
// vnf_polyhedron(vnf);
|
||||||
// vnf_polyhedron(vnf_small_offset(vnf,18));
|
// vnf_polyhedron(vnf_small_offset(vnf,18));
|
||||||
// }
|
// }
|
||||||
// Example: The polyhedron on the left is enlarged to match the size of the offset polyhedron on the right. Note that the offset does **not** preserve coplanarity of faces. This is because the vertices all move independently, so nothing constrains faces to remain coplanar.
|
// Example(3D): The polyhedron on the left is enlarged to match the size of the offset polyhedron on the right. Note that the offset does **not** preserve coplanarity of faces. This is because the vertices all move independently, so nothing constrains faces to remain coplanar.
|
||||||
// include <BOSL2-fork/polyhedra.scad>
|
// include <BOSL2/polyhedra.scad>
|
||||||
// vnf = regular_polyhedron_info("vnf","pentagonal icositetrahedron",d=25);
|
// vnf = regular_polyhedron_info("vnf","pentagonal icositetrahedron",d=25);
|
||||||
// xdistribute(spacing=300){
|
// xdistribute(spacing=300){
|
||||||
// scale(11)vnf_polyhedron(vnf);
|
// scale(11)vnf_polyhedron(vnf);
|
||||||
|
@ -1762,7 +1762,7 @@ function vnf_small_offset(vnf, delta, merge=true) =
|
||||||
[offset,faces];
|
[offset,faces];
|
||||||
|
|
||||||
// Function: vnf_sheet()
|
// Function: vnf_sheet()
|
||||||
// Synopsis: Extends a VNF into a thin sheet by forming a small offset
|
// Synopsis: Extends a VNF into a thin sheet by extruding normal to the VNF
|
||||||
// SynTags: VNF
|
// SynTags: VNF
|
||||||
// Topics: VNF Manipulation
|
// Topics: VNF Manipulation
|
||||||
// See Also: vnf_small_offset(), vnf_boundary(), vnf_merge_points()
|
// See Also: vnf_small_offset(), vnf_boundary(), vnf_merge_points()
|
||||||
|
@ -1796,17 +1796,17 @@ function vnf_small_offset(vnf, delta, merge=true) =
|
||||||
// ---
|
// ---
|
||||||
// style = {{vnf_vertex_array()}} style to use. Default: "default"
|
// style = {{vnf_vertex_array()}} style to use. Default: "default"
|
||||||
// merge = if false then do not run {{vnf_merge_points()}}. Default: true
|
// merge = if false then do not run {{vnf_merge_points()}}. Default: true
|
||||||
// Example:
|
// Example(3D):
|
||||||
// pts = [for(x=[30:5:180]) [for(y=[-6:0.5:6]) [7*y,x, sin(x)*y^2]]];
|
// pts = [for(x=[30:5:180]) [for(y=[-6:0.5:6]) [7*y,x, sin(x)*y^2]]];
|
||||||
// vnf=vnf_vertex_array(pts);
|
// vnf=vnf_vertex_array(pts);
|
||||||
// vnf_polyhedron(vnf_sheet(vnf,-10));
|
// vnf_polyhedron(vnf_sheet(vnf,-10));
|
||||||
// Example: This example has multiple holes
|
// Example(3D): This example has multiple holes
|
||||||
// pts = [for(x=[-10:2:10]) [ for(y=[-10:2:10]) [x,1.4*y,(-abs(x)^3+y^3)/250]]];
|
// pts = [for(x=[-10:2:10]) [ for(y=[-10:2:10]) [x,1.4*y,(-abs(x)^3+y^3)/250]]];
|
||||||
// vnf = vnf_vertex_array(pts);
|
// vnf = vnf_vertex_array(pts);
|
||||||
// newface = list_remove(vnf[1], [43,42,63,88,108,109,135,134,129,155,156,164,165]);
|
// newface = list_remove(vnf[1], [43,42,63,88,108,109,135,134,129,155,156,164,165]);
|
||||||
// newvnf = [vnf[0],newface];
|
// newvnf = [vnf[0],newface];
|
||||||
// vnf_polyhedron(vnf_sheet(newvnf,2));
|
// vnf_polyhedron(vnf_sheet(newvnf,2));
|
||||||
// Example: When applied to a sphere the sheet is constructed inward, so the object appears unchanged, but cutting it in half reveals that we have changed the sphere into a shell.
|
// Example(3D): When applied to a sphere the sheet is constructed inward, so the object appears unchanged, but cutting it in half reveals that we have changed the sphere into a shell.
|
||||||
// vnf = sphere(d=100, $fn=28);
|
// vnf = sphere(d=100, $fn=28);
|
||||||
// left_half()
|
// left_half()
|
||||||
// vnf_polyhedron(vnf_sheet(vnf,15));
|
// vnf_polyhedron(vnf_sheet(vnf,15));
|
||||||
|
|
Loading…
Reference in a new issue