discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Assembling complex components with hollow parts

MM
Michael Möller
Fri, May 14, 2021 12:45 PM

I know this is an old issue. I think it was asked back in 2012 when I was
new then. The question then was if one could create "negative" pieces, ie.
one which subtracts in CSG. This would ease the contortions in code to get
everything aligned for difference().

The issue is if you want to merge two pipes, ie. hollow cylinders, you need
to do the difference bit AFTER you have merged (translate/rotated into
position) the pipes. Or it's just me that can't get a good idea.

Here is a "complex"(?) object. It consists of half cylinders with a
hollowed out part. I join these in a marble track construction. The
transform is non-trivial, the pieces "Track" are perhaps trivial, but the
argument applies even more if they, too, are complex

If I create the individual pieces as hollowed out cylinders and join them
for the track, the walls partially block the track in corners. I need to
repeat the (complex) placement transform on the hollowed out part.

To ease this, I have my piece module take the argument "PM" (for Plus or
Minus).

I am wondering if there is an easier program structure  or idiom. I have
returned to this list after 6 years of absence, so a smart answer might
already be common knowledge.

Msquare

Marbletrack example:

K=25.4 ; // Marblesize
C=0.7 ; // clearance, marble
T=2 ; // wall thickness
Dy = K+C+2*T ;// Outer diameter of tracks
Di = K+C ;    // Inner    -"-

module Polar(P) {
// Rotate childern to P (polar vector)
x = P[0] ; y = P[1] ; z = P[2] ;
l = norm([x,y,z]) ;
v = acos(z/l);      // inclination angle
w = atan2(y,x);    // azimuthal angle
rotate([0,v,w]) children() ;
}

module Cylrund(D,H) {
// Cylinder with rounded end
// D=Diameter, H=height (total height=H+D/2)
difference() {
union() {
cylinder(d=D,h=H+1) ;
sphere(d=D) ;
}
translate([0,0,H]) cylinder(d=D*1.001,h=D/2) ; // trunctates sphere if
cylinder shorter
}
}

module Track(PM,H=100) {
// single track piece (stands up)
// PM is true for "plus" (Solid outer), false for "minus", (hollow bit) -
use for difference in callling module
difference() {
if ( PM )  Cylrund(Dy,H) ;
else      Cylrund(Di,H) ;
translate([-K*(PM?1:2),-K/2-T-C/2,-K/2-T-C]) cube([Di,Dy,Dy+H]) ;
}
}

module BallTrack(SP,SD=K/3) {
// Track with several sections, and support cylinders
// SP defines track path as xyz triplets (2 dim. array)
// SD can be value, list (1:1 with SP list) or 2D list:
SDlist = is_list(SD[0]) ;// SD is 2 dim. array of x,y,height placements
SDdia = is_list(SD) ; // SD is single list of diameter at each "corner"
(0=no cylinder)
// SD omitted/single value: diameter at each "corner" (0= no support)
if ( SDlist ) echo ("SDlist") ;
if ( SDdia ) echo ("SDdia") ;
difference() {
union() {
for ( n = [0:len(SP)-1] ) {
if ( n > 0 ) { // Skip first corner as track is between n and n-1
corner
ES = [ SP[n][0]-SP[n-1][0],  // single segment, "normalized"
SP[n][1]-SP[n-1][1],
SP[n][2]-SP[n-1][2] ] ;
translate(SP[n-1]) // move to start point
Polar(ES)        //  rotate to fit next point
Track(true,norm(ES)) ;
}

   // Place support cylinder at "corners"
    if ( ! SDlist ) {
      if ( SDdia ) { // take diameter from SD list, skip if =0
        if ( SD[n] > 0 ) translate([SP[n][0],SP[n][1],0])
          cylinder(d=SD[n], SP[n][2]) ;
      } else { // same diameter for all cylinders
      if ( SD > 0 ) translate([SP[n][0],SP[n][1],0])
        cylinder(d=SD, SP[n][2]) ;
      }
    }
  }
  // independent cylinder placement x,y,height
  if ( SDlist ) for ( n = [0:len(SD)-1] ) {
   translate([SD[n][0],SD[n][1],0]) cylinder(d=K/2,h=SD[n][2]) ;
  }
}

// Repeat tracklist, but now subtract inside
for ( n = [1:len(SP)-1] ) {
      ES = [ SP[n][0]-SP[n-1][0],  // single segment, "normalized"
             SP[n][1]-SP[n-1][1],
             SP[n][2]-SP[n-1][2] ] ;
      translate(SP[n-1]) // move to start point
        Polar(ES)        //  rotate to fit next point
          Track(false,norm(ES)) ;
}

}
}

BallTrack( [
[0, 0, 90], [110, 0, 74], [120, 15, 72],
[110,30,70],[20,30,57],[10,15,53],
[20,0,50],[95,0,42],[110,25,40],
[73,50,35],[73,70,30]
]
,[20,20,0,0,10,0,0,0,10,0,0]  // Few support cylinders, some slimmer
//  ,10                            // Support cylinders each corner
//  ,[ [32,12,80],[90,12,70],[73,60,35] ]  // Independent support cylinders
//  ,0                            // None
) ;

NB: I've 3D printed this and it works fine. 3D-Support is a nightmare.

