Hi,
I guess this is a pretty common newbie question, but it seems hard to
google. My use case is pretty simple. I have a plate that I want to mount
on a piece of hardware and the plate needs to have raised holes for PCB:s.
The raised holes are constructed from a large cylinder and a smaller
cylinder is then the hole:
module raised_hole(innerdiam) {
difference() {
cylinder(h = hole_height, d1 = 8, d2 = 6, $fn = 20);
cylinder(h = hole_height, d = innerdiam, $fn = 10);
}
}
The hole needs to go through the plate itself though.
So I just want to abstract the "raised mounting hole" thingie, but of
course if I union the plate with something with a hole in, it doesn't make
the hole go through the plate…
[image: Screen Shot 2017-02-15 at 00.10.36.png]
So I don't see a way to actually abstract something that has both positive
and negative volume without pulling the two things (the raised thing and
the whole) apart - which kindof defeats the purpose of defining a reusable
component.
I guess I'm just not seeing something obvious here…
Thanks for any pointers!
Cheers,
Viktor
I'm not certain this approach fits yours, but it might be helpful in some
form.
Consider the following:
solids(){
cube([stuff here]);
cylinder(more stuff here);
}
holes(){
cube([yet more stuff here]);
cylinder(you know);
}
difference(){
solids();
holes();
}
It's worked for me for a few projects in the past, when one wishes to
combine shapes in an order that otherwise would not generate the desired
output.
--
View this message in context: http://forum.openscad.org/A-component-abstraction-with-both-positive-and-negative-volume-tp20439p20440.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
Hi Viktor,
As fred said, you generally have to separate the additive elements from the subtractive ones, so that at the top-level you're basically differencing the union of all of the additive items from the union of all of the subtractive ones. You can't always do the differencing at a lower level and get the desired result, as you've observed.
Another thing you can consider is relativity.scad. It makes it pretty easy to assign 'classes' to objects (at least once you 'get' the whole CSS thing, I never played with CSS myself), and provides a "differed()" which is like difference() but you tell it the positive and negative classes rather than it depending on the order they're declared. Also makes it easy to build objects on top of other objects. I believe the official home is here: <https://github.com/davidson16807/relativity.scad>.
In the end differed() is really just running difference(), the first object is your entire sub-tree of parts with only the positive items enabled, the second object is the entire sub-tree again with only the negative items enabled, it effectively (at least) doubles the size of your tree (really it triples it because there's a third argument for an 'unaffected' class, see the description of differed from the link above), so with complex trees the rendering time can explode especially as you layer more relativity constructs. Provided your objects aren't too complex or you're patient, I find it to be a really handy library for lots of things, it may be overkill in this particular application, but I figured it's worth pointing out.
I hope that helps. - Will
On Feb 14, 2017, at 3:17 PM, Viktor Hedefalk hedefalk@gmail.com wrote:
Hi,
I guess this is a pretty common newbie question, but it seems hard to google. My use case is pretty simple. I have a plate that I want to mount on a piece of hardware and the plate needs to have raised holes for PCB:s. The raised holes are constructed from a large cylinder and a smaller cylinder is then the hole:
module raised_hole(innerdiam) {
difference() {
cylinder(h = hole_height, d1 = 8, d2 = 6, $fn = 20);
cylinder(h = hole_height, d = innerdiam, $fn = 10);
}
}
The hole needs to go through the plate itself though.
So I just want to abstract the "raised mounting hole" thingie, but of course if I union the plate with something with a hole in, it doesn't make the hole go through the plate…
<Screen Shot 2017-02-15 at 00.10.36.png>
So I don't see a way to actually abstract something that has both positive and negative volume without pulling the two things (the raised thing and the whole) apart - which kindof defeats the purpose of defining a reusable component.
I guess I'm just not seeing something obvious here…
Thanks for any pointers!
Cheers,
Viktor
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Yep, there is no magic solution to this. There was some talk of having
'negative' objects, but that would be something for the future.
BTW, that green shimmer in your image is z-fighting
https://en.wikipedia.org/wiki/Z-fighting , when differencing you want the
negative part to be bigger, extending either side of the cut faces, like
s=0.05;
module raised_hole(innerdiam) {
difference() {
cylinder(h = hole_height, d1 = 8, d2 = 6, $fn = 20);
translate([0,0,-s])
cylinder(h = hole_height+s*2, d = innerdiam, $fn = 10);
}
}
Admin - PM me if you need anything, or if I've done something stupid...
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
View this message in context: http://forum.openscad.org/A-component-abstraction-with-both-positive-and-negative-volume-tp20439p20441.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
Thanks all for the quick replies!
Yeah, I guess there's no free lunch here. I remember I read something about
'negative' objects too, but I guess then everything needs to have some kind
of 'priority' too…
Is there any kind of standard procedure to define the components using
functions then? Like returning pairs of positive and negative volumes?
Definitely will look into reletivity.scad!
About Z-fighting - I haven't really seen the it as a problem. When I turn
it into an STL for printing with Cura it seems to be gone anyways - rather
it just proves I'm doing stuff exact rather than having unnecessarily large
negative volumes… Is there a problem I'm not considering?
[image: Screen Shot 2017-02-15 at 08.40.42.png]
Cheers,
Viktor
On 2017-02-15 08:41, Viktor Hedefalk wrote:
Thanks all for the quick replies!
Yeah, I guess there's no free lunch here. I remember I read something
about 'negative' objects too, but I guess then everything needs to
have some kind of 'priority' too…
Is there any kind of standard procedure to define the components using
functions then? Like returning pairs of positive and negative volumes?
I don't think "negative volume" is a useful term here. I have sympathy
for the idea of reusable components as I have been thinking that way
myself. However, the "raised mounting hole" example is not an
independent component that can be attached to something else and then
there is a hole under it by magic. Instead I find it useful to think
like a real construction process: Attach the "raised mounting pad" to
the plate, then drill a hole through both using a tool. You have then
only "positive" volumes, the parts and the tool(s). The drilling is of
course done using difference() between the two.
So a "standard" procedure could be something like
a) model the parts
b) model the tools.
c) assemble the parts
d) drill ( using difference() ) holes in the assembly, using the tools.
Carsten Arnholm
I do understand this, however, I still think there is definitely room for a
simple and approachable standard way to "componentize" things with both
positive and negative volumes together in actual code. I think my concrete
use case is a very reasonable example of this. How would you go about being
DRY (don't repeat yourself) for the coordinates of each of my PCB mounting
points?
In my case, I could just define an array of x,y's for each such hole and
then use this from two places - one for the raised part and one for the
hole. I still see problems with this though. If I want to go further (which
I actually do) as to make a module for each PCB (there are three here) so I
can move them around individually, stuff get's complicated and it's really
really hard to be dry.
The thing with not having negative volumes is that it makes it impossible
to compenentize things that depend on it any deeper than one level in a
scene graph, doesn't it? At least using modules.
I'm pretty new to openscad so I'm not entirely sure what's possible.
relativity.scad seems do achieve what I need though - I haven't looked into
how it is implemented. Before seeing that I would have said that modules
becomes pretty much useless for this, but probably I could use functions
where translations for each subtree of the graph is passed as arguments
instead of using transformations and then pairs of positive and negative
volumes (already translated to absolute coords) are returned to the topmost
level doing a disjunction?
Just for reference, here the thing:
https://gist.github.com/hedefalk/74cdf38cc3cb7f855e743bc8d5c8b389
I'm now printing without complete holes (gonna use a drill bit instead :) )
Cheers,
Viktor
I'm pretty new to openscad, but not to programming
On Wed, Feb 15, 2017, 09:28 arnholm@arnholm.org wrote:
On 2017-02-15 08:41, Viktor Hedefalk wrote:
Thanks all for the quick replies!
Yeah, I guess there's no free lunch here. I remember I read something
about 'negative' objects too, but I guess then everything needs to
have some kind of 'priority' too…
Is there any kind of standard procedure to define the components using
functions then? Like returning pairs of positive and negative volumes?
I don't think "negative volume" is a useful term here. I have sympathy
for the idea of reusable components as I have been thinking that way
myself. However, the "raised mounting hole" example is not an
independent component that can be attached to something else and then
there is a hole under it by magic. Instead I find it useful to think
like a real construction process: Attach the "raised mounting pad" to
the plate, then drill a hole through both using a tool. You have then
only "positive" volumes, the parts and the tool(s). The drilling is of
course done using difference() between the two.
So a "standard" procedure could be something like
a) model the parts
b) model the tools.
c) assemble the parts
d) drill ( using difference() ) holes in the assembly, using the tools.
Carsten Arnholm
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
And thanks again for the pointer to relativity.scad. I think I will use it
next time, just didn't have the time right now :)
On Wed, Feb 15, 2017 at 10:32 AM Viktor Hedefalk hedefalk@gmail.com wrote:
I do understand this, however, I still think there is definitely room for
a simple and approachable standard way to "componentize" things with both
positive and negative volumes together in actual code. I think my concrete
use case is a very reasonable example of this. How would you go about being
DRY (don't repeat yourself) for the coordinates of each of my PCB mounting
points?
In my case, I could just define an array of x,y's for each such hole and
then use this from two places - one for the raised part and one for the
hole. I still see problems with this though. If I want to go further (which
I actually do) as to make a module for each PCB (there are three here) so I
can move them around individually, stuff get's complicated and it's really
really hard to be dry.
The thing with not having negative volumes is that it makes it impossible
to compenentize things that depend on it any deeper than one level in a
scene graph, doesn't it? At least using modules.
I'm pretty new to openscad so I'm not entirely sure what's possible.
relativity.scad seems do achieve what I need though - I haven't looked into
how it is implemented. Before seeing that I would have said that modules
becomes pretty much useless for this, but probably I could use functions
where translations for each subtree of the graph is passed as arguments
instead of using transformations and then pairs of positive and negative
volumes (already translated to absolute coords) are returned to the topmost
level doing a disjunction?
Just for reference, here the thing:
https://gist.github.com/hedefalk/74cdf38cc3cb7f855e743bc8d5c8b389
I'm now printing without complete holes (gonna use a drill bit instead :) )
Cheers,
Viktor
I'm pretty new to openscad, but not to programming
On Wed, Feb 15, 2017, 09:28 arnholm@arnholm.org wrote:
On 2017-02-15 08:41, Viktor Hedefalk wrote:
Thanks all for the quick replies!
Yeah, I guess there's no free lunch here. I remember I read something
about 'negative' objects too, but I guess then everything needs to
have some kind of 'priority' too…
Is there any kind of standard procedure to define the components using
functions then? Like returning pairs of positive and negative volumes?
I don't think "negative volume" is a useful term here. I have sympathy
for the idea of reusable components as I have been thinking that way
myself. However, the "raised mounting hole" example is not an
independent component that can be attached to something else and then
there is a hole under it by magic. Instead I find it useful to think
like a real construction process: Attach the "raised mounting pad" to
the plate, then drill a hole through both using a tool. You have then
only "positive" volumes, the parts and the tool(s). The drilling is of
course done using difference() between the two.
So a "standard" procedure could be something like
a) model the parts
b) model the tools.
c) assemble the parts
d) drill ( using difference() ) holes in the assembly, using the tools.
Carsten Arnholm
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
The way I define objects with DRY without negative volumes is to define a
module that translates its children to the hole positions. I use that to
make bosses, drill holes and place fasteners, etc. E.g. a simplified
version:
module pcb_screw_positions(type) {
holes = pcb_holes(type);
for($i = [0 : len(holes) - 1]) {
p = pcb_coord(type, holes[$i]);
translate([p.x, p.y, 0])
children();
}
}
module pcb(type) {
color("green") render() difference() {
rounded_rectangle([pcb_length(type), pcb_width(type),
pcb_thickness(type)], r = pcb_radius(type), center = false);
pcb_screw_positions(type)
cylinder(d = pcb_hole_d(type) + eps, h = 100, center = true);
}
color("silver") render()
pcb_screw_positions(type)
difference() {
translate([0, 0, -eps])
cylinder(d = 6, h = pcb_thickness(type) + 2 * eps);
cylinder(d = pcb_hole_d(type), h = 100, center = true);
}
pcb_components(type);
}
module pcb_assembly(type, height, thickness) {
screw = pcb_screw(type);
washer = screw_washer(screw);
nut = screw_nut(screw);
screw_length = screw_longer_than(height + thickness +
pcb_thickness(type) + washer_thickness(washer) + nut_thickness(nut, true));
translate([0, 0, height])
pcb(type);
pcb_screw_positions(type) {
translate([0, 0, height + pcb_thickness(type)])
screw(screw, screw_length);
color("lime") render() pcb_spacer(screw, height);
translate([0, 0, -thickness])
vflip()
nut_and_washer(nut, true);
}
}
module box_base(type) {
render_sheet(box_sheets(box)) difference() {
box_base_blank(type);
foot_positions()
drill(screw_clearance_radius(foot_screw()));
translate([psu_x, psu_y, 0])
rotate(-90)
psu_screw_positions(psu)
drill(psu_screw_hole_radius(psu));
translate([ssr_x, ssr_y, 0])
ssr_hole_positions(bed_ssr)
drill(M3_clearance_radius);
if(rpi)
translate([rpi_x, rpi_y, 0])
rotate(90)
pcb_screw_positions(rpi)
drill(pcb_screw(rpi));
translate([controller_x, controller_y, 0])
rotate(90)
pcb_screw_positions(controller)
drill(pcb_screw(controller));
}
translate([psu_x, psu_y, sheet_thickness / 2])
rotate(-90) {
psu(psu);
psu_screw_positions(psu)
translate([0, 0, -sheet_thickness])
vflip()
screw_and_washer(psu_screw_type(psu), 6, true);
}
translate([ssr_x, ssr_y, sheet_thickness / 2])
ssr_assembly(bed_ssr, M3_cap_screw, sheet_thickness);
if(rpi)
translate([rpi_x, rpi_y, sheet_thickness / 2])
rotate(90)
pcb_assembly(rpi, rpi_z, sheet_thickness);
translate([controller_x, controller_y, sheet_thickness / 2])
rotate(90)
pcb_assembly(controller, controller_z, sheet_thickness);
foot_positions()
foot_assembly(sheet_thickness(box_sheets(box)));
}
On 15 February 2017 at 09:32, Viktor Hedefalk hedefalk@gmail.com wrote:
And thanks again for the pointer to relativity.scad. I think I will use it
next time, just didn't have the time right now :)
On Wed, Feb 15, 2017 at 10:32 AM Viktor Hedefalk hedefalk@gmail.com
wrote:
I do understand this, however, I still think there is definitely room for
a simple and approachable standard way to "componentize" things with both
positive and negative volumes together in actual code. I think my concrete
use case is a very reasonable example of this. How would you go about being
DRY (don't repeat yourself) for the coordinates of each of my PCB mounting
points?
In my case, I could just define an array of x,y's for each such hole and
then use this from two places - one for the raised part and one for the
hole. I still see problems with this though. If I want to go further (which
I actually do) as to make a module for each PCB (there are three here) so I
can move them around individually, stuff get's complicated and it's really
really hard to be dry.
The thing with not having negative volumes is that it makes it impossible
to compenentize things that depend on it any deeper than one level in a
scene graph, doesn't it? At least using modules.
I'm pretty new to openscad so I'm not entirely sure what's possible.
relativity.scad seems do achieve what I need though - I haven't looked into
how it is implemented. Before seeing that I would have said that modules
becomes pretty much useless for this, but probably I could use functions
where translations for each subtree of the graph is passed as arguments
instead of using transformations and then pairs of positive and negative
volumes (already translated to absolute coords) are returned to the topmost
level doing a disjunction?
Just for reference, here the thing:
https://gist.github.com/hedefalk/74cdf38cc3cb7f855e743bc8d5c8b389
I'm now printing without complete holes (gonna use a drill bit instead :)
)
Cheers,
Viktor
I'm pretty new to openscad, but not to programming
On Wed, Feb 15, 2017, 09:28 arnholm@arnholm.org wrote:
On 2017-02-15 08:41, Viktor Hedefalk wrote:
Thanks all for the quick replies!
Yeah, I guess there's no free lunch here. I remember I read something
about 'negative' objects too, but I guess then everything needs to
have some kind of 'priority' too…
Is there any kind of standard procedure to define the components using
functions then? Like returning pairs of positive and negative volumes?
I don't think "negative volume" is a useful term here. I have sympathy
for the idea of reusable components as I have been thinking that way
myself. However, the "raised mounting hole" example is not an
independent component that can be attached to something else and then
there is a hole under it by magic. Instead I find it useful to think
like a real construction process: Attach the "raised mounting pad" to
the plate, then drill a hole through both using a tool. You have then
only "positive" volumes, the parts and the tool(s). The drilling is of
course done using difference() between the two.
So a "standard" procedure could be something like
a) model the parts
b) model the tools.
c) assemble the parts
d) drill ( using difference() ) holes in the assembly, using the tools.
Carsten Arnholm
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Another DRY technique I use is my connector modules take a cutout flag.
When it is true instead of drawing the connector they define the shape of
the panel cutout needed for it to be panel mounted. I then have a
pcb_components module that either draws all the connectors in the correct
positions or produces all the cutouts that can be subtracted from a panel
or case. E.g.
module rj45(cutout = false) {
l = 21;
w = 16;
h = 13.5;
plug_h = 6.8;
plug_w = 12;
plug_z = 4;
tab_z = 0.8;
tab_w = 4;
translate([0, 0, h / 2])
if(cutout)
rotate([90, 0, 90])
dogbone_rectangle([w + 2 * panel_clearance, h + 2 *
panel_clearance, 100], center = false);
else
difference() {
color("silver") render() cube([l, w, h], center = true);
color(grey30) render(convexity = 3) {
translate([l / 2, 0, -h / 2 + plug_z + plug_h / 2])
cube([l, plug_w, plug_h], center = true);
translate([l / 2, 0, -h / 2 + tab_z + plug_h / 2])
cube([l, tab_w, plug_h], center = true);
translate([l / 2, 0, -h / 2 + tab_z + plug_h / 2])
cube([0.1, plug_w, plug_h], center = true);
}
}
}
module pcb_components(type, cutouts = false)
for(comp = pcb_components(type)) {
p = pcb_coord(type, [comp.x, comp.y]);
name = comp[3];
translate([p.x, p.y, pcb_thickness(type)])
rotate(comp.z) {
if(name == "2p54header") pin_header(2p54header, comp[4],
comp[5], cutouts);
if(name == "chip") chip(comp[4], comp[5], comp[6], cutouts);
if(name == "rj45") rj45(cutouts);
if(name == "usb_Ax2") usb_Ax2(cutouts);
if(name == "usb_uA") usb_uA(cutouts);
if(name == "jack") jack(cutouts);
if(name == "hdmi") hdmi(cutouts);
if(name == "flex") flex(cutouts);
}
}
module pcb_cutouts(type) pcb_components(type, true);
They act as negative volumes because I subtract them with difference.
module box_back(type) {
assembly("back_assembly");
render_sheet(box_sheets(box)) difference() {
box_back_blank(type);
translate([-extruder_hot_end_offset(extruders[0]).x,
box_back_bottom, 0])
z_axis_holes();
translate([-fan_x, fan_z, 0])
fan_holes(electronics_fan);
translate([-inlet_x, inlet_z, 0])
iec_holes(mains_inlet);
translate([-switch_x, switch_z, 0])
rocker_hole(mains_switch);
if(rpi)
translate([-rpi_x, rpi_z - box_height(box) / 2,
-pcb_length(rpi)])
rotate([-90, -90, 0])
pcb_cutouts(rpi);
}
translate([-fan_x, fan_z, -sheet_thickness / 2]) {
fan_assembly(electronics_fan, sheet_thickness +
fan_guard_thickness(electronics_fan));
translate([0, 0, sheet_thickness])
color("lime") render() fan_guard(electronics_fan);
}
translate([-inlet_x, inlet_z, sheet_thickness / 2])
iec_assembly(mains_inlet, sheet_thickness);
translate([-switch_x, switch_z, sheet_thickness / 2])
rocker(mains_switch);
translate([-extruder_hot_end_offset(extruders[0]).x, box_back_bottom,
-sheet_thickness / 2,])
rotate([90, 0, 180])
z_axis_assembly();
end("back_assembly");
}