discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Wish: named transformable points

PW
Piotr Wyderski
Sun, Oct 15, 2017 9:14 AM

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.

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.
DM
doug moen
Sun, Oct 15, 2017 10:22 PM

The Relativity library supports anchor points. Take a look and see if it
satisfies your particular use cases.
https://github.com/davidson16807/relativity.scad/wiki

On 15 October 2017 at 05:14, Piotr Wyderski piotr.wyderski@gmail.com
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
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

The Relativity library supports anchor points. Take a look and see if it satisfies your particular use cases. https://github.com/davidson16807/relativity.scad/wiki On 15 October 2017 at 05:14, Piotr Wyderski <piotr.wyderski@gmail.com> 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 > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
PW
Piotr Wyderski
Mon, Oct 16, 2017 12:17 PM

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 doug@moens.org:

The Relativity library supports anchor points. Take a look and see if it
satisfies your particular use cases.
https://github.com/davidson16807/relativity.scad/wiki

On 15 October 2017 at 05:14, Piotr Wyderski piotr.wyderski@gmail.com
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
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

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 <doug@moens.org>: > The Relativity library supports anchor points. Take a look and see if it > satisfies your particular use cases. > https://github.com/davidson16807/relativity.scad/wiki > > On 15 October 2017 at 05:14, Piotr Wyderski <piotr.wyderski@gmail.com> > 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 >> Discuss@lists.openscad.org >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> >> > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
NH
nop head
Mon, Oct 16, 2017 4:58 PM

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 piotr.wyderski@gmail.com
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 doug@moens.org:

The Relativity library supports anchor points. Take a look and see if it
satisfies your particular use cases.
https://github.com/davidson16807/relativity.scad/wiki

On 15 October 2017 at 05:14, Piotr Wyderski piotr.wyderski@gmail.com
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
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

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 <piotr.wyderski@gmail.com> 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 <doug@moens.org>: > >> The Relativity library supports anchor points. Take a look and see if it >> satisfies your particular use cases. >> https://github.com/davidson16807/relativity.scad/wiki >> >> On 15 October 2017 at 05:14, Piotr Wyderski <piotr.wyderski@gmail.com> >> 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 >>> Discuss@lists.openscad.org >>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>> >>> >> >> _______________________________________________ >> OpenSCAD mailing list >> Discuss@lists.openscad.org >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> >> > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
PW
Piotr Wyderski
Tue, Oct 17, 2017 1:32 PM

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 nop.head@gmail.com:

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 piotr.wyderski@gmail.com
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 doug@moens.org:

The Relativity library supports anchor points. Take a look and see if it
satisfies your particular use cases.
https://github.com/davidson16807/relativity.scad/wiki

On 15 October 2017 at 05:14, Piotr Wyderski piotr.wyderski@gmail.com
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
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

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 <nop.head@gmail.com>: > 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 <piotr.wyderski@gmail.com> > 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 <doug@moens.org>: >> >>> The Relativity library supports anchor points. Take a look and see if it >>> satisfies your particular use cases. >>> https://github.com/davidson16807/relativity.scad/wiki >>> >>> On 15 October 2017 at 05:14, Piotr Wyderski <piotr.wyderski@gmail.com> >>> 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 >>>> Discuss@lists.openscad.org >>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>> >>>> >>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> Discuss@lists.openscad.org >>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>> >>> >> >> _______________________________________________ >> OpenSCAD mailing list >> Discuss@lists.openscad.org >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> >> > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
NH
nop head
Tue, Oct 17, 2017 3:21 PM

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 piotr.wyderski@gmail.com
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 nop.head@gmail.com:

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 piotr.wyderski@gmail.com
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 doug@moens.org:

The Relativity library supports anchor points. Take a look and see if
it satisfies your particular use cases.
https://github.com/davidson16807/relativity.scad/wiki