I know this is an old issue. I think it was asked back in 2012 when I was new then. The question then was if one could create "negative" pieces, ie. one which subtracts in CSG. This would ease the contortions in code to get everything aligned for difference(). The issue is if you want to merge two pipes, ie. hollow cylinders, you need to do the difference bit AFTER you have merged (translate/rotated into position) the pipes. Or it's just me that can't get a good idea. Here is a "complex"(?) object. It consists of half cylinders with a hollowed out part. I join these in a marble track construction. The transform is non-trivial, the pieces "Track" are perhaps trivial, but the argument applies even more if they, too, are complex If I create the individual pieces as hollowed out cylinders and join them for the track, the walls partially block the track in corners. I need to repeat the (complex) placement transform on the hollowed out part. To ease this, I have my piece module take the argument "PM" (for Plus or Minus). I am wondering if there is an easier program structure or idiom. I have returned to this list after 6 years of absence, so a smart answer might already be common knowledge. Msquare Marbletrack example: K=25.4 ; // Marblesize C=0.7 ; // clearance, marble T=2 ; // wall thickness Dy = K+C+2*T ;// Outer diameter of tracks Di = K+C ; // Inner -"- module Polar(P) { // Rotate childern to P (polar vector) x = P[0] ; y = P[1] ; z = P[2] ; l = norm([x,y,z]) ; v = acos(z/l); // inclination angle w = atan2(y,x); // azimuthal angle rotate([0,v,w]) children() ; } module Cylrund(D,H) { // Cylinder with rounded end // D=Diameter, H=height (total height=H+D/2) difference() { union() { cylinder(d=D,h=H+1) ; sphere(d=D) ; } translate([0,0,H]) cylinder(d=D*1.001,h=D/2) ; // trunctates sphere if cylinder shorter } } module Track(PM,H=100) { // single track piece (stands up) // PM is true for "plus" (Solid outer), false for "minus", (hollow bit) - use for difference in callling module difference() { if ( PM ) Cylrund(Dy,H) ; else Cylrund(Di,H) ; translate([-K*(PM?1:2),-K/2-T-C/2,-K/2-T-C]) cube([Di,Dy,Dy+H]) ; } } module BallTrack(SP,SD=K/3) { // Track with several sections, and support cylinders // SP defines track path as xyz triplets (2 dim. array) // SD can be value, list (1:1 with SP list) or 2D list: SDlist = is_list(SD[0]) ;// SD is 2 dim. array of x,y,height placements SDdia = is_list(SD) ; // SD is single list of diameter at each "corner" (0=no cylinder) // SD omitted/single value: diameter at each "corner" (0= no support) if ( SDlist ) echo ("SDlist") ; if ( SDdia ) echo ("SDdia") ; difference() { union() { for ( n = [0:len(SP)-1] ) { if ( n > 0 ) { // Skip first corner as track is between n and n-1 corner ES = [ SP[n][0]-SP[n-1][0], // single segment, "normalized" SP[n][1]-SP[n-1][1], SP[n][2]-SP[n-1][2] ] ; translate(SP[n-1]) // move to start point Polar(ES) // rotate to fit next point Track(true,norm(ES)) ; } // Place support cylinder at "corners" if ( ! SDlist ) { if ( SDdia ) { // take diameter from SD list, skip if =0 if ( SD[n] > 0 ) translate([SP[n][0],SP[n][1],0]) cylinder(d=SD[n], SP[n][2]) ; } else { // same diameter for all cylinders if ( SD > 0 ) translate([SP[n][0],SP[n][1],0]) cylinder(d=SD, SP[n][2]) ; } } } // independent cylinder placement x,y,height if ( SDlist ) for ( n = [0:len(SD)-1] ) { translate([SD[n][0],SD[n][1],0]) cylinder(d=K/2,h=SD[n][2]) ; } } // Repeat tracklist, but now subtract inside for ( n = [1:len(SP)-1] ) { ES = [ SP[n][0]-SP[n-1][0], // single segment, "normalized" SP[n][1]-SP[n-1][1], SP[n][2]-SP[n-1][2] ] ; translate(SP[n-1]) // move to start point Polar(ES) // rotate to fit next point Track(false,norm(ES)) ; } } } BallTrack( [ [0, 0, 90], [110, 0, 74], [120, 15, 72], [110,30,70],[20,30,57],[10,15,53], [20,0,50],[95,0,42],[110,25,40], [73,50,35],[73,70,30] ] ,[20,20,0,0,10,0,0,0,10,0,0] // Few support cylinders, some slimmer // ,10 // Support cylinders each corner // ,[ [32,12,80],[90,12,70],[73,60,35] ] // Independent support cylinders // ,0 // None ) ; NB: I've 3D printed this and it works fine. 3D-Support is a nightmare.
NH
nop head
Fri, May 14, 2021 12:52 PM

If I need to place two things in the same place, e.g. a screw and a screw
hole, or in your case the outside and the inside I just make the
placement a module that translates and rotates its children so I never
repeat code.

On Fri, 14 May 2021 at 13:46, Michael Möller private2michael@gmail.com
wrote:

I know this is an old issue. I think it was asked back in 2012 when I was
new then. The question then was if one could create "negative" pieces, ie.
one which subtracts in CSG. This would ease the contortions in code to get
everything aligned for difference().

The issue is if you want to merge two pipes, ie. hollow cylinders, you
need to do the difference bit AFTER you have merged (translate/rotated into
position) the pipes. Or it's just me that can't get a good idea.

Here is a "complex"(?) object. It consists of half cylinders with a
hollowed out part. I join these in a marble track construction. The
transform is non-trivial, the pieces "Track" are perhaps trivial, but the
argument applies even more if they, too, are complex

If I create the individual pieces as hollowed out cylinders and join them
for the track, the walls partially block the track in corners. I need to
repeat the (complex) placement transform on the hollowed out part.

To ease this, I have my piece module take the argument "PM" (for Plus or
Minus).

I am wondering if there is an easier program structure  or idiom. I have
returned to this list after 6 years of absence, so a smart answer might
already be common knowledge.

