Wish: named transformable points

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Wish: named transformable points

Piotr Wyderski
I propose to add the following syntactis sugar to greatly simplify compositions of complex modules.
It is desirable to decompose a complex project into a number of reusable modules and then connect
them at the top level. The problem is that it is extremely hard to define the correct point of interest
of a given module (say, the upper right corner of a box) after all the transformations applied to the module.
OpenSCAD contains all the necessary algebraic framework to calculate its position, but it directly breaks
the modularization principle, requiring a parallel 3D point calculator structure. The proposed extension
is to introduce named points which are both externally referable and undergo all the transformations
the rest of the module undergoes. For example:

 module anchored_cube(string prefix) {

     cube(1, center=false);
     point(prefix + "_" + "my_anchor", [1, 1, 1]);
 }

translate([4, 5, 6]) rotate([13.7, 0, 90]) anchored_cube("ac1");
translate(point("ac1_my_anchor")) sphere(r=1, center=true); // adds a sphere centered at the anchored_cube's reference frame [1,1,1], whereever it truly is.
  
The example is simple, but not very realistic, the main purpose of the named points is to be used in extruded polygons.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: Wish: named transformable points

doug.moen
The Relativity library supports anchor points. Take a look and see if it satisfies your particular use cases.

On 15 October 2017 at 05:14, Piotr Wyderski <[hidden email]> wrote:
I propose to add the following syntactis sugar to greatly simplify compositions of complex modules.
It is desirable to decompose a complex project into a number of reusable modules and then connect
them at the top level. The problem is that it is extremely hard to define the correct point of interest
of a given module (say, the upper right corner of a box) after all the transformations applied to the module.
OpenSCAD contains all the necessary algebraic framework to calculate its position, but it directly breaks
the modularization principle, requiring a parallel 3D point calculator structure. The proposed extension
is to introduce named points which are both externally referable and undergo all the transformations
the rest of the module undergoes. For example:

 module anchored_cube(string prefix) {

     cube(1, center=false);
     point(prefix + "_" + "my_anchor", [1, 1, 1]);
 }

translate([4, 5, 6]) rotate([13.7, 0, 90]) anchored_cube("ac1");
translate(point("ac1_my_anchor")) sphere(r=1, center=true); // adds a sphere centered at the anchored_cube's reference frame [1,1,1], whereever it truly is.
  
The example is simple, but not very realistic, the main purpose of the named points is to be used in extruded polygons.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: Wish: named transformable points

Piotr Wyderski
A very interesting library, but my proposal is unrelated to your anchor points, as I understand them. The essence is to have designated spatial locations referable from the outside, not very convenient local reference frames. It is
very easy to define such a named point in the module's reference frame, but then you quickly loose control about its location in the final reference frame due to all the garden-variety transformations that can be applied. In principle you can restore all that information using the well-known 4x4 transformation matrix approach, but this breaks the modular design approach (build more complex shapes by composing the more fundamental ones), as you need to implement it again next to the involved solids and then keep them in sync if something changes.
Say you have a complex shape defined by a module (a sequence of terminal block busbars) and want to bind three such module instances with supporting walls using an extruded polygon. It is then just insanely hard to tell where, say, the upper left corner of the second bar is in the "final" space, even though it is very easy to define it in the module's reference frame. And it is all I ask for: allow me to attach a symbolic name to such a designated point and then refer to this location from outside of the module, e.g. making it an item of the polygon's list. It is just a syntactic sugar, because the required transformation framework is already there and used for transforming every single vertex of the design. It "just" vastly simplifies compositions and eradicates an entire universe of bugs.

   Best regards, Piotr





2017-10-16 0:22 GMT+02:00 doug moen <[hidden email]>:
The Relativity library supports anchor points. Take a look and see if it satisfies your particular use cases.

On 15 October 2017 at 05:14, Piotr Wyderski <[hidden email]> wrote:
I propose to add the following syntactis sugar to greatly simplify compositions of complex modules.
It is desirable to decompose a complex project into a number of reusable modules and then connect
them at the top level. The problem is that it is extremely hard to define the correct point of interest
of a given module (say, the upper right corner of a box) after all the transformations applied to the module.
OpenSCAD contains all the necessary algebraic framework to calculate its position, but it directly breaks
the modularization principle, requiring a parallel 3D point calculator structure. The proposed extension
is to introduce named points which are both externally referable and undergo all the transformations
the rest of the module undergoes. For example:

 module anchored_cube(string prefix) {

     cube(1, center=false);
     point(prefix + "_" + "my_anchor", [1, 1, 1]);
 }

translate([4, 5, 6]) rotate([13.7, 0, 90]) anchored_cube("ac1");
translate(point("ac1_my_anchor")) sphere(r=1, center=true); // adds a sphere centered at the anchored_cube's reference frame [1,1,1], whereever it truly is.
  
The example is simple, but not very realistic, the main purpose of the named points is to be used in extruded polygons.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: Wish: named transformable points

nophead
All you need to do is define a module that transforms its children to the anchor point. Then however the object is placed you can transform to the anchor.

module anchored_cube() {
    cube(1, center=false);
}

module cube_anchor()
     translate([1,1,1])
         children();

