Open MarkJeronimus opened 11 years ago
Hm, I think this was fixed once in the past by @GilesBathgate: 9357469cce78a60cb988d904c475eb8b38928827 My main worry is breaking existing designs which depend on the current behaviour. Any comments related to that would be interesting.
I would have expected at least the projection of the sphere in the Z=0 plane to be fully intersecting with the same-order circle() and projection() cylinder():
$fn=12; difference() { circle();
} % projection() cylinder(h=2);
It would certainly make simple overlapping geometries like this easier to do without hulling to eliminate the stair-casing at the interface:
$fn=12; union() { cylinder(); sphere(); }
there are probably quite a few designs that depend on the current sphere code, just as there are many that depend on a 'cylinder' with fn=3 being a triangular prism. so it would probably break things.
however one could imagine a language extension to the sphere() command, like sphere(5,tessellation="delaunay") or sphere(10,tess="triangular"), (i can never remember how to spell tessellation)
for examples of the ways a sphere is approximated using polygons, one can visit
however IIRC the user who started the discussion found a workaround using a rotational extrusion of a circle?
If you're gonna do that, make it $ft= rather than tessellation=, allowing $ft to be set globally or passed as a parameter, (and saves typing).
In the past, the sphere probably used a hull of circles of carying detail, as can be seen in the wiki images. This is even worse if you ask me.
@clothbot it would make sense if the projection of a sphere in all three cardinal directions is equal to the circle (as stated in different words in the mailing list thread).
I tought about breaking existing designs, but imo it fixes more designs than it breaks. For example, the latest thingy i encountered that suffers from this is www.thingiverse.com/apps/customizer/run?thing_id=21758 which sufers from both staircasing and zig-zagging. People like that aren't going to implement rotational extrusion of a half-circle a by themselves.
@donbright What you call a tessellation of a sphere, 3ds Max calls a Geosphere, and it's distinct from a normal sphere because of it's useful properties.
"If you're gonna do that, make it $ft= rather than tessellation=, allowing $ft to be set globally or passed as a parameter, (and saves typing)."
yeah but then people also want to tessellate the surface of other polyhedrons in different ways, like on a gear maybe they want 'constrained delaunay triangulation' of the face, but on a 3-d bunny rabbit they want 'quadrilateral' surface tessellation. ... so it should be sphere specific if you want all your spheres tessellated in a certain way.
@MarkJeronimus - Just to clarify, I like (and prefer) the aesthetic of having explicit points at the [0,0] poles. Requiring that the X=0 and Y=0 projected circles preserve $fn (I interpret "equal to the circle" to mean geometric equivalence) would not allow this for all odd values of $fn. It would break a lot of existing designs that expect symmetry about Z=0.
Your my_sphere() example does have this symmetric points-at-poles characteristic, so perhaps that is your intended meaning of "equal to the circle", to just mean explicit points at both poles and that the projected circle about Z=0 be geometrically identical to the circle()?
$fn=11;
module my_sphere() rotate([0,0,180/$fn]) {
rotate_extrude()
intersection() {
circle();
translate([0, -1]) square(2);
}
}
// Geometrically equal to the circle
% translate([0,0,-2]) cylinder(h=2);
% my_sphere();
circle();
translate([2,0]) {
// Not geometrically equal to the circle
%projection() rotate([90,0,0]) my_sphere();
circle();
}
translate([0,2]) {
// Not geometrically equal to the circle
%projection() rotate([0,90,90]) my_sphere();
circle();
}
@donbright ATM they cant tessellate other polyhedrons. (your same argument goes to $fn etc for a sphere v's a circle or cylinder)
What I was getting at is usability. For a type of design you probably want consistency, having to add tesselation=something
as a parameter in every sphere statement would be a PITA, similarly to using $fs etc, much easier to do $fs=0.1
once and use it as a parameter in specific instances where it needs to be different.
So if you foresee other tesselations then $tX (I avoided $t before due animation) ie $ts (& surface lucks out, otherwise $tsp). Or whatever $variable...
Alternatively some global default mechanism?? default(sphere,tesselation=x,$fn=0.1) default(circle,$fn=1) etc could be handy for center= ie default(cube,center=true), or convexity=. Although extending that leads to further lexical/dynamic questions...
@clothbot No. The latitude range [-90, 90] is smaller than the longitude range [0, 360] so it makes sense that the number of facets is halved too. This means that the 'projected circle' along the x and y axes should always have an even number of facets, as if $fn is even. The current sphere also has this behavior of you look closely.
My my_sphere doesn't, bit I don't care as I never use odd values for $fn anyway. It's easily fixed anyway if you really wanted.
I thought I would post my version of this sphere implementation.
By generating list of points for a semicircle and rotate_extrude'ing that, you save a boolean operation. And you also don't have to throw away half of the circle points you just computed.
This uses the arc, and fragments functions that I orignally developed for my "Functional OpenSCAD" library which operates directly on vertex data.
module my_sphere(r=1, d) {
let(R = d == undef ? r : d/2)
rotate_extrude()
polygon(arc(r=R, angle=180, offsetAngle=-90));
}
// Draw a circular arc with center c, radius r, etc.
// "center" parameter centers the sweep of the arc about the offsetAngle (half to each side of it)
// "internal" parameter enables polyhole radius correction
function arc(r=1, angle=360, offsetAngle=0, c=[0,0], center=false, internal=false) =
let (
fragments = ceil((abs(angle) / 360) * fragments(r,$fn)),
step = angle / fragments,
a = offsetAngle-(center ? angle/2 : 0),
R = internal ? r / cos (180 / fragments) : r,
last = (abs(angle) == 360 ? 1 : 0)
)
[ for (i = [0:fragments-last] ) let(a2=i*step+a) c+R*[cos(a2), sin(a2)] ];
function fragments(r=1) = ($fn > 0) ?
($fn >= 3 ? $fn : 3) :
ceil(max(min(360.0 / $fa, r*2*PI / $fs), 5));
This produces more precise sphere with the same number of triangles.
module sphere(r=1,$fn=$fn){
n=$fn/2+1;
efn=2*ceil($fn/2);
points = concat([[0,0,-r]],
[for(j=[1:efn/2], i=[0:$fn-1])
[r*cos((j-efn/4)*360/efn)*cos((i-j/2)*360/$fn),r*cos((j-efn/4)*360/efn)*sin((i-j/2)*360/$fn),r*sin((j-efn/4)*360/efn)]],
[[0,0,r]]
);
faces = concat(
///bottom
[for(i=[0:$fn-2]) [0,i+1,i+2]], [[0,$fn,1]],
///even on side
[for(z=[1:$fn:$fn*(efn/2-2)+1])
each concat(
[for(i=[0:$fn-2]) [z+i,z+$fn+i,z+$fn+i+1]],
[[z+$fn-1,z+$fn+$fn-1,z+$fn]]
)
],
///odd on side
[for(z=[1:$fn:$fn*(efn/2-2)+1])
each concat(
[for(i=[0:$fn-2]) [z+i,z+$fn+i+1,z+i+1]],
[[z+$fn-1,z+$fn,z]]
)
]
);
polyhedron(points=points, faces=faces);
}
Related to #1990, which is arguably the 2D case of the same thing: when the vertices aren't on the axes, the bounding box is wrong.
Yes I have simply redefined sphere() in NopSCADlib to have points on all six half axes. I have never found a problem with doing that.
A common CG sphere has coordinates at integral positions along the longitude and latitude axes, but OpenSCAD spheres have vertices on the latitude axis on half positions instead.
To elaborate, if $fn=12, the sphere has longitude vertices at 0°, 30°, 60°, etc. but on the latitude, the vertices are at 15°, 45°, 75°, etc. creating an unwanted flat top and bottom.
This creates scenarios where geometry doesn't match with other geometry, or geometry ends up too thin. The full discussion with examples is on http://forum.openscad.org/Fix-the-sphere-with-vertex-on-top-td5332.html
Temporary workaround (rotation of a half-circle):
(note that a rotation of a full circle causes a void object when rendering F6).