Msquare

Marbletrack example:

K=25.4 ; // Marblesize
C=0.7 ; // clearance, marble
T=2 ; // wall thickness
Dy = K+C+2*T ;// Outer diameter of tracks
Di = K+C ;    // Inner    -"-

module Polar(P) {
// Rotate childern to P (polar vector)
x = P[0] ; y = P[1] ; z = P[2] ;
l = norm([x,y,z]) ;
v = acos(z/l);      // inclination angle
w = atan2(y,x);    // azimuthal angle
rotate([0,v,w]) children() ;
}

module Cylrund(D,H) {
// Cylinder with rounded end
// D=Diameter, H=height (total height=H+D/2)
difference() {
union() {
cylinder(d=D,h=H+1) ;
sphere(d=D) ;
}
translate([0,0,H]) cylinder(d=D*1.001,h=D/2) ; // trunctates sphere if
cylinder shorter
}
}

module Track(PM,H=100) {
// single track piece (stands up)
// PM is true for "plus" (Solid outer), false for "minus", (hollow bit) -
use for difference in callling module
difference() {
if ( PM )  Cylrund(Dy,H) ;
else      Cylrund(Di,H) ;
translate([-K*(PM?1:2),-K/2-T-C/2,-K/2-T-C]) cube([Di,Dy,Dy+H]) ;
}
}

module BallTrack(SP,SD=K/3) {
// Track with several sections, and support cylinders
// SP defines track path as xyz triplets (2 dim. array)
// SD can be value, list (1:1 with SP list) or 2D list:
SDlist = is_list(SD[0]) ;// SD is 2 dim. array of x,y,height placements
SDdia = is_list(SD) ; // SD is single list of diameter at each "corner"
(0=no cylinder)
// SD omitted/single value: diameter at each "corner" (0= no support)
if ( SDlist ) echo ("SDlist") ;
if ( SDdia ) echo ("SDdia") ;
difference() {
union() {
for ( n = [0:len(SP)-1] ) {
if ( n > 0 ) { // Skip first corner as track is between n and n-1
corner
ES = [ SP[n][0]-SP[n-1][0],  // single segment, "normalized"
SP[n][1]-SP[n-1][1],
SP[n][2]-SP[n-1][2] ] ;
translate(SP[n-1]) // move to start point
Polar(ES)        //  rotate to fit next point
Track(true,norm(ES)) ;
}

    // Place support cylinder at "corners"
     if ( ! SDlist ) {
       if ( SDdia ) { // take diameter from SD list, skip if =0
         if ( SD[n] > 0 ) translate([SP[n][0],SP[n][1],0])
           cylinder(d=SD[n], SP[n][2]) ;
       } else { // same diameter for all cylinders
       if ( SD > 0 ) translate([SP[n][0],SP[n][1],0])
         cylinder(d=SD, SP[n][2]) ;
       }
     }
   }
   // independent cylinder placement x,y,height
   if ( SDlist ) for ( n = [0:len(SD)-1] ) {
    translate([SD[n][0],SD[n][1],0]) cylinder(d=K/2,h=SD[n][2]) ;
   }
 }

 // Repeat tracklist, but now subtract inside
 for ( n = [1:len(SP)-1] ) {
       ES = [ SP[n][0]-SP[n-1][0],  // single segment, "normalized"
              SP[n][1]-SP[n-1][1],
              SP[n][2]-SP[n-1][2] ] ;
       translate(SP[n-1]) // move to start point
         Polar(ES)        //  rotate to fit next point
           Track(false,norm(ES)) ;
 }

}
}

BallTrack( [
[0, 0, 90], [110, 0, 74], [120, 15, 72],
[110,30,70],[20,30,57],[10,15,53],
[20,0,50],[95,0,42],[110,25,40],
[73,50,35],[73,70,30]
]
,[20,20,0,0,10,0,0,0,10,0,0]  // Few support cylinders, some slimmer
//  ,10                            // Support cylinders each corner
//  ,[ [32,12,80],[90,12,70],[73,60,35] ]  // Independent support
cylinders
//  ,0                            // None
) ;

NB: I've 3D printed this and it works fine. 3D-Support is a nightmare.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