On 15 October 2017 at 05:14, Piotr Wyderski piotr.wyderski@gmail.com
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
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

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 <piotr.wyderski@gmail.com> 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 <nop.head@gmail.com>: > >> 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 <piotr.wyderski@gmail.com> >> 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 <doug@moens.org>: >>> >>>> The Relativity library supports anchor points. Take a look and see if >>>> it satisfies your particular use cases. >>>> https://github.com/davidson16807/relativity.scad/wiki >>>> >>>> On 15 October 2017 at 05:14, Piotr Wyderski <piotr.wyderski@gmail.com> >>>> 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 >>>>> Discuss@lists.openscad.org >>>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>>> >>>>> >>>> >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> Discuss@lists.openscad.org >>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>> >>>> >>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> Discuss@lists.openscad.org >>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>> >>> >> >> _______________________________________________ >> OpenSCAD mailing list >> Discuss@lists.openscad.org >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> >> > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
PW
Piotr Wyderski
Tue, Oct 17, 2017 3:30 PM

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 nop.head@gmail.com:

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 piotr.wyderski@gmail.com
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 nop.head@gmail.com:

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 piotr.wyderski@gmail.com
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 doug@moens.org:

The Relativity library supports anchor points. Take a look and see if
it satisfies your particular use cases.
https://github.com/davidson16807/relativity.scad/wiki

On 15 October 2017 at 05:14, Piotr Wyderski piotr.wyderski@gmail.com
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
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

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 <nop.head@gmail.com>: > 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 <piotr.wyderski@gmail.com> > 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 <nop.head@gmail.com>: >> >>> 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 <piotr.wyderski@gmail.com> >>> 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 <doug@moens.org>: >>>> >>>>> The Relativity library supports anchor points. Take a look and see if >>>>> it satisfies your particular use cases. >>>>> https://github.com/davidson16807/relativity.scad/wiki >>>>> >>>>> On 15 October 2017 at 05:14, Piotr Wyderski <piotr.wyderski@gmail.com> >>>>> 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 >>>>>> Discuss@lists.openscad.org >>>>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>>>> >>>>>> >>>>> >>>>> _______________________________________________ >>>>> OpenSCAD mailing list >>>>> Discuss@lists.openscad.org >>>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>>> >>>>> >>>> >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> Discuss@lists.openscad.org >>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>> >>>> >>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> Discuss@lists.openscad.org >>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>> >>> >> >> _______________________________________________ >> OpenSCAD mailing list >> Discuss@lists.openscad.org >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> >> > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
PW
Piotr Wyderski
Tue, Oct 17, 2017 4:05 PM

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 nop.head@gmail.com:

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 piotr.wyderski@gmail.com
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 nop.head@gmail.com:

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 piotr.wyderski@gmail.com
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 doug@moens.org:

The Relativity library supports anchor points. Take a look and see if
it satisfies your particular use cases.
https://github.com/davidson16807/relativity.scad/wiki

On 15 October 2017 at 05:14, Piotr Wyderski piotr.wyderski@gmail.com
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
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

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 <nop.head@gmail.com>: > 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 <piotr.wyderski@gmail.com> > 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 <nop.head@gmail.com>: >> >>> 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 <piotr.wyderski@gmail.com> >>> 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 <doug@moens.org>: >>>> >>>>> The Relativity library supports anchor points. Take a look and see if >>>>> it satisfies your particular use cases. >>>>> https://github.com/davidson16807/relativity.scad/wiki >>>>> >>>>> On 15 October 2017 at 05:14, Piotr Wyderski <piotr.wyderski@gmail.com> >>>>> 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 >>>>>> Discuss@lists.openscad.org >>>>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>>>> >>>>>> >>>>> >>>>> _______________________________________________ >>>>> OpenSCAD mailing list >>>>> Discuss@lists.openscad.org >>>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>>> >>>>> >>>> >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> Discuss@lists.openscad.org >>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>>> >>>> >>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> Discuss@lists.openscad.org >>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >>> >>> >> >> _______________________________________________ >> OpenSCAD mailing list >> Discuss@lists.openscad.org >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> >> > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >