Loading

MakerBot Print is our newest print-prepration software, which supports native CAD files and STL assemblies,
allows you to interact with all your printers via the Cloud, and many other exciting new features.

Download Now

Data Structures - Defining and Accessing

Please Login to Comment

I'm looking for a method for defining and accessing data structures. I frequently need to have a pre-defined data set to make calls easier. I've used the following two methods below in the past, but I'm looking for a better method that makes it easy to add future data and is reasonably easy to read and maintain.

If you have a suggestion, it would be really helpful if you could offer a little sample code to go along with it.

Thanks!

Lookup method
As can be seen in this (failed) project: http://www.thingiverse.com/thing:668210

type1 = [[len, 10], [dia, 5], [bolt, 4]];
type2 = [[len, 20], [dia, 7], [bolt, 3]];

types = [type1, type2];

i = 0;

//variables need to be defined before using a lookup
len = 0;
dia = 0; 
bolt = 0;

len = lookup(len, type[i]);
dia = lookup(dia, type[i]);
bolt = lookup(dia, type[i]);

module doStuff() {
*/ Do stuff here /*
}

doStuff()

Pros:

  • Allows for each element to be discretely defined
  • Allows for descriptive variable names to be used in the definitions
  • Elements can be in any order

Cons:

  • Abuse of the lookup() function
  • Non-straitght-forward way to use as a library - You must know the contents of the types array as well as the index number you want to call

Indexed Array Method
As can be seen in this project: http://www.thingiverse.com/thing:1259672

types = [["Human Readable Type Name", "total length", "diameter at mid-point", "bolt length"], ["Type 1", 10, 5, 4], ["Type 2", 20, 7, 3]];

module doStuff(type=1]) {
   len = types[type][1];
   dia = types[type][2];
   bolt = types[type][3];

   /* do stuff here /* 
}
doStuff(2);

Pros:

  • Allows for adding a 0th element with human readable descriptions that can be referenced later
  • Allows for embedding human readable names for each group
  • Easy to call if types are common sizes (such as M series metric bolts)

Cons:

  • Elements must be in a particular order
  • Must remember order of datum in array when programming
  • Not easy to reference elements if you don't happen to know what order they are in
  • Crap to maintain, if element order changes everything goes to hell
Customizable Parametric Battery Holder (AAA, AA, C, D)
by txoof
nuts_and_bolts - MCAD replacement (nuts, bolts, washers, T-Slots)
by txoof

3 days ago I posted a hash function that I used frequently:

function hash(h,k)= 
( 
  [ for(i=[0:2:len(h)-2]) if( h[i]==k ) h[i+1] ][0]
); 

It turns out that the performance would be significantly improved by using search() (inspired by txoof's example). With a little modification, it can be applied to a data structure like:

dog = [
  "fur", "black",
  "legs", 4
];

in comparison to the data structure in txoof's case:

dog = [
  ["fur", "black"], 
  ["legs", 4]
];

The code is here:

function hash(h,k)= 
(
  h[search([k], [for(i=[0:2:len(h)-2])h[i]])[0]*2+1]   
); 

Wow! This is elegant. I'll have to try this out when I'm back home. I'm not smart enough to parse this in my head.

My version:

function hash(h,k)= 
( 
  [ for(i=[0:2:len(h)-2]) if( h[i]==k ) h[i+1] ][0]
); 

function hashs(h,ks, _i=0)= 
(
  h==undef || _i>len(ks)-1? h
  : let ( h = hash(h, ks[_i]) )
    hashs( h, ks, _i+1 )
);

dog = [
  "fur", ["color", "mixed", "len", "short" ]
  "legs", 4,
];
hash( dog, "legs") ==> 4
hashs(dog, ["fur","color"]) ==> "mixed"

I think the problem with implementing what you want is that OpenSCAD has no runtime variables, just parameters assigned at compile time. There also isn't a RETURN command. I think given these limitations, you MUST abuse the lookup() command. From my readings on the forums, it is not abuse, it was specifically designed to do what you want to do given the language's current limitations.

You will get a much better explanation from one of the devs on the email listserv.

I spoke with Kintel, one of the devs, over on the IRC channel. He suggested using the search() function. I've actually built a working structure around it and it does exactly what I want. It's a bit difficult to wrap your head around how search works from the examples, but here's a working implementation: http://www.thingiverse.com/thing:1460370

Flexing Battery Holders - Customizable
by txoof

So this is meant for when your dealing with 2d arrays right? Could you use/make your own lookup function?
Not sure how you'd get the get return. (probably is a way) but do a nested for loop for the size of the array (would require tweaking for different dimensions of arrays like 3d would need it's own way) kinda like:

func(haystack, needle)
//so haystack is the array you want to search, and needle is what you want to find in it. (sorry for the bad pun but I like it)
for(d1= [0,len(haystack)-1]){
for(d2=[0,len(haystack[d1]-1]{
if(haystack[d1][d2] == needle){
return [d1,d2];
}
}
}

or something like that, then interpret the value as you need to in the code, I'm guessing in your first example you want the second value so you would want to have your return statement be:

return haystack[d1][d2+1]

This is for dealing with N dimensional arrays as needed. Unfortunately OpenSCAD doesn't support any kind of dictionary data structure yet. Though there's an RFC for it, but according to the devs, it's a LONG way off.

It's an interesting idea to use a function as a lookup though I just rediscovered the search() function which I think I can use directly, but could probably benefit from a function call to refine it and make it easier to use. With a bit of work it's possible to create a dictionary of arbitrary dimensions though it gets messy quick. One big advantage is that the data can be stored in an arbitrary order. With the creative use of some conditional checking further on, it's possible to deal with undefined data as well.

Here's a bit of sample code that does the basics of what I want:

dog = [
  ["fur", 100],
  ["legs", 4],
  ["attitude", "friendly"],
  ["leash", true],
  ["name", "Fido", "Ball King"]
];

cat =[
  ["legs", 4],
  ["name", "Fuzzy", "Bird Killer"],
  ["fur", 100000000],
  ["attitude", "awful"],
  ["leash", false]
];

module doStuff(animal = dog) {
  //the search code is a bit cryptic because it returns an list who's elements represent the index of the found element
  legs = animal[search(["legs"], animal, num_returns_per_match = 1)[0][1];
  //this extracts the first data element for this key
  humanName = animal[search["name"], animal, num_returns_per_match = 1)[0][1];
  //this extracts the second data element for this key
  animalName = animal[search["name"], animal, num_returns_per_match = 1)[0][2];

 //do some stuff here with the variables
}

This makes it easy to run a module call such as doStuff(cat) without knowing much about how what data is stored in cat, or what order it is arranged.

So you want a function that searches an n dimension array and returns where that value is? Or return the number of legs the cat has?

Both would be handy.
Here's some code that is generalized to work with elements that have more than one piece of data, but could be refined just to return the number of legs or whatever.

dog = [
    ["name", "Rex", "Ball King"],
    ["legs", 4],
    ["leash", true],
    ["attitude", "friendly"],
    ["age", 4]
];

cat = [
    ["attitude", "jerk"],
    ["name", "Flufy", "Bird Killer"],
    ["legs", 3],
    ["leash", false],
    ["age", 7]
];

function keyLookup (data, key) = search(key, data, num_returns_per_match=1)[0];

// index where the search term is found in the data array
echo(keyLookup(dog, ["name"])); // index of name in array
// entire contents of array element
echo(dog[keyLookup(dog, ["name"])]); // contents of element
// specific value within the element
echo(dog[keyLookup(dog, ["name"])][1]); // human readable name of dog

echo(keyLookup(cat, ["legs"])); // index of legs in the array
echo(cat[keyLookup(cat, ["legs"])]); // contents of element
echo(dog[keyLookup(dog, ["legs"])][1]); // number of legs Fluffy has

To just return the number of legs, change the function to function valueOnly (data, key) = data[search(key, data, num_returns_per_match=1)[0]][1];

I'm just longing for something like the python dict structure.

So you want it to return the legs array or just the position of legs?
The I think search function can work like this: (or at least according to the wiki)

 lTable2=[ ["cat",1],["b",2],["c",3],["dog",4],["a",5],["b",6],["c",7],["d",8],["e",9],["apple",10],["a",11] ];
 lSearch2=["apple"];
 l2=search(lSearch2,lTable2);
 echo(l2);

Returns

ECHO: [9]

which I think would then give you the array if you tweaked it to say:
function (ITable2,ISearch2) = [
I2 = search(lSearch2,lTable2);
return lTable2[I2];]

This would be a great Q to ask on the forum - where several people share common interests with this kind of thing...

Thread is a bit old but still valid. Sort of got my head around openscad as moving some enclosure creation code from maxscript to openscad as more people would have access.

I too have come across the "problem" of the behavior of variables and the need to have what would realistically be simple data tables.

I come from a C/C++ background and have done a lot of work with My*SQL and Oracle but just want a non-hard-coded approach to defining objects that can then be combined.

I have an array of "bolts" that I can render once the index is known, along with some very small helper functions.

// code

/
Bolts - defines bolts with insert nuts and functions to use them
/

g_arr_bolts =
[
["m3-cheese", // name
5.90, // head_d_top
5.90, // head_d_bottom
3.60, // head_h
3.10, // shaft_d
8.00, // padding_d
4.40, // ins_d
3.00], // ins_h

["m4-cheese", // name
6.9, // head_d_top
5.9, // head_d_bottom
3, // head_h
4.10, // shaft_d
9.00, // padding_d
5.40, // ins_d
3.00] // ins_h
];

function bolt_count() = len(g_arr_bolts);

function bolt_index(n) = (g_arr_bolts[0][0] == n) ? 0 :
(g_arr_bolts[1][0] == n) ? 1 :
0 ;

function bolt_name(b) = (b < 0 || b >= bolt_count()) ? bolt_name(0) : g_arr_bolts[b][0];

function bolt_head_d_top(b) = (b < 0 || b >= bolt_count()) ? bolt_head_d_top(0) : g_arr_bolts[b][1];

function bolt_head_d_bottom(b) = (b < 0 || b >= bolt_count()) ? bolt_head_d_bottom(0) : g_arr_bolts[b][2];

function bolt_head_h(b) = (b < 0 || b >= bolt_count()) ? bolt_head_h(0) : g_arr_bolts[b][3];

function bolt_shaft_d(b) = (b < 0 || b >= bolt_count()) ? bolt_shaft_d(0) : g_arr_bolts[b][4];

function bolt_padding_d(b) = (b < 0 || b >= bolt_count()) ? bolt_padding_d(0) : g_arr_bolts[b][5];

function bolt_ins_d(b) = (b < 0 || b >= bolt_count()) ? bolt_ins_d(0) : g_arr_bolts[b][6];

function bolt_ins_h(b) = (b < 0 || b >= bolt_count()) ? bolt_ins_h(0) : g_arr_bolts[b][7];

Now... I have similar functions for boards and enclosures. A board is... say an Arduino UNO or Raspberry Pi, etc while the enclosure defines how to enclose the board. The enclosure has things like height of walls, padding for all dimensions, wall height, lid height and ... well basically everything to do with the enclosure including cutouts and holes for cables etc.

Like state above, any changes have a huge ripple effect. In the enclosure I have a variable for the standoff bolt "m3-cheese" as I refer to them by name and then use a function to get the correct index for the name.

Not the most elegant way but it means order of arrays is not important and adding a variable just means adding another access function. Probably better ways to get the index for a name but I only started openscad a few days ago. :-)

When rendering anything I have 3 options. (DF_MINUS, DF_PLUS and DF_BOTH) These make it possible to do union() difference() etc on the correct bits. When rendered with DF_BOTH it will basically render a complete preview of the part whereas DF_MINUS and DF_PLUS are usually used in Boolean operations.

At the moment it renders a basic case with lid, fasteners, lid clips, standoff supports but I want to allow for laying out multiple boards in a single enclosure. (An example of this would be a 3d printer main board + a Raspberry Pi + a heater power board in the 1 enclosure.) As long as the program knows the position and size of each element it can calculate the largest bounding box in the x,y plane and essentially treat it like a single board.