If I need to place two things in the same place, e.g. a screw and a screw hole, or in your case the outside and the inside I just make the placement a module that translates and rotates its children so I never repeat code. On Fri, 14 May 2021 at 13:46, Michael Möller <private2michael@gmail.com> wrote: > I know this is an old issue. I think it was asked back in 2012 when I was > new then. The question then was if one could create "negative" pieces, ie. > one which subtracts in CSG. This would ease the contortions in code to get > everything aligned for difference(). > > The issue is if you want to merge two pipes, ie. hollow cylinders, you > need to do the difference bit AFTER you have merged (translate/rotated into > position) the pipes. Or it's just me that can't get a good idea. > > Here is a "complex"(?) object. It consists of half cylinders with a > hollowed out part. I join these in a marble track construction. The > transform is non-trivial, the pieces "Track" are perhaps trivial, but the > argument applies even more if they, too, are complex > > If I create the individual pieces as hollowed out cylinders and join them > for the track, the walls partially block the track in corners. I need to > repeat the (complex) placement transform on the hollowed out part. > > To ease this, I have my piece module take the argument "PM" (for Plus or > Minus). > > I am wondering if there is an easier program structure or idiom. I have > returned to this list after 6 years of absence, so a smart answer might > already be common knowledge. > > Msquare > > Marbletrack example: > > K=25.4 ; // Marblesize > C=0.7 ; // clearance, marble > T=2 ; // wall thickness > Dy = K+C+2*T ;// Outer diameter of tracks > Di = K+C ; // Inner -"- > > module Polar(P) { > // Rotate childern to P (polar vector) > x = P[0] ; y = P[1] ; z = P[2] ; > l = norm([x,y,z]) ; > v = acos(z/l); // inclination angle > w = atan2(y,x); // azimuthal angle > rotate([0,v,w]) children() ; > } > > module Cylrund(D,H) { > // Cylinder with rounded end > // D=Diameter, H=height (total height=H+D/2) > difference() { > union() { > cylinder(d=D,h=H+1) ; > sphere(d=D) ; > } > translate([0,0,H]) cylinder(d=D*1.001,h=D/2) ; // trunctates sphere if > cylinder shorter > } > } > > module Track(PM,H=100) { > // single track piece (stands up) > // PM is true for "plus" (Solid outer), false for "minus", (hollow bit) - > use for difference in callling module > difference() { > if ( PM ) Cylrund(Dy,H) ; > else Cylrund(Di,H) ; > translate([-K*(PM?1:2),-K/2-T-C/2,-K/2-T-C]) cube([Di,Dy,Dy+H]) ; > } > } > > module BallTrack(SP,SD=K/3) { > // Track with several sections, and support cylinders > // SP defines track path as xyz triplets (2 dim. array) > // SD can be value, list (1:1 with SP list) or 2D list: > SDlist = is_list(SD[0]) ;// SD is 2 dim. array of x,y,height placements > SDdia = is_list(SD) ; // SD is single list of diameter at each "corner" > (0=no cylinder) > // SD omitted/single value: diameter at each "corner" (0= no support) > if ( SDlist ) echo ("SDlist") ; > if ( SDdia ) echo ("SDdia") ; > difference() { > union() { > for ( n = [0:len(SP)-1] ) { > if ( n > 0 ) { // Skip first corner as track is between n and n-1 > corner > ES = [ SP[n][0]-SP[n-1][0], // single segment, "normalized" > SP[n][1]-SP[n-1][1], > SP[n][2]-SP[n-1][2] ] ; > translate(SP[n-1]) // move to start point > Polar(ES) // rotate to fit next point > Track(true,norm(ES)) ; > } > > // Place support cylinder at "corners" > if ( ! SDlist ) { > if ( SDdia ) { // take diameter from SD list, skip if =0 > if ( SD[n] > 0 ) translate([SP[n][0],SP[n][1],0]) > cylinder(d=SD[n], SP[n][2]) ; > } else { // same diameter for all cylinders > if ( SD > 0 ) translate([SP[n][0],SP[n][1],0]) > cylinder(d=SD, SP[n][2]) ; > } > } > } > // independent cylinder placement x,y,height > if ( SDlist ) for ( n = [0:len(SD)-1] ) { > translate([SD[n][0],SD[n][1],0]) cylinder(d=K/2,h=SD[n][2]) ; > } > } > > // Repeat tracklist, but now subtract inside > for ( n = [1:len(SP)-1] ) { > ES = [ SP[n][0]-SP[n-1][0], // single segment, "normalized" > SP[n][1]-SP[n-1][1], > SP[n][2]-SP[n-1][2] ] ; > translate(SP[n-1]) // move to start point > Polar(ES) // rotate to fit next point > Track(false,norm(ES)) ; > } > } > } > > BallTrack( [ > [0, 0, 90], [110, 0, 74], [120, 15, 72], > [110,30,70],[20,30,57],[10,15,53], > [20,0,50],[95,0,42],[110,25,40], > [73,50,35],[73,70,30] > ] > ,[20,20,0,0,10,0,0,0,10,0,0] // Few support cylinders, some slimmer > // ,10 // Support cylinders each corner > // ,[ [32,12,80],[90,12,70],[73,60,35] ] // Independent support > cylinders > // ,0 // None > ) ; > > NB: I've 3D printed this and it works fine. 3D-Support is a nightmare. > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
AC
A. Craig West
Fri, May 14, 2021 1:00 PM

Using a module for positioning is something I really need to use more. I
think I have modules stuck in my head as objects, I really need to make
more use of  children()

On Fri, 14 May 2021, 08:53 nop head, nop.head@gmail.com wrote:

If I need to place two things in the same place, e.g. a screw and a screw
hole, or in your case the outside and the inside I just make the
placement a module that translates and rotates its children so I never
repeat code.

On Fri, 14 May 2021 at 13:46, Michael Möller private2michael@gmail.com
wrote:

I know this is an old issue. I think it was asked back in 2012 when I was
new then. The question then was if one could create "negative" pieces, ie.
one which subtracts in CSG. This would ease the contortions in code to get
everything aligned for difference().

The issue is if you want to merge two pipes, ie. hollow cylinders, you
need to do the difference bit AFTER you have merged (translate/rotated into
position) the pipes. Or it's just me that can't get a good idea.

Here is a "complex"(?) object. It consists of half cylinders with a
hollowed out part. I join these in a marble track construction. The
transform is non-trivial, the pieces "Track" are perhaps trivial, but the
argument applies even more if they, too, are complex

If I create the individual pieces as hollowed out cylinders and join them
for the track, the walls partially block the track in corners. I need to
repeat the (complex) placement transform on the hollowed out part.

To ease this, I have my piece module take the argument "PM" (for Plus or
Minus).

I am wondering if there is an easier program structure  or idiom. I have
returned to this list after 6 years of absence, so a smart answer might
already be common knowledge.

Msquare

Marbletrack example:

K=25.4 ; // Marblesize
C=0.7 ; // clearance, marble
T=2 ; // wall thickness
Dy = K+C+2*T ;// Outer diameter of tracks
Di = K+C ;    // Inner    -"-