translate([4, 5, 6]) rotate([13.7, 0, 90]) {
      anchored_cube(");
      cube_anchor()
           sphere(1);
}


For example I have a module to draw stepper motors with the origin at the base of the shaft.

module NEMA(motor);

I also have a module to represent the screw holes relative to its origin.

module NEMA_screw_positions(motor, n = 4);


In my design I will have a position for the motor, defined relative to the origin of the machine. I can use that to place the motor but I can also use it to drill the holes for it and place the screws. Nothing is repeated or calculated twice. Whatever transforms I use to place the motor are named modules or constants so I can apply them to the screw positions. I make a module to represent a motor bracket assembly that places the motor, its bracket and the fasteners and pulley. Then I place the assembly on another sub assembly, such as the X axis. That gets placed in the machine.

For something simple with one anchor point, like your example, or in my case a washer, I make the module that draws the washer translate any children to is top surface. So I can just do washer(M3_washer) screw(m3_screw);

So rather than make up a new language feature you just need to represent connection points with modules instead of strings.

I produce very modular and hierarchical designs with no manual positioning or repetition. Positions are only ever defined once and used everywhere.



On 16 October 2017 at 13:17, Piotr Wyderski <[hidden email]> wrote:
A very interesting library, but my proposal is unrelated to your anchor points, as I understand them. The essence is to have designated spatial locations referable from the outside, not very convenient local reference frames. It is
very easy to define such a named point in the module's reference frame, but then you quickly loose control about its location in the final reference frame due to all the garden-variety transformations that can be applied. In principle you can restore all that information using the well-known 4x4 transformation matrix approach, but this breaks the modular design approach (build more complex shapes by composing the more fundamental ones), as you need to implement it again next to the involved solids and then keep them in sync if something changes.
Say you have a complex shape defined by a module (a sequence of terminal block busbars) and want to bind three such module instances with supporting walls using an extruded polygon. It is then just insanely hard to tell where, say, the upper left corner of the second bar is in the "final" space, even though it is very easy to define it in the module's reference frame. And it is all I ask for: allow me to attach a symbolic name to such a designated point and then refer to this location from outside of the module, e.g. making it an item of the polygon's list. It is just a syntactic sugar, because the required transformation framework is already there and used for transforming every single vertex of the design. It "just" vastly simplifies compositions and eradicates an entire universe of bugs.

   Best regards, Piotr





2017-10-16 0:22 GMT+02:00 doug moen <[hidden email]>:
The Relativity library supports anchor points. Take a look and see if it satisfies your particular use cases.

On 15 October 2017 at 05:14, Piotr Wyderski <[hidden email]> wrote:
I propose to add the following syntactis sugar to greatly simplify compositions of complex modules.
It is desirable to decompose a complex project into a number of reusable modules and then connect
them at the top level. The problem is that it is extremely hard to define the correct point of interest
of a given module (say, the upper right corner of a box) after all the transformations applied to the module.
OpenSCAD contains all the necessary algebraic framework to calculate its position, but it directly breaks
the modularization principle, requiring a parallel 3D point calculator structure. The proposed extension
is to introduce named points which are both externally referable and undergo all the transformations
the rest of the module undergoes. For example:

 module anchored_cube(string prefix) {

     cube(1, center=false);
     point(prefix + "_" + "my_anchor", [1, 1, 1]);
 }

translate([4, 5, 6]) rotate([13.7, 0, 90]) anchored_cube("ac1");
translate(point("ac1_my_anchor")) sphere(r=1, center=true); // adds a sphere centered at the anchored_cube's reference frame [1,1,1], whereever it truly is.
  
The example is simple, but not very realistic, the main purpose of the named points is to be used in extruded polygons.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: Wish: named transformable points

Piotr Wyderski
Thank you very much for your explanation, but I still don't know how to scale this technique to a more complex design, so, for the sake of concreteness, below I included an excerpt from my project. You have three busbars there
and each busbar compartment is the atomic entity where the real modeling starts. Now I want to connect all the three smallest side faces with a supporting wall, defined in a separate module and made of a linearly extruded polygon, say of thickness 2. How am I going to find the exact spatial locations of the four corners of every face to put them on the polygon vertex list? With my named point approach I would have done exactly this: define these four vertices in the bar's reference frame (extremely easy, say they are (0,0), (0, 1), (1, 0) and (1, 1)), let them transform together with the  bar itself and then simply refer to them in the wall's module:

    linear_extrude(height=2, center=false) {
 
      polygon(points = ["bar1.upper_left", "bar1.lower_left", "bar2.upper_left",    "bar2.lower_left", ...
    }

Could you please show me a sketch of the solution using your technique?

default_fn=30;
epsilon=1e-2;
alot=1e2;

frame_width=199.5;
frame_height=140;
frame_depth=96;

cable_channel_width=30;
outside_compartment_height = 10;

draw_metal_elements=true;

module screw(diameter, height) {

    difference() {

        color("Silver") {

            cylinder(d=diameter, h=height-2, $fn=default_fn);

            translate([0, 0, height-2])
                cylinder(d=diameter + 1, h=2, $fn=default_fn);
        }

        translate([-diameter/2, -0.5, height-1+epsilon]) {

            cube([diameter, 1, 1]);

            translate([diameter/2-0.5, -1, 0])
            cube([1, diameter, 1]);
        }
    }
}

module busbar_without_holes(width, height, depth, hole_count, hole_initial_offset, hole_diameter, hole_distance, screw_diameter, screw_length) {

    color("Gold") {

        cube([width, height, depth]);
    }

    for(i = [0:1:hole_count - 1]) {

        x = i * hole_distance + hole_initial_offset;

        translate([x, height / 2, depth])
            screw(screw_diameter, screw_length);
    }
}

module busbar_compartment(width, wall_thickness) {

    busbar_width = width - 2 * wall_thickness;
    busbar_thickness = 6;
    busbar_height = 9;
    busbar_hole_diameter = 5;
    busbar_screw_diameter = 3;
    busbar_screw_length = 7;
    busbar_hole_distance = 1000/170;
    height = busbar_height + wall_thickness;
    depth = busbar_thickness + 2*wall_thickness;

    hole_count = floor(busbar_width / busbar_hole_distance);
    busbar_width_remainder=busbar_width - (((hole_count - 1) * busbar_hole_distance) + busbar_hole_diameter);
    hole_initial_offset=busbar_width_remainder / 2 + busbar_hole_diameter / 2;

    echo("hole count = ", hole_count);

    difference() {

        union() {

            difference() {

                color("Gray") cube([width, depth, height]);

                translate([wall_thickness, wall_thickness, wall_thickness]) {

                    cube([busbar_width, busbar_thickness, busbar_height + epsilon]);
                }
            }

            if (draw_metal_elements) {

                translate([wall_thickness, wall_thickness, wall_thickness]) {

                    busbar_without_holes(busbar_width, busbar_thickness, busbar_height, hole_count, hole_initial_offset, busbar_hole_diameter, busbar_hole_distance, busbar_screw_diameter, busbar_screw_length);
                }
            }
        }

        translate([wall_thickness, -epsilon, wall_thickness]) {

            for(i = [0:1:hole_count - 1]) {

                x = i * busbar_hole_distance + hole_initial_offset;

                color("Red") {

                    translate([x, 0, depth / 2]) {

                        rotate([-90, 0, 0])

                            cylinder(d = busbar_hole_diameter, h=depth - wall_thickness, $fn=default_fn);
                    }
                }
            }
        }
    }
}

module busbars_assembly() {

    busbar_compartment_wall_thickness = 1;
    busbar_supporting_wall_thickness = 3;
    busbar_compartment_width = frame_width - cable_channel_width - (busbar_supporting_wall_thickness - busbar_compartment_wall_thickness);

    translate([busbar_supporting_wall_thickness - busbar_compartment_wall_thickness, 0, 0]) {

        translate([0, frame_height - 8, frame_depth - 20]) rotate([10, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
        translate([0, frame_height - 15, frame_depth - 40]) rotate([20, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
        translate([0, frame_height - 22, frame_depth - 60]) rotate([37, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
    }
}

busbars_assembly();


    Best regards, Piotr

2017-10-16 18:58 GMT+02:00 nop head <[hidden email]>:
All you need to do is define a module that transforms its children to the anchor point. Then however the object is placed you can transform to the anchor.

module anchored_cube() {
    cube(1, center=false);
}

module cube_anchor()
     translate([1,1,1])
         children();

translate([4, 5, 6]) rotate([13.7, 0, 90]) {
      anchored_cube(");
      cube_anchor()
           sphere(1);
}


For example I have a module to draw stepper motors with the origin at the base of the shaft.

module NEMA(motor);

I also have a module to represent the screw holes relative to its origin.

module NEMA_screw_positions(motor, n = 4);


In my design I will have a position for the motor, defined relative to the origin of the machine. I can use that to place the motor but I can also use it to drill the holes for it and place the screws. Nothing is repeated or calculated twice. Whatever transforms I use to place the motor are named modules or constants so I can apply them to the screw positions. I make a module to represent a motor bracket assembly that places the motor, its bracket and the fasteners and pulley. Then I place the assembly on another sub assembly, such as the X axis. That gets placed in the machine.

For something simple with one anchor point, like your example, or in my case a washer, I make the module that draws the washer translate any children to is top surface. So I can just do washer(M3_washer) screw(m3_screw);

So rather than make up a new language feature you just need to represent connection points with modules instead of strings.

I produce very modular and hierarchical designs with no manual positioning or repetition. Positions are only ever defined once and used everywhere.



On 16 October 2017 at 13:17, Piotr Wyderski <[hidden email]> wrote:
A very interesting library, but my proposal is unrelated to your anchor points, as I understand them. The essence is to have designated spatial locations referable from the outside, not very convenient local reference frames. It is
very easy to define such a named point in the module's reference frame, but then you quickly loose control about its location in the final reference frame due to all the garden-variety transformations that can be applied. In principle you can restore all that information using the well-known 4x4 transformation matrix approach, but this breaks the modular design approach (build more complex shapes by composing the more fundamental ones), as you need to implement it again next to the involved solids and then keep them in sync if something changes.
Say you have a complex shape defined by a module (a sequence of terminal block busbars) and want to bind three such module instances with supporting walls using an extruded polygon. It is then just insanely hard to tell where, say, the upper left corner of the second bar is in the "final" space, even though it is very easy to define it in the module's reference frame. And it is all I ask for: allow me to attach a symbolic name to such a designated point and then refer to this location from outside of the module, e.g. making it an item of the polygon's list. It is just a syntactic sugar, because the required transformation framework is already there and used for transforming every single vertex of the design. It "just" vastly simplifies compositions and eradicates an entire universe of bugs.

   Best regards, Piotr





2017-10-16 0:22 GMT+02:00 doug moen <[hidden email]>:
The Relativity library supports anchor points. Take a look and see if it satisfies your particular use cases.

On 15 October 2017 at 05:14, Piotr Wyderski <[hidden email]> wrote:
I propose to add the following syntactis sugar to greatly simplify compositions of complex modules.
It is desirable to decompose a complex project into a number of reusable modules and then connect
them at the top level. The problem is that it is extremely hard to define the correct point of interest
of a given module (say, the upper right corner of a box) after all the transformations applied to the module.
OpenSCAD contains all the necessary algebraic framework to calculate its position, but it directly breaks
the modularization principle, requiring a parallel 3D point calculator structure. The proposed extension
is to introduce named points which are both externally referable and undergo all the transformations
the rest of the module undergoes. For example:

 module anchored_cube(string prefix) {

     cube(1, center=false);
     point(prefix + "_" + "my_anchor", [1, 1, 1]);
 }

translate([4, 5, 6]) rotate([13.7, 0, 90]) anchored_cube("ac1");
translate(point("ac1_my_anchor")) sphere(r=1, center=true); // adds a sphere centered at the anchored_cube's reference frame [1,1,1], whereever it truly is.
  
The example is simple, but not very realistic, the main purpose of the named points is to be used in extruded polygons.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: Wish: named transformable points

nophead
This is how I would do it

busbar_thickness = 6;
busbar_height = 9;
function busbar_compartment_height(wall_thickness) = busbar_height + wall_thickness;
function busbar_compartment_depth(wall_thickness) = busbar_thickness + 2*wall_thickness;

module busbar_compartment(width, wall_thickness) {

    busbar_width = width - 2 * wall_thickness;
    busbar_hole_diameter = 5;
    busbar_screw_diameter = 3;
    busbar_screw_length = 7;
    busbar_hole_distance = 1000/170;
    height = busbar_compartment_height(wall_thickness);
    depth = busbar_compartment_depth(wall_thickness);

    hole_count = floor(busbar_width / busbar_hole_distance);
    busbar_width_remainder=busbar_width - (((hole_count - 1) * busbar_hole_distance) + busbar_hole_diameter);
    hole_initial_offset=busbar_width_remainder / 2 + busbar_hole_diameter / 2;

    echo("hole count = ", hole_count);

    difference() {
        union() {
            difference() {
                color("Gray") cube([width, depth, height]);
                translate([wall_thickness, wall_thickness, wall_thickness]) {
                    cube([busbar_width, busbar_thickness, busbar_height + epsilon]);
                }
            }

            if (draw_metal_elements) {
                translate([wall_thickness, wall_thickness, wall_thickness]) {
                    busbar_without_holes(busbar_width, busbar_thickness, busbar_height, hole_count, hole_initial_offset, busbar_hole_diameter, busbar_hole_distance, busbar_screw_diameter, busbar_screw_length);
                }
            }
        }

        translate([wall_thickness, -epsilon, wall_thickness]) {
            for(i = [0:1:hole_count - 1]) {
                x = i * busbar_hole_distance + hole_initial_offset;
                color("Red") {
                    translate([x, 0, depth / 2]) {
                        rotate([-90, 0, 0])
                            cylinder(d = busbar_hole_diameter, h=depth - wall_thickness, $fn=default_fn);
                    }
                }
            }
        }
    }
}

busbar_positions = [[-8, -20, 10], [-15, -40, 20], [-22, -60, 37]];

module busbar_positions()
    for(p = busbar_positions)
        translate([0, frame_height + p[0], frame_depth + p[1]])
            rotate([p[2], 0, 0])
                children();

module end_wall(wall_thickness, thickness)
    hull()
        busbar_positions()
            cube([thickness,busbar_compartment_depth(wall_thickness),
                busbar_compartment_height(wall_thickness)]);
   
module busbars_assembly() {
    end_width = 5;
    busbar_compartment_wall_thickness = 1;
    busbar_supporting_wall_thickness = 3;
    busbar_compartment_width = frame_width - cable_channel_width - (busbar_supporting_wall_thickness - busbar_compartment_wall_thickness);

    translate([busbar_supporting_wall_thickness - busbar_compartment_wall_thickness, 0, 0]) {
        busbar_positions()
            busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
       
        for(side = [0 : 1])
            translate([side * (busbar_compartment_width + end_width) - end_width, 0])
                color("lime") end_wall(busbar_compartment_wall_thickness, end_width);
    }
}

busbars_assembly();


I cheated by using hull() so I don't need the corner coordinates. I could have worked them out if I needed to by offsetting from the position array.

On 17 October 2017 at 14:32, Piotr Wyderski <[hidden email]> wrote:
Thank you very much for your explanation, but I still don't know how to scale this technique to a more complex design, so, for the sake of concreteness, below I included an excerpt from my project. You have three busbars there
and each busbar compartment is the atomic entity where the real modeling starts. Now I want to connect all the three smallest side faces with a supporting wall, defined in a separate module and made of a linearly extruded polygon, say of thickness 2. How am I going to find the exact spatial locations of the four corners of every face to put them on the polygon vertex list? With my named point approach I would have done exactly this: define these four vertices in the bar's reference frame (extremely easy, say they are (0,0), (0, 1), (1, 0) and (1, 1)), let them transform together with the  bar itself and then simply refer to them in the wall's module:

    linear_extrude(height=2, center=false) {
 
      polygon(points = ["bar1.upper_left", "bar1.lower_left", "bar2.upper_left",    "bar2.lower_left", ...
    }

Could you please show me a sketch of the solution using your technique?

default_fn=30;
epsilon=1e-2;
alot=1e2;

frame_width=199.5;
frame_height=140;
frame_depth=96;

cable_channel_width=30;
outside_compartment_height = 10;

draw_metal_elements=true;

module screw(diameter, height) {

    difference() {

        color("Silver") {

            cylinder(d=diameter, h=height-2, $fn=default_fn);

            translate([0, 0, height-2])
                cylinder(d=diameter + 1, h=2, $fn=default_fn);
        }

        translate([-diameter/2, -0.5, height-1+epsilon]) {

            cube([diameter, 1, 1]);

            translate([diameter/2-0.5, -1, 0])
            cube([1, diameter, 1]);
        }
    }
}

module busbar_without_holes(width, height, depth, hole_count, hole_initial_offset, hole_diameter, hole_distance, screw_diameter, screw_length) {

    color("Gold") {

        cube([width, height, depth]);
    }

    for(i = [0:1:hole_count - 1]) {

        x = i * hole_distance + hole_initial_offset;

        translate([x, height / 2, depth])
            screw(screw_diameter, screw_length);
    }
}

module busbar_compartment(width, wall_thickness) {

    busbar_width = width - 2 * wall_thickness;
    busbar_thickness = 6;
    busbar_height = 9;
    busbar_hole_diameter = 5;
    busbar_screw_diameter = 3;
    busbar_screw_length = 7;
    busbar_hole_distance = 1000/170;
    height = busbar_height + wall_thickness;
    depth = busbar_thickness + 2*wall_thickness;

    hole_count = floor(busbar_width / busbar_hole_distance);
    busbar_width_remainder=busbar_width - (((hole_count - 1) * busbar_hole_distance) + busbar_hole_diameter);
    hole_initial_offset=busbar_width_remainder / 2 + busbar_hole_diameter / 2;

    echo("hole count = ", hole_count);

    difference() {

        union() {

            difference() {

                color("Gray") cube([width, depth, height]);

                translate([wall_thickness, wall_thickness, wall_thickness]) {

                    cube([busbar_width, busbar_thickness, busbar_height + epsilon]);
                }
            }

            if (draw_metal_elements) {

                translate([wall_thickness, wall_thickness, wall_thickness]) {

                    busbar_without_holes(busbar_width, busbar_thickness, busbar_height, hole_count, hole_initial_offset, busbar_hole_diameter, busbar_hole_distance, busbar_screw_diameter, busbar_screw_length);
                }
            }
        }

        translate([wall_thickness, -epsilon, wall_thickness]) {

            for(i = [0:1:hole_count - 1]) {

                x = i * busbar_hole_distance + hole_initial_offset;

                color("Red") {

                    translate([x, 0, depth / 2]) {

                        rotate([-90, 0, 0])

                            cylinder(d = busbar_hole_diameter, h=depth - wall_thickness, $fn=default_fn);
                    }
                }
            }
        }
    }
}

module busbars_assembly() {

    busbar_compartment_wall_thickness = 1;
    busbar_supporting_wall_thickness = 3;
    busbar_compartment_width = frame_width - cable_channel_width - (busbar_supporting_wall_thickness - busbar_compartment_wall_thickness);

    translate([busbar_supporting_wall_thickness - busbar_compartment_wall_thickness, 0, 0]) {

        translate([0, frame_height - 8, frame_depth - 20]) rotate([10, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
        translate([0, frame_height - 15, frame_depth - 40]) rotate([20, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
        translate([0, frame_height - 22, frame_depth - 60]) rotate([37, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
    }
}

busbars_assembly();


    Best regards, Piotr

2017-10-16 18:58 GMT+02:00 nop head <[hidden email]>:
All you need to do is define a module that transforms its children to the anchor point. Then however the object is placed you can transform to the anchor.

module anchored_cube() {
    cube(1, center=false);
}

module cube_anchor()
     translate([1,1,1])
         children();

translate([4, 5, 6]) rotate([13.7, 0, 90]) {
      anchored_cube(");
      cube_anchor()
           sphere(1);
}


For example I have a module to draw stepper motors with the origin at the base of the shaft.

module NEMA(motor);

I also have a module to represent the screw holes relative to its origin.

module NEMA_screw_positions(motor, n = 4);


In my design I will have a position for the motor, defined relative to the origin of the machine. I can use that to place the motor but I can also use it to drill the holes for it and place the screws. Nothing is repeated or calculated twice. Whatever transforms I use to place the motor are named modules or constants so I can apply them to the screw positions. I make a module to represent a motor bracket assembly that places the motor, its bracket and the fasteners and pulley. Then I place the assembly on another sub assembly, such as the X axis. That gets placed in the machine.

For something simple with one anchor point, like your example, or in my case a washer, I make the module that draws the washer translate any children to is top surface. So I can just do washer(M3_washer) screw(m3_screw);

So rather than make up a new language feature you just need to represent connection points with modules instead of strings.

I produce very modular and hierarchical designs with no manual positioning or repetition. Positions are only ever defined once and used everywhere.



On 16 October 2017 at 13:17, Piotr Wyderski <[hidden email]> wrote:
A very interesting library, but my proposal is unrelated to your anchor points, as I understand them. The essence is to have designated spatial locations referable from the outside, not very convenient local reference frames. It is
very easy to define such a named point in the module's reference frame, but then you quickly loose control about its location in the final reference frame due to all the garden-variety transformations that can be applied. In principle you can restore all that information using the well-known 4x4 transformation matrix approach, but this breaks the modular design approach (build more complex shapes by composing the more fundamental ones), as you need to implement it again next to the involved solids and then keep them in sync if something changes.
Say you have a complex shape defined by a module (a sequence of terminal block busbars) and want to bind three such module instances with supporting walls using an extruded polygon. It is then just insanely hard to tell where, say, the upper left corner of the second bar is in the "final" space, even though it is very easy to define it in the module's reference frame. And it is all I ask for: allow me to attach a symbolic name to such a designated point and then refer to this location from outside of the module, e.g. making it an item of the polygon's list. It is just a syntactic sugar, because the required transformation framework is already there and used for transforming every single vertex of the design. It "just" vastly simplifies compositions and eradicates an entire universe of bugs.

   Best regards, Piotr





2017-10-16 0:22 GMT+02:00 doug moen <[hidden email]>:
The Relativity library supports anchor points. Take a look and see if it satisfies your particular use cases.

On 15 October 2017 at 05:14, Piotr Wyderski <[hidden email]> wrote:
I propose to add the following syntactis sugar to greatly simplify compositions of complex modules.
It is desirable to decompose a complex project into a number of reusable modules and then connect
them at the top level. The problem is that it is extremely hard to define the correct point of interest
of a given module (say, the upper right corner of a box) after all the transformations applied to the module.
OpenSCAD contains all the necessary algebraic framework to calculate its position, but it directly breaks
the modularization principle, requiring a parallel 3D point calculator structure. The proposed extension
is to introduce named points which are both externally referable and undergo all the transformations
the rest of the module undergoes. For example:

 module anchored_cube(string prefix) {

     cube(1, center=false);
     point(prefix + "_" + "my_anchor", [1, 1, 1]);
 }

translate([4, 5, 6]) rotate([13.7, 0, 90]) anchored_cube("ac1");
translate(point("ac1_my_anchor")) sphere(r=1, center=true); // adds a sphere centered at the anchored_cube's reference frame [1,1,1], whereever it truly is.
  
The example is simple, but not very realistic, the main purpose of the named points is to be used in extruded polygons.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: Wish: named transformable points

Piotr Wyderski
Thank you very much, this displays *exactly* what I wanted. Now I need to understand how you achieved this.

    Best regards, Piotr

2017-10-17 17:21 GMT+02:00 nop head <[hidden email]>:
This is how I would do it

busbar_thickness = 6;
busbar_height = 9;
function busbar_compartment_height(wall_thickness) = busbar_height + wall_thickness;
function busbar_compartment_depth(wall_thickness) = busbar_thickness + 2*wall_thickness;

module busbar_compartment(width, wall_thickness) {

    busbar_width = width - 2 * wall_thickness;
    busbar_hole_diameter = 5;
    busbar_screw_diameter = 3;
    busbar_screw_length = 7;
    busbar_hole_distance = 1000/170;
    height = busbar_compartment_height(wall_thickness);
    depth = busbar_compartment_depth(wall_thickness);

    hole_count = floor(busbar_width / busbar_hole_distance);
    busbar_width_remainder=busbar_width - (((hole_count - 1) * busbar_hole_distance) + busbar_hole_diameter);
    hole_initial_offset=busbar_width_remainder / 2 + busbar_hole_diameter / 2;

    echo("hole count = ", hole_count);

    difference() {
        union() {
            difference() {
                color("Gray") cube([width, depth, height]);
                translate([wall_thickness, wall_thickness, wall_thickness]) {
                    cube([busbar_width, busbar_thickness, busbar_height + epsilon]);
                }
            }

            if (draw_metal_elements) {
                translate([wall_thickness, wall_thickness, wall_thickness]) {
                    busbar_without_holes(busbar_width, busbar_thickness, busbar_height, hole_count, hole_initial_offset, busbar_hole_diameter, busbar_hole_distance, busbar_screw_diameter, busbar_screw_length);
                }
            }
        }

        translate([wall_thickness, -epsilon, wall_thickness]) {
            for(i = [0:1:hole_count - 1]) {
                x = i * busbar_hole_distance + hole_initial_offset;
                color("Red") {
                    translate([x, 0, depth / 2]) {
                        rotate([-90, 0, 0])
                            cylinder(d = busbar_hole_diameter, h=depth - wall_thickness, $fn=default_fn);
                    }
                }
            }
        }
    }
}

busbar_positions = [[-8, -20, 10], [-15, -40, 20], [-22, -60, 37]];

module busbar_positions()
    for(p = busbar_positions)
        translate([0, frame_height + p[0], frame_depth + p[1]])
            rotate([p[2], 0, 0])
                children();

module end_wall(wall_thickness, thickness)
    hull()
        busbar_positions()
            cube([thickness,busbar_compartment_depth(wall_thickness),
                busbar_compartment_height(wall_thickness)]);
   
module busbars_assembly() {
    end_width = 5;
    busbar_compartment_wall_thickness = 1;
    busbar_supporting_wall_thickness = 3;
    busbar_compartment_width = frame_width - cable_channel_width - (busbar_supporting_wall_thickness - busbar_compartment_wall_thickness);

    translate([busbar_supporting_wall_thickness - busbar_compartment_wall_thickness, 0, 0]) {
        busbar_positions()
            busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
       
        for(side = [0 : 1])
            translate([side * (busbar_compartment_width + end_width) - end_width, 0])
                color("lime") end_wall(busbar_compartment_wall_thickness, end_width);
    }
}

busbars_assembly();


I cheated by using hull() so I don't need the corner coordinates. I could have worked them out if I needed to by offsetting from the position array.

On 17 October 2017 at 14:32, Piotr Wyderski <[hidden email]> wrote:
Thank you very much for your explanation, but I still don't know how to scale this technique to a more complex design, so, for the sake of concreteness, below I included an excerpt from my project. You have three busbars there
and each busbar compartment is the atomic entity where the real modeling starts. Now I want to connect all the three smallest side faces with a supporting wall, defined in a separate module and made of a linearly extruded polygon, say of thickness 2. How am I going to find the exact spatial locations of the four corners of every face to put them on the polygon vertex list? With my named point approach I would have done exactly this: define these four vertices in the bar's reference frame (extremely easy, say they are (0,0), (0, 1), (1, 0) and (1, 1)), let them transform together with the  bar itself and then simply refer to them in the wall's module:

    linear_extrude(height=2, center=false) {
 
      polygon(points = ["bar1.upper_left", "bar1.lower_left", "bar2.upper_left",    "bar2.lower_left", ...
    }

Could you please show me a sketch of the solution using your technique?

default_fn=30;
epsilon=1e-2;
alot=1e2;

frame_width=199.5;
frame_height=140;
frame_depth=96;

cable_channel_width=30;
outside_compartment_height = 10;

draw_metal_elements=true;

module screw(diameter, height) {

    difference() {

        color("Silver") {

            cylinder(d=diameter, h=height-2, $fn=default_fn);

            translate([0, 0, height-2])
                cylinder(d=diameter + 1, h=2, $fn=default_fn);
        }

        translate([-diameter/2, -0.5, height-1+epsilon]) {

            cube([diameter, 1, 1]);

            translate([diameter/2-0.5, -1, 0])
            cube([1, diameter, 1]);
        }
    }
}

module busbar_without_holes(width, height, depth, hole_count, hole_initial_offset, hole_diameter, hole_distance, screw_diameter, screw_length) {

    color("Gold") {

        cube([width, height, depth]);
    }

    for(i = [0:1:hole_count - 1]) {

        x = i * hole_distance + hole_initial_offset;

        translate([x, height / 2, depth])
            screw(screw_diameter, screw_length);
    }
}

module busbar_compartment(width, wall_thickness) {

    busbar_width = width - 2 * wall_thickness;
    busbar_thickness = 6;
    busbar_height = 9;
    busbar_hole_diameter = 5;
    busbar_screw_diameter = 3;
    busbar_screw_length = 7;
    busbar_hole_distance = 1000/170;
    height = busbar_height + wall_thickness;
    depth = busbar_thickness + 2*wall_thickness;

    hole_count = floor(busbar_width / busbar_hole_distance);
    busbar_width_remainder=busbar_width - (((hole_count - 1) * busbar_hole_distance) + busbar_hole_diameter);
    hole_initial_offset=busbar_width_remainder / 2 + busbar_hole_diameter / 2;

    echo("hole count = ", hole_count);

    difference() {

        union() {

            difference() {

                color("Gray") cube([width, depth, height]);

                translate([wall_thickness, wall_thickness, wall_thickness]) {

                    cube([busbar_width, busbar_thickness, busbar_height + epsilon]);
                }
            }

            if (draw_metal_elements) {

                translate([wall_thickness, wall_thickness, wall_thickness]) {

                    busbar_without_holes(busbar_width, busbar_thickness, busbar_height, hole_count, hole_initial_offset, busbar_hole_diameter, busbar_hole_distance, busbar_screw_diameter, busbar_screw_length);
                }
            }
        }

        translate([wall_thickness, -epsilon, wall_thickness]) {

            for(i = [0:1:hole_count - 1]) {

                x = i * busbar_hole_distance + hole_initial_offset;

                color("Red") {

                    translate([x, 0, depth / 2]) {

                        rotate([-90, 0, 0])

                            cylinder(d = busbar_hole_diameter, h=depth - wall_thickness, $fn=default_fn);
                    }
                }
            }
        }
    }
}

module busbars_assembly() {

    busbar_compartment_wall_thickness = 1;
    busbar_supporting_wall_thickness = 3;
    busbar_compartment_width = frame_width - cable_channel_width - (busbar_supporting_wall_thickness - busbar_compartment_wall_thickness);

    translate([busbar_supporting_wall_thickness - busbar_compartment_wall_thickness, 0, 0]) {

        translate([0, frame_height - 8, frame_depth - 20]) rotate([10, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
        translate([0, frame_height - 15, frame_depth - 40]) rotate([20, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
        translate([0, frame_height - 22, frame_depth - 60]) rotate([37, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
    }
}

busbars_assembly();


    Best regards, Piotr

2017-10-16 18:58 GMT+02:00 nop head <[hidden email]>:
All you need to do is define a module that transforms its children to the anchor point. Then however the object is placed you can transform to the anchor.

module anchored_cube() {
    cube(1, center=false);
}

module cube_anchor()
     translate([1,1,1])
         children();

translate([4, 5, 6]) rotate([13.7, 0, 90]) {
      anchored_cube(");
      cube_anchor()
           sphere(1);
}


For example I have a module to draw stepper motors with the origin at the base of the shaft.

module NEMA(motor);

I also have a module to represent the screw holes relative to its origin.

module NEMA_screw_positions(motor, n = 4);


In my design I will have a position for the motor, defined relative to the origin of the machine. I can use that to place the motor but I can also use it to drill the holes for it and place the screws. Nothing is repeated or calculated twice. Whatever transforms I use to place the motor are named modules or constants so I can apply them to the screw positions. I make a module to represent a motor bracket assembly that places the motor, its bracket and the fasteners and pulley. Then I place the assembly on another sub assembly, such as the X axis. That gets placed in the machine.

For something simple with one anchor point, like your example, or in my case a washer, I make the module that draws the washer translate any children to is top surface. So I can just do washer(M3_washer) screw(m3_screw);

So rather than make up a new language feature you just need to represent connection points with modules instead of strings.

I produce very modular and hierarchical designs with no manual positioning or repetition. Positions are only ever defined once and used everywhere.



On 16 October 2017 at 13:17, Piotr Wyderski <[hidden email]> wrote:
A very interesting library, but my proposal is unrelated to your anchor points, as I understand them. The essence is to have designated spatial locations referable from the outside, not very convenient local reference frames. It is
very easy to define such a named point in the module's reference frame, but then you quickly loose control about its location in the final reference frame due to all the garden-variety transformations that can be applied. In principle you can restore all that information using the well-known 4x4 transformation matrix approach, but this breaks the modular design approach (build more complex shapes by composing the more fundamental ones), as you need to implement it again next to the involved solids and then keep them in sync if something changes.
Say you have a complex shape defined by a module (a sequence of terminal block busbars) and want to bind three such module instances with supporting walls using an extruded polygon. It is then just insanely hard to tell where, say, the upper left corner of the second bar is in the "final" space, even though it is very easy to define it in the module's reference frame. And it is all I ask for: allow me to attach a symbolic name to such a designated point and then refer to this location from outside of the module, e.g. making it an item of the polygon's list. It is just a syntactic sugar, because the required transformation framework is already there and used for transforming every single vertex of the design. It "just" vastly simplifies compositions and eradicates an entire universe of bugs.

   Best regards, Piotr





2017-10-16 0:22 GMT+02:00 doug moen <[hidden email]>:
The Relativity library supports anchor points. Take a look and see if it satisfies your particular use cases.

On 15 October 2017 at 05:14, Piotr Wyderski <[hidden email]> wrote:
I propose to add the following syntactis sugar to greatly simplify compositions of complex modules.
It is desirable to decompose a complex project into a number of reusable modules and then connect
them at the top level. The problem is that it is extremely hard to define the correct point of interest
of a given module (say, the upper right corner of a box) after all the transformations applied to the module.
OpenSCAD contains all the necessary algebraic framework to calculate its position, but it directly breaks
the modularization principle, requiring a parallel 3D point calculator structure. The proposed extension
is to introduce named points which are both externally referable and undergo all the transformations
the rest of the module undergoes. For example:

 module anchored_cube(string prefix) {

     cube(1, center=false);
     point(prefix + "_" + "my_anchor", [1, 1, 1]);
 }

translate([4, 5, 6]) rotate([13.7, 0, 90]) anchored_cube("ac1");
translate(point("ac1_my_anchor")) sphere(r=1, center=true); // adds a sphere centered at the anchored_cube's reference frame [1,1,1], whereever it truly is.
  
The example is simple, but not very realistic, the main purpose of the named points is to be used in extruded polygons.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: Wish: named transformable points

Piotr Wyderski
In reply to this post by nophead
I love the trick with hull, an extremely powerful generic technique I didn't know of. Disabling it shows the transformation technique you were talking about. Now I see how it works, brilliant. This solves my real problem, thank you Nop.

On the other hand I still have the feeling that OpenSCAD is lacking something extremely important. I see no reason for not having access
to the global transformation matrix and module instance attributes.
That would allow me to model things using the way of thinking which
is most natural to me. There is no point in rewriting OpenSCAD, especially its rendering engine, but I think I should write a translator which would
emit *.scad files from my own 3D modeling language, i.e. what SolidPython does
to implement permanent holes. The resulting scad files don't have to be human-readable, so I could bolt down the results of algebraic transformations directly.

    Best regards, Piotr



2017-10-17 17:21 GMT+02:00 nop head <[hidden email]>:
This is how I would do it

busbar_thickness = 6;
busbar_height = 9;
function busbar_compartment_height(wall_thickness) = busbar_height + wall_thickness;
function busbar_compartment_depth(wall_thickness) = busbar_thickness + 2*wall_thickness;

module busbar_compartment(width, wall_thickness) {

    busbar_width = width - 2 * wall_thickness;
    busbar_hole_diameter = 5;
    busbar_screw_diameter = 3;
    busbar_screw_length = 7;
    busbar_hole_distance = 1000/170;
    height = busbar_compartment_height(wall_thickness);
    depth = busbar_compartment_depth(wall_thickness);

    hole_count = floor(busbar_width / busbar_hole_distance);
    busbar_width_remainder=busbar_width - (((hole_count - 1) * busbar_hole_distance) + busbar_hole_diameter);
    hole_initial_offset=busbar_width_remainder / 2 + busbar_hole_diameter / 2;

    echo("hole count = ", hole_count);

    difference() {
        union() {
            difference() {
                color("Gray") cube([width, depth, height]);
                translate([wall_thickness, wall_thickness, wall_thickness]) {
                    cube([busbar_width, busbar_thickness, busbar_height + epsilon]);
                }
            }

            if (draw_metal_elements) {
                translate([wall_thickness, wall_thickness, wall_thickness]) {
                    busbar_without_holes(busbar_width, busbar_thickness, busbar_height, hole_count, hole_initial_offset, busbar_hole_diameter, busbar_hole_distance, busbar_screw_diameter, busbar_screw_length);
                }
            }
        }

        translate([wall_thickness, -epsilon, wall_thickness]) {
            for(i = [0:1:hole_count - 1]) {
                x = i * busbar_hole_distance + hole_initial_offset;
                color("Red") {
                    translate([x, 0, depth / 2]) {
                        rotate([-90, 0, 0])
                            cylinder(d = busbar_hole_diameter, h=depth - wall_thickness, $fn=default_fn);
                    }
                }
            }
        }
    }
}

busbar_positions = [[-8, -20, 10], [-15, -40, 20], [-22, -60, 37]];

module busbar_positions()
    for(p = busbar_positions)
        translate([0, frame_height + p[0], frame_depth + p[1]])
            rotate([p[2], 0, 0])
                children();

module end_wall(wall_thickness, thickness)
    hull()
        busbar_positions()
            cube([thickness,busbar_compartment_depth(wall_thickness),
                busbar_compartment_height(wall_thickness)]);
   
module busbars_assembly() {
    end_width = 5;
    busbar_compartment_wall_thickness = 1;
    busbar_supporting_wall_thickness = 3;
    busbar_compartment_width = frame_width - cable_channel_width - (busbar_supporting_wall_thickness - busbar_compartment_wall_thickness);

    translate([busbar_supporting_wall_thickness - busbar_compartment_wall_thickness, 0, 0]) {
        busbar_positions()
            busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
       
        for(side = [0 : 1])
            translate([side * (busbar_compartment_width + end_width) - end_width, 0])
                color("lime") end_wall(busbar_compartment_wall_thickness, end_width);
    }
}

busbars_assembly();


I cheated by using hull() so I don't need the corner coordinates. I could have worked them out if I needed to by offsetting from the position array.

On 17 October 2017 at 14:32, Piotr Wyderski <[hidden email]> wrote:
Thank you very much for your explanation, but I still don't know how to scale this technique to a more complex design, so, for the sake of concreteness, below I included an excerpt from my project. You have three busbars there
and each busbar compartment is the atomic entity where the real modeling starts. Now I want to connect all the three smallest side faces with a supporting wall, defined in a separate module and made of a linearly extruded polygon, say of thickness 2. How am I going to find the exact spatial locations of the four corners of every face to put them on the polygon vertex list? With my named point approach I would have done exactly this: define these four vertices in the bar's reference frame (extremely easy, say they are (0,0), (0, 1), (1, 0) and (1, 1)), let them transform together with the  bar itself and then simply refer to them in the wall's module:

    linear_extrude(height=2, center=false) {
 
      polygon(points = ["bar1.upper_left", "bar1.lower_left", "bar2.upper_left",    "bar2.lower_left", ...
    }

Could you please show me a sketch of the solution using your technique?

default_fn=30;
epsilon=1e-2;
alot=1e2;

frame_width=199.5;
frame_height=140;
frame_depth=96;

cable_channel_width=30;
outside_compartment_height = 10;

draw_metal_elements=true;

module screw(diameter, height) {

    difference() {

        color("Silver") {

            cylinder(d=diameter, h=height-2, $fn=default_fn);

            translate([0, 0, height-2])
                cylinder(d=diameter + 1, h=2, $fn=default_fn);
        }

        translate([-diameter/2, -0.5, height-1+epsilon]) {

            cube([diameter, 1, 1]);

            translate([diameter/2-0.5, -1, 0])
            cube([1, diameter, 1]);
        }
    }
}

module busbar_without_holes(width, height, depth, hole_count, hole_initial_offset, hole_diameter, hole_distance, screw_diameter, screw_length) {

    color("Gold") {

        cube([width, height, depth]);
    }

    for(i = [0:1:hole_count - 1]) {

        x = i * hole_distance + hole_initial_offset;

        translate([x, height / 2, depth])
            screw(screw_diameter, screw_length);
    }
}

module busbar_compartment(width, wall_thickness) {

    busbar_width = width - 2 * wall_thickness;
    busbar_thickness = 6;
    busbar_height = 9;
    busbar_hole_diameter = 5;
    busbar_screw_diameter = 3;
    busbar_screw_length = 7;
    busbar_hole_distance = 1000/170;
    height = busbar_height + wall_thickness;
    depth = busbar_thickness + 2*wall_thickness;

    hole_count = floor(busbar_width / busbar_hole_distance);
    busbar_width_remainder=busbar_width - (((hole_count - 1) * busbar_hole_distance) + busbar_hole_diameter);
    hole_initial_offset=busbar_width_remainder / 2 + busbar_hole_diameter / 2;

    echo("hole count = ", hole_count);

    difference() {

        union() {

            difference() {

                color("Gray") cube([width, depth, height]);

                translate([wall_thickness, wall_thickness, wall_thickness]) {

                    cube([busbar_width, busbar_thickness, busbar_height + epsilon]);
                }
            }

            if (draw_metal_elements) {

                translate([wall_thickness, wall_thickness, wall_thickness]) {

                    busbar_without_holes(busbar_width, busbar_thickness, busbar_height, hole_count, hole_initial_offset, busbar_hole_diameter, busbar_hole_distance, busbar_screw_diameter, busbar_screw_length);
                }
            }
        }

        translate([wall_thickness, -epsilon, wall_thickness]) {

            for(i = [0:1:hole_count - 1]) {

                x = i * busbar_hole_distance + hole_initial_offset;

                color("Red") {

                    translate([x, 0, depth / 2]) {

                        rotate([-90, 0, 0])

                            cylinder(d = busbar_hole_diameter, h=depth - wall_thickness, $fn=default_fn);
                    }
                }
            }
        }
    }
}

module busbars_assembly() {

    busbar_compartment_wall_thickness = 1;
    busbar_supporting_wall_thickness = 3;
    busbar_compartment_width = frame_width - cable_channel_width - (busbar_supporting_wall_thickness - busbar_compartment_wall_thickness);

    translate([busbar_supporting_wall_thickness - busbar_compartment_wall_thickness, 0, 0]) {

        translate([0, frame_height - 8, frame_depth - 20]) rotate([10, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
        translate([0, frame_height - 15, frame_depth - 40]) rotate([20, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
        translate([0, frame_height - 22, frame_depth - 60]) rotate([37, 0, 0]) busbar_compartment(busbar_compartment_width, busbar_compartment_wall_thickness);
    }
}

busbars_assembly();


    Best regards, Piotr

2017-10-16 18:58 GMT+02:00 nop head <[hidden email]>:
All you need to do is define a module that transforms its children to the anchor point. Then however the object is placed you can transform to the anchor.

module anchored_cube() {
    cube(1, center=false);
}

module cube_anchor()
     translate([1,1,1])
         children();

translate([4, 5, 6]) rotate([13.7, 0, 90]) {
      anchored_cube(");
      cube_anchor()
           sphere(1);
}


For example I have a module to draw stepper motors with the origin at the base of the shaft.

module NEMA(motor);

I also have a module to represent the screw holes relative to its origin.

module NEMA_screw_positions(motor, n = 4);


In my design I will have a position for the motor, defined relative to the origin of the machine. I can use that to place the motor but I can also use it to drill the holes for it and place the screws. Nothing is repeated or calculated twice. Whatever transforms I use to place the motor are named modules or constants so I can apply them to the screw positions. I make a module to represent a motor bracket assembly that places the motor, its bracket and the fasteners and pulley. Then I place the assembly on another sub assembly, such as the X axis. That gets placed in the machine.

For something simple with one anchor point, like your example, or in my case a washer, I make the module that draws the washer translate any children to is top surface. So I can just do washer(M3_washer) screw(m3_screw);

So rather than make up a new language feature you just need to represent connection points with modules instead of strings.

I produce very modular and hierarchical designs with no manual positioning or repetition. Positions are only ever defined once and used everywhere.



On 16 October 2017 at 13:17, Piotr Wyderski <[hidden email]> wrote:
A very interesting library, but my proposal is unrelated to your anchor points, as I understand them. The essence is to have designated spatial locations referable from the outside, not very convenient local reference frames. It is
very easy to define such a named point in the module's reference frame, but then you quickly loose control about its location in the final reference frame due to all the garden-variety transformations that can be applied. In principle you can restore all that information using the well-known 4x4 transformation matrix approach, but this breaks the modular design approach (build more complex shapes by composing the more fundamental ones), as you need to implement it again next to the involved solids and then keep them in sync if something changes.
Say you have a complex shape defined by a module (a sequence of terminal block busbars) and want to bind three such module instances with supporting walls using an extruded polygon. It is then just insanely hard to tell where, say, the upper left corner of the second bar is in the "final" space, even though it is very easy to define it in the module's reference frame. And it is all I ask for: allow me to attach a symbolic name to such a designated point and then refer to this location from outside of the module, e.g. making it an item of the polygon's list. It is just a syntactic sugar, because the required transformation framework is already there and used for transforming every single vertex of the design. It "just" vastly simplifies compositions and eradicates an entire universe of bugs.

   Best regards, Piotr





2017-10-16 0:22 GMT+02:00 doug moen <[hidden email]>:
The Relativity library supports anchor points. Take a look and see if it satisfies your particular use cases.

On 15 October 2017 at 05:14, Piotr Wyderski <[hidden email]> wrote:
I propose to add the following syntactis sugar to greatly simplify compositions of complex modules.
It is desirable to decompose a complex project into a number of reusable modules and then connect
them at the top level. The problem is that it is extremely hard to define the correct point of interest
of a given module (say, the upper right corner of a box) after all the transformations applied to the module.
OpenSCAD contains all the necessary algebraic framework to calculate its position, but it directly breaks
the modularization principle, requiring a parallel 3D point calculator structure. The proposed extension
is to introduce named points which are both externally referable and undergo all the transformations
the rest of the module undergoes. For example:

 module anchored_cube(string prefix) {

     cube(1, center=false);
     point(prefix + "_" + "my_anchor", [1, 1, 1]);
 }

translate([4, 5, 6]) rotate([13.7, 0, 90]) anchored_cube("ac1");
translate(point("ac1_my_anchor")) sphere(r=1, center=true); // adds a sphere centered at the anchored_cube's reference frame [1,1,1], whereever it truly is.
  
The example is simple, but not very realistic, the main purpose of the named points is to be used in extruded polygons.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org