How to solve withouts classic variables?

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

How to solve withouts classic variables?

janek

Hello,

is there a way to solve this problem without classic variables?

The setup is (simplified for this example): In a vector I have a number of vectors that define the dimesions of boxes (length, height). The width for all boxes is fixed, and all these boxes should be placed one immedialety after another, however, when the total length exceeds a given maxWidth, the starting x-position should be reset to zero and y should be increased by width.

It is a similar problem to e.g. typesetting words aligned to left

In a "normal" scripting language I'd do something like


width = 10;
maxWidth = 50;
boxes = [ [10,10], [25,5], [5,15], [20, 5], … ];

// this should produce first "line" from the boxes long 10 + 25 + 5
// and then, the fourth box should "jump" to second line

currentX = 0;
currentY = 0;
for (box = boxes) {
  if (currentX + box[0] > maxWidth) {
    currentX = 0;
    currentY = currentY + width;
  }
  
  translate([currentX, currentY, 0])
    cube([box[0], width, box[1]);

  currentX = currentX + box[0];
}

But because of the weird behaviour of the non-variable variables in OpenSCAD I'm stuck.

Any hint for me? Many thanks.



Sent from the OpenSCAD mailing list archive at Nabble.com.

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

Re: How to solve withouts classic variables?

boxcarmib
Personally, i’d probably uses a recursive function like this:

width = 10;
maxWidth = 50;
boxes = [ [10,10], [25,5], [5,15], [20, 5], [10,10], [25,5], [5,15], [20, 5], [10,10], [25,5], [5,15], [20, 5]];

function justify(width = 0, index = 0, boxesInLine = []) =
(index >= len(boxes)) || (width + boxes[index].x > maxWidth)
? [boxesInLine, width]
: justify( concat(boxesInLine, index), width + boxes[index].x, index + 1)
;
echo(justify());


On Jun 23, 2020, at 2:57 PM, janek <[hidden email]> wrote:

Hello,

is there a way to solve this problem without classic variables?

The setup is (simplified for this example): In a vector I have a number of vectors that define the dimesions of boxes (length, height). The width for all boxes is fixed, and all these boxes should be placed one immedialety after another, however, when the total length exceeds a given maxWidth, the starting x-position should be reset to zero and y should be increased by width.

It is a similar problem to e.g. typesetting words aligned to left

In a "normal" scripting language I'd do something like


width = 10;
maxWidth = 50;
boxes = [ [10,10], [25,5], [5,15], [20, 5], … ];

// this should produce first "line" from the boxes long 10 + 25 + 5
// and then, the fourth box should "jump" to second line

currentX = 0;
currentY = 0;
for (box = boxes) {
  if (currentX + box[0] > maxWidth) {
    currentX = 0;
    currentY = currentY + width;
  }
  
  translate([currentX, currentY, 0])
    cube([box[0], width, box[1]);

  currentX = currentX + box[0];
}

But because of the weird behaviour of the non-variable variables in OpenSCAD I'm stuck.

Any hint for me? Many thanks.



Sent from the OpenSCAD mailing list archive at Nabble.com.
_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org


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

Re: How to solve withouts classic variables?

Gadgetman!
In reply to this post by janek
I had a similar issue recently...   

What you're trying is to fit a 1 dimensional problem into a 2 dimensional map. 

The currentX and CurrentY may look like a good idea, but...   You only need ONE of them. 
CurrentX

Just add up the CurrentX, then divide by Maxwidth, an multiply the integer result by the width to et 'CurrentY.
The proper CurrentX position is the modulo of CurrentX and Maxwidth.

translate([CurrentX%maxwidth, floor(CurrentX/Maxwidth, 0])

Something like that, at least...   

Why do things complicated?

;-)
Trygve

On 23 June 2020 at 23:57:33 +02:00, janek <[hidden email]> wrote:

Hello,

is there a way to solve this problem without classic variables?

The setup is (simplified for this example): In a vector I have a number of vectors that define the dimesions of boxes (length, height). The width for all boxes is fixed, and all these boxes should be placed one immedialety after another, however, when the total length exceeds a given maxWidth, the starting x-position should be reset to zero and y should be increased by width.

It is a similar problem to e.g. typesetting words aligned to left

In a "normal" scripting language I'd do something like


width = 10;
maxWidth = 50;
boxes = [ [10,10], [25,5], [5,15], [20, 5], … ];

// this should produce first "line" from the boxes long 10 + 25 + 5
// and then, the fourth box should "jump" to second line

currentX = 0;
currentY = 0;
for (box = boxes) {
  if (currentX + box[0] > maxWidth) {
    currentX = 0;
    currentY = currentY + width;
  }
  
  translate([currentX, currentY, 0])
    cube([box[0], width, box[1]);

  currentX = currentX + box[0];
}

But because of the weird behaviour of the non-variable variables in OpenSCAD I'm stuck.

Any hint for me? Many thanks.



Sent from theOpenSCAD mailing list archive at Nabble.com.
_______________________________________________
OpenSCAD mailing list


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

Re: How to solve withouts classic variables?

janek
In reply to this post by boxcarmib

Thanks,

I must confess, I came to a similar solution. However, since my programming experience tells me that recursion is a powerful tool with huge costs (typically memory and performance killer), suffering also from call stack size limitation (even though it is not my case yet, would I be able to process something, that would produce a matrix of ca. 100 x 200 elements using recursion?), I was wondering if there is a better method.

Jan
Dne 24.06.2020 v 0:22 Hugo Jackson napsal(a):
Personally, i’d probably uses a recursive function like this:

width = 10;
maxWidth = 50;
boxes = [ [10,10], [25,5], [5,15], [20, 5], [10,10], [25,5], [5,15], [20, 5], [10,10], [25,5], [5,15], [20, 5]];

function justify(width = 0, index = 0, boxesInLine = []) =
(index >= len(boxes)) || (width + boxes[index].x > maxWidth)
? [boxesInLine, width]
: justify( concat(boxesInLine, index), width + boxes[index].x, index + 1)
;
echo(justify());


On Jun 23, 2020, at 2:57 PM, janek <[hidden email]> wrote:

Hello,

is there a way to solve this problem without classic variables?

The setup is (simplified for this example): In a vector I have a number of vectors that define the dimesions of boxes (length, height). The width for all boxes is fixed, and all these boxes should be placed one immedialety after another, however, when the total length exceeds a given maxWidth, the starting x-position should be reset to zero and y should be increased by width.

It is a similar problem to e.g. typesetting words aligned to left

In a "normal" scripting language I'd do something like


width = 10;
maxWidth = 50;
boxes = [ [10,10], [25,5], [5,15], [20, 5], … ];

// this should produce first "line" from the boxes long 10 + 25 + 5
// and then, the fourth box should "jump" to second line

currentX = 0;
currentY = 0;
for (box = boxes) {
  if (currentX + box[0] > maxWidth) {
    currentX = 0;
    currentY = currentY + width;
  }
  
  translate([currentX, currentY, 0])
    cube([box[0], width, box[1]);

  currentX = currentX + box[0];
}

But because of the weird behaviour of the non-variable variables in OpenSCAD I'm stuck.

Any hint for me? Many thanks.



Sent from the OpenSCAD mailing list archive at Nabble.com.
_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org


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

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

Re: How to solve withouts classic variables?

janek
In reply to this post by Gadgetman!

Thank you,

Dne 24.06.2020 v 0:50 [hidden email] napsal(a):

What you're trying is to fit a 1 dimensional problem into a 2 dimensional map. 

That's exactly my problem
The currentX and CurrentY may look like a good idea, but...   You only need ONE of them. 
CurrentX

Just add up the CurrentX, then divide by Maxwidth, an multiply the integer result by the width to et 'CurrentY.
The proper CurrentX position is the modulo of CurrentX and Maxwidth.

translate([CurrentX%maxwidth, floor(CurrentX/Maxwidth, 0])

Something like that, at least...  

This is what I do for fixed-size boxes (I started with those). I can simply loop through the vector and calculate current position using method similar to described. However, here, I cannot calculate "statically" currentX and I cannot find the way to repeatedly add to the currentX inside the loop - at least without recursion.

Also, using this method, would boxes be aligned properly? If I understand it, then not. It is not given that the sum of lengths on first line would be divisible by MaxWidth and then all further rows would be shifted, wouldn't they?

But yes, that could have been solved by temporal calculation of "remaining space" and another addition to currentX - in case I had a method of summing/incrementing a variable inside a loop.


Why do things complicated?

My words. I love the idea of OpenScad. I bang my forehead to the closest wall almost every time I try to do something in a normal (for a person with some experience with common programming languages), readable or elegant way and call out a loud WHY! :)

Jan


;-)
Trygve

On 23 June 2020 at 23:57:33 +02:00, janek [hidden email] wrote:

Hello,

is there a way to solve this problem without classic variables?

The setup is (simplified for this example): In a vector I have a number of vectors that define the dimesions of boxes (length, height). The width for all boxes is fixed, and all these boxes should be placed one immedialety after another, however, when the total length exceeds a given maxWidth, the starting x-position should be reset to zero and y should be increased by width.

It is a similar problem to e.g. typesetting words aligned to left

In a "normal" scripting language I'd do something like


width = 10;
maxWidth = 50;
boxes = [ [10,10], [25,5], [5,15], [20, 5], … ];

// this should produce first "line" from the boxes long 10 + 25 + 5
// and then, the fourth box should "jump" to second line

currentX = 0;
currentY = 0;
for (box = boxes) {
  if (currentX + box[0] > maxWidth) {
    currentX = 0;
    currentY = currentY + width;
  }
  
  translate([currentX, currentY, 0])
    cube([box[0], width, box[1]);

  currentX = currentX + box[0];
}

But because of the weird behaviour of the non-variable variables in OpenSCAD I'm stuck.

Any hint for me? Many thanks.



Sent from theOpenSCAD mailing list archive at Nabble.com.
_______________________________________________
OpenSCAD mailing list


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

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

Re: How to solve withouts classic variables?

Gadgetman!
Hi!


Also, using this method, would boxes be aligned properly? If I understand it, then not. It is not given that the sum of lengths on first line would be divisible by MaxWidth and then all further rows would be shifted, wouldn't they?


But yes, that could have been solved by temporal calculation of "remaining space" and another addition to currentX - in case I had a method of summing/incrementing a variable inside a loop.


You're right. That was a mistake on my part. 

I believe it can be solved by adding an IF clause and an addition, but yeah, I'm on vacation right now, it's midday and I haven't even had breakfast yet. My brain probably won't work properly for at least a couple of weeks more....   

Trygve, or possibly a multiplication

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

Re: How to solve withouts classic variables?

tp3
This seems to work. Doing the calculation before starting
to generate geometry makes it easier to debug and also
there's not really a problem regarding the recursion as
pretty much any list that's too long at this point will
likely be impossible to render later anyway due to number
of vertices generated.

The commented out generator will make 10001 objects which
takes about a second to preview on my notebook.

maxWidth = 9;
o = [
    [3, "gray"],
    [1, "lightblue"],
    [4, "orange"],
    [2, "indigo"],
    [2, "violet"],
    [3, "yellow"],
    [5, "green"]
];

/*
maxWidth = 300;
r = rands(1, 10, pow(maxWidth / 3, 2) + 3);
o = [ for (i = [0 : len(r) - 3]) [ r[i], [ r[i + 1], r[i + 2], r[i + 3] ] / 10] ];
*/

function wadd(w, list, idx, cnt = 0) = cnt < 0
    ? w
    : w + wadd(idx < len(list) ? list[idx][0] : 0, list, idx + 1, cnt - 1);

function map(list, w) = [
    for (
        idx = 0, w = 0, y = 0;

        idx < len(list);

        w0 = wadd(w, list, idx),
        w1 = wadd(w, list, idx, 1),
        w = w1 > maxWidth ? 0 : w0,
        y = y + (w1 > maxWidth ? 1 : 0),
        idx = idx + 1
    )
    [each list[idx], w, y]
];

echo(Number_of_objects = len(o));
%linear_extrude(1) offset(delta = 1) square(maxWidth);
for (a = map(o)) {
    color(a[1]) translate([a[2], a[3], 0]) cube([a.x, 1, rands(2,5,1)[0]]);
}

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

Re: How to solve withouts classic variables?

Ronaldo
In reply to this post by janek

I must confess, I came to a similar solution. However, since my programming experience tells me that recursion is a powerful tool with huge costs (typically memory and performance killer), suffering also from call stack size limitation (even though it is not my case yet, would I be able to process something, that would produce a matrix of ca. 100 x 200 elements using recursion?), I was wondering if there is a better method.

Recursion is an invaluable resource you should try to master to easily use OpenSCAD . You don't need recursion to sum up a list of numbers or get its accumulated sum but you can't avoid some functions and list comprehension:

// total sum of a list of numbers (or of the rows of a matrix)
function sum(l) = [for(i=[0:len(l)-1]) (i<len(l))? 1: 0]*l;

// the accumulate sum of a list of numbers
function accumSum(l) = [for(i=[0:len(l)-1]) sum([for(j=[0:i]) l[j] ]) ];

To solve your problem we may use recursion to find the break points of the box rows from the box widths l and maximum width wmax:

function breakPoints(l, wmax, i=0, S=0, breakp=[]) =
  i>=len(l)-1
  ? breakp
  : breakPoints( l=l, wmax  = wmax  ,i=i+1,
                 S = S+l[i]> wmax  ? l[i]:S+l[i],
                 breakp = concat(breakp,S+l[i]> wmax  ? [i]: [] ) );

That is not an easy way for a beginner but it is a tail recursion elimination solution that doesn't overload stacks. 

Luckly, an equivalent non-recursive solution may be written using C-like for:

function breakPoints(l, wmax) =
  [for( i=0, break=false, S=l[0];
        i<len(l);
        i=i+1, break=S+l[i]> wmax  , S=break? l[i]: S+l[i]
      ) if(break) i]; 

That might be easier to understand by a beginner used to list comprehension and imperative programming.
As soon as you have the break points bp of the length list l, the row lengths may be computed by:

function row_lengths(l,bp) =
  [ sum([for(j=[0:1:bp[0]-1]) l[j] ]),
    for(i=[1:len(bp)-1]) sum([for(j=[bp[i-1]:1:bp[i]-1]) l[j] ]),
    sum([for(j=[bp[len(bp)-1]:1:len(l)-1]) l[j] ])
  ]; 

A simple final check of everything:

maxWidth = 50;
boxes = [ [10,10], [5,5], [45,5], [45,15], [45, 5], [10,10], [25,5],
          [5,15], [20, 5], [10,10], [25,5], [5,15], [20, 5]];
lens = [for(bi=boxes) bi.x];
echo(lens=lens);
accS = accumSum(lens);
echo(accumSum=accS);
bp = breakPoints(lens,maxWidth);
echo(break_points=bp);
echo(row_lengths=row_lengths(lens,bp));
/*

ECHO: lens = [10, 5, 45, 45, 45, 10, 25, 5, 20, 10, 25, 5, 20]

ECHO: accumSum = [10, 15, 60, 105, 150, 160, 185, 190, 210, 220, 245, 250, 270]

ECHO: break_points = [2, 3, 4, 5, 8, 10]

ECHO: row_lengths = [15, 45, 45, 45, 40, 30, 50]

*/


Using the break points how to find the Y displacement of the rows?


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

Re: How to solve withouts classic variables?

nophead
In sum() why do you test for i < len(l) when the loop terminates at len(l) - 1 ? And why would you want a zero in the vector of ones?

On Thu, 25 Jun 2020 at 12:45, Ronaldo Persiano <[hidden email]> wrote:

I must confess, I came to a similar solution. However, since my programming experience tells me that recursion is a powerful tool with huge costs (typically memory and performance killer), suffering also from call stack size limitation (even though it is not my case yet, would I be able to process something, that would produce a matrix of ca. 100 x 200 elements using recursion?), I was wondering if there is a better method.

Recursion is an invaluable resource you should try to master to easily use OpenSCAD . You don't need recursion to sum up a list of numbers or get its accumulated sum but you can't avoid some functions and list comprehension:

// total sum of a list of numbers (or of the rows of a matrix)
function sum(l) = [for(i=[0:len(l)-1]) (i<len(l))? 1: 0]*l;

// the accumulate sum of a list of numbers
function accumSum(l) = [for(i=[0:len(l)-1]) sum([for(j=[0:i]) l[j] ]) ];

To solve your problem we may use recursion to find the break points of the box rows from the box widths l and maximum width wmax:

function breakPoints(l, wmax, i=0, S=0, breakp=[]) =
  i>=len(l)-1
  ? breakp
  : breakPoints( l=l, wmax  = wmax  ,i=i+1,
                 S = S+l[i]> wmax  ? l[i]:S+l[i],
                 breakp = concat(breakp,S+l[i]> wmax  ? [i]: [] ) );

That is not an easy way for a beginner but it is a tail recursion elimination solution that doesn't overload stacks. 

Luckly, an equivalent non-recursive solution may be written using C-like for:

function breakPoints(l, wmax) =
  [for( i=0, break=false, S=l[0];
        i<len(l);
        i=i+1, break=S+l[i]> wmax  , S=break? l[i]: S+l[i]
      ) if(break) i]; 

That might be easier to understand by a beginner used to list comprehension and imperative programming.
As soon as you have the break points bp of the length list l, the row lengths may be computed by:

function row_lengths(l,bp) =
  [ sum([for(j=[0:1:bp[0]-1]) l[j] ]),
    for(i=[1:len(bp)-1]) sum([for(j=[bp[i-1]:1:bp[i]-1]) l[j] ]),
    sum([for(j=[bp[len(bp)-1]:1:len(l)-1]) l[j] ])
  ]; 

A simple final check of everything:

maxWidth = 50;
boxes = [ [10,10], [5,5], [45,5], [45,15], [45, 5], [10,10], [25,5],
          [5,15], [20, 5], [10,10], [25,5], [5,15], [20, 5]];
lens = [for(bi=boxes) bi.x];
echo(lens=lens);
accS = accumSum(lens);
echo(accumSum=accS);
bp = breakPoints(lens,maxWidth);
echo(break_points=bp);
echo(row_lengths=row_lengths(lens,bp));
/*

ECHO: lens = [10, 5, 45, 45, 45, 10, 25, 5, 20, 10, 25, 5, 20]

ECHO: accumSum = [10, 15, 60, 105, 150, 160, 185, 190, 210, 220, 245, 250, 270]

ECHO: break_points = [2, 3, 4, 5, 8, 10]

ECHO: row_lengths = [15, 45, 45, 45, 40, 30, 50]

*/


Using the break points how to find the Y displacement of the rows?

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

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

Re: How to solve withouts classic variables?

Ronaldo
In sum() why do you test for i < len(l) when the loop terminates at len(l) - 1 ? And why would you want a zero in the vector of ones? 
 
You are right, the if is unnecessary. I just have done a lazy change in a more general version of it:

// total sum of the first n elements a list of numbers (or of the rows of a matrix)
function sum(l,n) = [for(i=[0:len(l)-1]) (i<n)? 1: 0]*l; 

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

Re: How to solve withouts classic variables?

Parkinbot
There has been a lot of discussion about summing up something in OpenSCAD.
Wouldn't it be worth to add feature request for a sum(list) primitive to
OpenSCAD? Especially newbies, who aren't running their own libs or writing
functions, would appreciate this.

Not having a sum(), is like not having a len() function and having to
implement it on your own:

echo(len([1,2,3, 7]));
function len(X=[],i=0) = X[i]== undef?0:1+len(X,i+1);

I mean we have functions like cross(), norm(), abs(), min(), max(), floor(),
ceil(), sign(), sqrt() - to name the most prominent ones - which could all
be implemented by the users as well, so why is sum() missing?

Personally I also wouldn't be unhappy with a basic set of file functions,
like fopen(), fecho(), to eventually leave the medieval darkness OpenSCAD is
self-restricting itself ;-)




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

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

Re: How to solve withouts classic variables?

acwest
I am in favour of sum, and some sort of file io. It would be nice to be able to add users pace support for new file formats. The fopen/fread/fwrite/fclose/ftell/fseek set is complete, at least

On Thu, 25 Jun 2020, 09:08 Parkinbot, <[hidden email]> wrote:
There has been a lot of discussion about summing up something in OpenSCAD.
Wouldn't it be worth to add feature request for a sum(list) primitive to
OpenSCAD? Especially newbies, who aren't running their own libs or writing
functions, would appreciate this.

Not having a sum(), is like not having a len() function and having to
implement it on your own:

echo(len([1,2,3, 7]));
function len(X=[],i=0) = X[i]== undef?0:1+len(X,i+1);

I mean we have functions like cross(), norm(), abs(), min(), max(), floor(),
ceil(), sign(), sqrt() - to name the most prominent ones - which could all
be implemented by the users as well, so why is sum() missing?

Personally I also wouldn't be unhappy with a basic set of file functions,
like fopen(), fecho(), to eventually leave the medieval darkness OpenSCAD is
self-restricting itself ;-)




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

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

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