module Polar(P) {
// Rotate childern to P (polar vector)
x = P[0] ; y = P[1] ; z = P[2] ;
l = norm([x,y,z]) ;
v = acos(z/l);      // inclination angle
w = atan2(y,x);    // azimuthal angle
rotate([0,v,w]) children() ;
}

module Cylrund(D,H) {
// Cylinder with rounded end
// D=Diameter, H=height (total height=H+D/2)
difference() {
union() {
cylinder(d=D,h=H+1) ;
sphere(d=D) ;
}
translate([0,0,H]) cylinder(d=D*1.001,h=D/2) ; // trunctates sphere
if cylinder shorter
}
}

module Track(PM,H=100) {
// single track piece (stands up)
// PM is true for "plus" (Solid outer), false for "minus", (hollow bit) -
use for difference in callling module
difference() {
if ( PM )  Cylrund(Dy,H) ;
else      Cylrund(Di,H) ;
translate([-K*(PM?1:2),-K/2-T-C/2,-K/2-T-C]) cube([Di,Dy,Dy+H]) ;
}
}

module BallTrack(SP,SD=K/3) {
// Track with several sections, and support cylinders
// SP defines track path as xyz triplets (2 dim. array)
// SD can be value, list (1:1 with SP list) or 2D list:
SDlist = is_list(SD[0]) ;// SD is 2 dim. array of x,y,height placements
SDdia = is_list(SD) ; // SD is single list of diameter at each "corner"
(0=no cylinder)
// SD omitted/single value: diameter at each "corner" (0= no support)
if ( SDlist ) echo ("SDlist") ;
if ( SDdia ) echo ("SDdia") ;
difference() {
union() {
for ( n = [0:len(SP)-1] ) {
if ( n > 0 ) { // Skip first corner as track is between n and n-1
corner
ES = [ SP[n][0]-SP[n-1][0],  // single segment, "normalized"
SP[n][1]-SP[n-1][1],
SP[n][2]-SP[n-1][2] ] ;
translate(SP[n-1]) // move to start point
Polar(ES)        //  rotate to fit next point
Track(true,norm(ES)) ;
}

    // Place support cylinder at "corners"
     if ( ! SDlist ) {
       if ( SDdia ) { // take diameter from SD list, skip if =0
         if ( SD[n] > 0 ) translate([SP[n][0],SP[n][1],0])
           cylinder(d=SD[n], SP[n][2]) ;
       } else { // same diameter for all cylinders
       if ( SD > 0 ) translate([SP[n][0],SP[n][1],0])
         cylinder(d=SD, SP[n][2]) ;
       }
     }
   }
   // independent cylinder placement x,y,height
   if ( SDlist ) for ( n = [0:len(SD)-1] ) {
    translate([SD[n][0],SD[n][1],0]) cylinder(d=K/2,h=SD[n][2]) ;
   }
 }

 // Repeat tracklist, but now subtract inside
 for ( n = [1:len(SP)-1] ) {
       ES = [ SP[n][0]-SP[n-1][0],  // single segment, "normalized"
              SP[n][1]-SP[n-1][1],
              SP[n][2]-SP[n-1][2] ] ;
       translate(SP[n-1]) // move to start point
         Polar(ES)        //  rotate to fit next point
           Track(false,norm(ES)) ;
 }

}
}

BallTrack( [
[0, 0, 90], [110, 0, 74], [120, 15, 72],
[110,30,70],[20,30,57],[10,15,53],
[20,0,50],[95,0,42],[110,25,40],
[73,50,35],[73,70,30]
]
,[20,20,0,0,10,0,0,0,10,0,0]  // Few support cylinders, some slimmer
//  ,10                            // Support cylinders each corner
//  ,[ [32,12,80],[90,12,70],[73,60,35] ]  // Independent support
cylinders
//  ,0                            // None
) ;

NB: I've 3D printed this and it works fine. 3D-Support is a nightmare.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Using a module for positioning is something I really need to use more. I think I have modules stuck in my head as objects, I really need to make more use of children() On Fri, 14 May 2021, 08:53 nop head, <nop.head@gmail.com> wrote: > If I need to place two things in the same place, e.g. a screw and a screw > hole, or in your case the outside and the inside I just make the > placement a module that translates and rotates its children so I never > repeat code. > > On Fri, 14 May 2021 at 13:46, Michael Möller <private2michael@gmail.com> > wrote: > >> I know this is an old issue. I think it was asked back in 2012 when I was >> new then. The question then was if one could create "negative" pieces, ie. >> one which subtracts in CSG. This would ease the contortions in code to get >> everything aligned for difference(). >> >> The issue is if you want to merge two pipes, ie. hollow cylinders, you >> need to do the difference bit AFTER you have merged (translate/rotated into >> position) the pipes. Or it's just me that can't get a good idea. >> >> Here is a "complex"(?) object. It consists of half cylinders with a >> hollowed out part. I join these in a marble track construction. The >> transform is non-trivial, the pieces "Track" are perhaps trivial, but the >> argument applies even more if they, too, are complex >> >> If I create the individual pieces as hollowed out cylinders and join them >> for the track, the walls partially block the track in corners. I need to >> repeat the (complex) placement transform on the hollowed out part. >> >> To ease this, I have my piece module take the argument "PM" (for Plus or >> Minus). >> >> I am wondering if there is an easier program structure or idiom. I have >> returned to this list after 6 years of absence, so a smart answer might >> already be common knowledge. >> >> Msquare >> >> Marbletrack example: >> >> K=25.4 ; // Marblesize >> C=0.7 ; // clearance, marble >> T=2 ; // wall thickness >> Dy = K+C+2*T ;// Outer diameter of tracks >> Di = K+C ; // Inner -"- >> >> module Polar(P) { >> // Rotate childern to P (polar vector) >> x = P[0] ; y = P[1] ; z = P[2] ; >> l = norm([x,y,z]) ; >> v = acos(z/l); // inclination angle >> w = atan2(y,x); // azimuthal angle >> rotate([0,v,w]) children() ; >> } >> >> module Cylrund(D,H) { >> // Cylinder with rounded end >> // D=Diameter, H=height (total height=H+D/2) >> difference() { >> union() { >> cylinder(d=D,h=H+1) ; >> sphere(d=D) ; >> } >> translate([0,0,H]) cylinder(d=D*1.001,h=D/2) ; // trunctates sphere >> if cylinder shorter >> } >> } >> >> module Track(PM,H=100) { >> // single track piece (stands up) >> // PM is true for "plus" (Solid outer), false for "minus", (hollow bit) - >> use for difference in callling module >> difference() { >> if ( PM ) Cylrund(Dy,H) ; >> else Cylrund(Di,H) ; >> translate([-K*(PM?1:2),-K/2-T-C/2,-K/2-T-C]) cube([Di,Dy,Dy+H]) ; >> } >> } >> >> module BallTrack(SP,SD=K/3) { >> // Track with several sections, and support cylinders >> // SP defines track path as xyz triplets (2 dim. array) >> // SD can be value, list (1:1 with SP list) or 2D list: >> SDlist = is_list(SD[0]) ;// SD is 2 dim. array of x,y,height placements >> SDdia = is_list(SD) ; // SD is single list of diameter at each "corner" >> (0=no cylinder) >> // SD omitted/single value: diameter at each "corner" (0= no support) >> if ( SDlist ) echo ("SDlist") ; >> if ( SDdia ) echo ("SDdia") ; >> difference() { >> union() { >> for ( n = [0:len(SP)-1] ) { >> if ( n > 0 ) { // Skip first corner as track is between n and n-1 >> corner >> ES = [ SP[n][0]-SP[n-1][0], // single segment, "normalized" >> SP[n][1]-SP[n-1][1], >> SP[n][2]-SP[n-1][2] ] ; >> translate(SP[n-1]) // move to start point >> Polar(ES) // rotate to fit next point >> Track(true,norm(ES)) ; >> } >> >> // Place support cylinder at "corners" >> if ( ! SDlist ) { >> if ( SDdia ) { // take diameter from SD list, skip if =0 >> if ( SD[n] > 0 ) translate([SP[n][0],SP[n][1],0]) >> cylinder(d=SD[n], SP[n][2]) ; >> } else { // same diameter for all cylinders >> if ( SD > 0 ) translate([SP[n][0],SP[n][1],0]) >> cylinder(d=SD, SP[n][2]) ; >> } >> } >> } >> // independent cylinder placement x,y,height >> if ( SDlist ) for ( n = [0:len(SD)-1] ) { >> translate([SD[n][0],SD[n][1],0]) cylinder(d=K/2,h=SD[n][2]) ; >> } >> } >> >> // Repeat tracklist, but now subtract inside >> for ( n = [1:len(SP)-1] ) { >> ES = [ SP[n][0]-SP[n-1][0], // single segment, "normalized" >> SP[n][1]-SP[n-1][1], >> SP[n][2]-SP[n-1][2] ] ; >> translate(SP[n-1]) // move to start point >> Polar(ES) // rotate to fit next point >> Track(false,norm(ES)) ; >> } >> } >> } >> >> BallTrack( [ >> [0, 0, 90], [110, 0, 74], [120, 15, 72], >> [110,30,70],[20,30,57],[10,15,53], >> [20,0,50],[95,0,42],[110,25,40], >> [73,50,35],[73,70,30] >> ] >> ,[20,20,0,0,10,0,0,0,10,0,0] // Few support cylinders, some slimmer >> // ,10 // Support cylinders each corner >> // ,[ [32,12,80],[90,12,70],[73,60,35] ] // Independent support >> cylinders >> // ,0 // None >> ) ; >> >> NB: I've 3D printed this and it works fine. 3D-Support is a nightmare. >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
MM
Michael Möller
Fri, May 14, 2021 2:37 PM

Thanks, good advice.

So I still need to keep track of my inside and outside part/version in the
right place of my compound object difference function.

Michael, from mobile

fre. 14. maj 2021 14.53 skrev nop head nop.head@gmail.com:

If I need to place two things in the same place, e.g. a screw and a screw
hole, or in your case the outside and the inside I just make the
placement a module that translates and rotates its children so I never
repeat code.

On Fri, 14 May 2021 at 13:46, Michael Möller private2michael@gmail.com
wrote:

I know this is an old issue. I think it was asked back in 2012 when I was
new then. The question then was if one could create "negative" pieces, ie.
one which subtracts in CSG. This would ease the contortions in code to get
everything aligned for difference().

The issue is if you want to merge two pipes, ie. hollow cylinders, you
need to do the difference bit AFTER you have merged (translate/rotated into
position) the pipes. Or it's just me that can't get a good idea.

Here is a "complex"(?) object. It consists of half cylinders with a
hollowed out part. I join these in a marble track construction. The
transform is non-trivial, the pieces "Track" are perhaps trivial, but the
argument applies even more if they, too, are complex

If I create the individual pieces as hollowed out cylinders and join them
for the track, the walls partially block the track in corners. I need to
repeat the (complex) placement transform on the hollowed out part.

To ease this, I have my piece module take the argument "PM" (for Plus or
Minus).

I am wondering if there is an easier program structure  or idiom. I have
returned to this list after 6 years of absence, so a smart answer might
already be common knowledge.

Msquare

Marbletrack example:

K=25.4 ; // Marblesize
C=0.7 ; // clearance, marble
T=2 ; // wall thickness
Dy = K+C+2*T ;// Outer diameter of tracks
Di = K+C ;    // Inner    -"-

module Polar(P) {
// Rotate childern to P (polar vector)
x = P[0] ; y = P[1] ; z = P[2] ;
l = norm([x,y,z]) ;
v = acos(z/l);      // inclination angle
w = atan2(y,x);    // azimuthal angle
rotate([0,v,w]) children() ;
}

module Cylrund(D,H) {
// Cylinder with rounded end
// D=Diameter, H=height (total height=H+D/2)
difference() {
union() {
cylinder(d=D,h=H+1) ;
sphere(d=D) ;
}
translate([0,0,H]) cylinder(d=D*1.001,h=D/2) ; // trunctates sphere
if cylinder shorter
}
}

module Track(PM,H=100) {
// single track piece (stands up)
// PM is true for "plus" (Solid outer), false for "minus", (hollow bit) -
use for difference in callling module
difference() {
if ( PM )  Cylrund(Dy,H) ;
else      Cylrund(Di,H) ;
translate([-K*(PM?1:2),-K/2-T-C/2,-K/2-T-C]) cube([Di,Dy,Dy+H]) ;
}
}

module BallTrack(SP,SD=K/3) {
// Track with several sections, and support cylinders
// SP defines track path as xyz triplets (2 dim. array)
// SD can be value, list (1:1 with SP list) or 2D list:
SDlist = is_list(SD[0]) ;// SD is 2 dim. array of x,y,height placements
SDdia = is_list(SD) ; // SD is single list of diameter at each "corner"
(0=no cylinder)
// SD omitted/single value: diameter at each "corner" (0= no support)
if ( SDlist ) echo ("SDlist") ;
if ( SDdia ) echo ("SDdia") ;
difference() {
union() {
for ( n = [0:len(SP)-1] ) {
if ( n > 0 ) { // Skip first corner as track is between n and n-1
corner
ES = [ SP[n][0]-SP[n-1][0],  // single segment, "normalized"
SP[n][1]-SP[n-1][1],
SP[n][2]-SP[n-1][2] ] ;
translate(SP[n-1]) // move to start point
Polar(ES)        //  rotate to fit next point
Track(true,norm(ES)) ;
}

    // Place support cylinder at "corners"
     if ( ! SDlist ) {
       if ( SDdia ) { // take diameter from SD list, skip if =0
         if ( SD[n] > 0 ) translate([SP[n][0],SP[n][1],0])
           cylinder(d=SD[n], SP[n][2]) ;
       } else { // same diameter for all cylinders
       if ( SD > 0 ) translate([SP[n][0],SP[n][1],0])
         cylinder(d=SD, SP[n][2]) ;
       }
     }
   }
   // independent cylinder placement x,y,height
   if ( SDlist ) for ( n = [0:len(SD)-1] ) {
    translate([SD[n][0],SD[n][1],0]) cylinder(d=K/2,h=SD[n][2]) ;
   }
 }

 // Repeat tracklist, but now subtract inside
 for ( n = [1:len(SP)-1] ) {
       ES = [ SP[n][0]-SP[n-1][0],  // single segment, "normalized"
              SP[n][1]-SP[n-1][1],
              SP[n][2]-SP[n-1][2] ] ;
       translate(SP[n-1]) // move to start point
         Polar(ES)        //  rotate to fit next point
           Track(false,norm(ES)) ;
 }

}
}

BallTrack( [
[0, 0, 90], [110, 0, 74], [120, 15, 72],
[110,30,70],[20,30,57],[10,15,53],
[20,0,50],[95,0,42],[110,25,40],
[73,50,35],[73,70,30]
]
,[20,20,0,0,10,0,0,0,10,0,0]  // Few support cylinders, some slimmer
//  ,10                            // Support cylinders each corner
//  ,[ [32,12,80],[90,12,70],[73,60,35] ]  // Independent support
cylinders
//  ,0                            // None
) ;

NB: I've 3D printed this and it works fine. 3D-Support is a nightmare.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Thanks, good advice. So I still need to keep track of my inside and outside part/version in the right place of my compound object difference function. Michael, from mobile fre. 14. maj 2021 14.53 skrev nop head <nop.head@gmail.com>: > If I need to place two things in the same place, e.g. a screw and a screw > hole, or in your case the outside and the inside I just make the > placement a module that translates and rotates its children so I never > repeat code. > > On Fri, 14 May 2021 at 13:46, Michael Möller <private2michael@gmail.com> > wrote: > >> I know this is an old issue. I think it was asked back in 2012 when I was >> new then. The question then was if one could create "negative" pieces, ie. >> one which subtracts in CSG. This would ease the contortions in code to get >> everything aligned for difference(). >> >> The issue is if you want to merge two pipes, ie. hollow cylinders, you >> need to do the difference bit AFTER you have merged (translate/rotated into >> position) the pipes. Or it's just me that can't get a good idea. >> >> Here is a "complex"(?) object. It consists of half cylinders with a >> hollowed out part. I join these in a marble track construction. The >> transform is non-trivial, the pieces "Track" are perhaps trivial, but the >> argument applies even more if they, too, are complex >> >> If I create the individual pieces as hollowed out cylinders and join them >> for the track, the walls partially block the track in corners. I need to >> repeat the (complex) placement transform on the hollowed out part. >> >> To ease this, I have my piece module take the argument "PM" (for Plus or >> Minus). >> >> I am wondering if there is an easier program structure or idiom. I have >> returned to this list after 6 years of absence, so a smart answer might >> already be common knowledge. >> >> Msquare >> >> Marbletrack example: >> >> K=25.4 ; // Marblesize >> C=0.7 ; // clearance, marble >> T=2 ; // wall thickness >> Dy = K+C+2*T ;// Outer diameter of tracks >> Di = K+C ; // Inner -"- >> >> module Polar(P) { >> // Rotate childern to P (polar vector) >> x = P[0] ; y = P[1] ; z = P[2] ; >> l = norm([x,y,z]) ; >> v = acos(z/l); // inclination angle >> w = atan2(y,x); // azimuthal angle >> rotate([0,v,w]) children() ; >> } >> >> module Cylrund(D,H) { >> // Cylinder with rounded end >> // D=Diameter, H=height (total height=H+D/2) >> difference() { >> union() { >> cylinder(d=D,h=H+1) ; >> sphere(d=D) ; >> } >> translate([0,0,H]) cylinder(d=D*1.001,h=D/2) ; // trunctates sphere >> if cylinder shorter >> } >> } >> >> module Track(PM,H=100) { >> // single track piece (stands up) >> // PM is true for "plus" (Solid outer), false for "minus", (hollow bit) - >> use for difference in callling module >> difference() { >> if ( PM ) Cylrund(Dy,H) ; >> else Cylrund(Di,H) ; >> translate([-K*(PM?1:2),-K/2-T-C/2,-K/2-T-C]) cube([Di,Dy,Dy+H]) ; >> } >> } >> >> module BallTrack(SP,SD=K/3) { >> // Track with several sections, and support cylinders >> // SP defines track path as xyz triplets (2 dim. array) >> // SD can be value, list (1:1 with SP list) or 2D list: >> SDlist = is_list(SD[0]) ;// SD is 2 dim. array of x,y,height placements >> SDdia = is_list(SD) ; // SD is single list of diameter at each "corner" >> (0=no cylinder) >> // SD omitted/single value: diameter at each "corner" (0= no support) >> if ( SDlist ) echo ("SDlist") ; >> if ( SDdia ) echo ("SDdia") ; >> difference() { >> union() { >> for ( n = [0:len(SP)-1] ) { >> if ( n > 0 ) { // Skip first corner as track is between n and n-1 >> corner >> ES = [ SP[n][0]-SP[n-1][0], // single segment, "normalized" >> SP[n][1]-SP[n-1][1], >> SP[n][2]-SP[n-1][2] ] ; >> translate(SP[n-1]) // move to start point >> Polar(ES) // rotate to fit next point >> Track(true,norm(ES)) ; >> } >> >> // Place support cylinder at "corners" >> if ( ! SDlist ) { >> if ( SDdia ) { // take diameter from SD list, skip if =0 >> if ( SD[n] > 0 ) translate([SP[n][0],SP[n][1],0]) >> cylinder(d=SD[n], SP[n][2]) ; >> } else { // same diameter for all cylinders >> if ( SD > 0 ) translate([SP[n][0],SP[n][1],0]) >> cylinder(d=SD, SP[n][2]) ; >> } >> } >> } >> // independent cylinder placement x,y,height >> if ( SDlist ) for ( n = [0:len(SD)-1] ) { >> translate([SD[n][0],SD[n][1],0]) cylinder(d=K/2,h=SD[n][2]) ; >> } >> } >> >> // Repeat tracklist, but now subtract inside >> for ( n = [1:len(SP)-1] ) { >> ES = [ SP[n][0]-SP[n-1][0], // single segment, "normalized" >> SP[n][1]-SP[n-1][1], >> SP[n][2]-SP[n-1][2] ] ; >> translate(SP[n-1]) // move to start point >> Polar(ES) // rotate to fit next point >> Track(false,norm(ES)) ; >> } >> } >> } >> >> BallTrack( [ >> [0, 0, 90], [110, 0, 74], [120, 15, 72], >> [110,30,70],[20,30,57],[10,15,53], >> [20,0,50],[95,0,42],[110,25,40], >> [73,50,35],[73,70,30] >> ] >> ,[20,20,0,0,10,0,0,0,10,0,0] // Few support cylinders, some slimmer >> // ,10 // Support cylinders each corner >> // ,[ [32,12,80],[90,12,70],[73,60,35] ] // Independent support >> cylinders >> // ,0 // None >> ) ; >> >> NB: I've 3D printed this and it works fine. 3D-Support is a nightmare. >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
L
LenStruttmann
Fri, May 14, 2021 8:13 PM

If I need to place two things in the same place, e.g. a screw and a screw
hole, or in your case the outside and the inside I just make the placement a
module that translates and rotates its children so I never repeat code.

I do something similar with a module called adjust() that performs rotations
and translations. adjust() is part of a file of utilities that I "use <>" in
every part.

Example:

screwAdjustments = [[0,0,90],[5,10,-5]]; // [rotations,translations]

adjust(screwAdjustments)
screws();

adjust(screwAdjustments)
screwHoless();

--
Sent from: http://forum.openscad.org/

If I need to place two things in the same place, e.g. a screw and a screw hole, or in your case the outside and the inside I just make the placement a module that translates and rotates its children so I never repeat code. I do something similar with a module called adjust() that performs rotations and translations. adjust() is part of a file of utilities that I "use <>" in every part. Example: screwAdjustments = [[0,0,90],[5,10,-5]]; // [rotations,translations] adjust(screwAdjustments) screws(); adjust(screwAdjustments) screwHoless(); -- Sent from: http://forum.openscad.org/