Yes, rewriting something like BOSL2 is a huge task. I’ve been thinking how
we could simplify this task. After all, inside BOSL2 you do use objects but
you map them to arrays and indexes. I think I see possibilities to easily
map them from an array to an object and vice versa of we have some layout
description of the array. I’ll be trying to come up with some ideas this
week.
Actually, BOSL2 is my primary motivation for this work. :-) I can’t live
without the attachable but nor can I live without the object abstraction.
Life puts strange challenges on our path. 😂
Peter
On Sun 27 Jul 2025 at 20:37, Adrian Mariano via Discuss <
discuss@lists.openscad.org> wrote:
This feature is something I’ve been waiting for and even if Peter doesn’t
contrive a way to make methods it will still make a big difference. But
BOSL2 supports the stable openscad with just a few very localized
exceptions for textmetrics so I have not actually tried the new feature
yet.
Also as someone else noted the task of rewriting to use the new feature is
a big one and not backwards compatible (except in the case of changes that
are entirely internal).
On Sun, Jul 27, 2025 at 09:01 Peter Kriens via Discuss <
discuss@lists.openscad.org> wrote:
Hi Nathan,
Just a heads up. I am trying to get an additional feature in so that
function objects can act as "methods". This will make it easy to add
functions to your objects that will be bound to their 'current' object and
not the original object.
Fixed binding to the original object can be very confusing. Since
OpenSCAD has no mutability, you always need to make copies. However, if
you'd copy the function object they remain bound to the original data.
a_prism = object( size=[10,20], size2=5, h=10, shift=[4,0],
function slice(begin=0,end=h) { ... }
);
You can now use a_prism.slice(2,8)
but if you make a copy:
another_prism = object( a_prism, h=20)
The function another_prism.slice(2,8)
will use the values of a_prism
sadly.
Referencing the current object is imho a necessary feature to make
objects really shine in libraries. The alternative, writing functions that
take a data-only object works of course. However, this has the disadvantage
that these functions are in the global shared namespace.
The advantage of functions in objects (aka methods) is that you can use
nice short and simple names:
prism_slice( a_prism, begin=5, end=8);
Versus
a_prism.slice(begin=4, end=15);
So I'd wait a bit before you convert any libraries if you think this is
interesting.
It might be nice if we could develop common conventions for this "object
oriented" use of objects in OpenSCAD so don't hesitate to discuss issues
and choices you encounter here.
Peter
On 26 Jul 2025, at 17:55, Nathan Sokalski via Discuss <
discuss@lists.openscad.org> wrote:
It is a feature that I have looked forward to almost ever since I started
using OpenSCAD (which I did when I first started doing 3D printing). One
thing that I (and I am guessing many others) will need to do as well is
figure out how to as well is convert & modify my existing projects &
libraries. This will definitely be time consuming, and I have multiple
libraries that I use in almost all of my projects. I am trying to figure
out the best way to do this, since it will require either making a whole
new version of the library (making a version of mylibrary.scad called
mylibrary_obj.scad) for future use or updating every project in which I
have used the library (which has human error and missing instance written
all over it). Don't get me wrong, this is by no means a complaint,
everything comes with a price, and this is a price I think is worth paying.
I am also going to mention (although it probably can't be done until this
is out of experimental stage) that I often use the Visual Studio Code
extension for editing my projects, which will need updated (although that
is obviously more a topic for their site), so if anybody has any
association with working on that, I would suggest working on updating that
(and the same probably applies to any other 3rd party editors) ASAP. So
once again, thanks and I look forward to this feature, and hopefully
everyone else does as well!
From: Peter Kriens via Discuss discuss@lists.openscad.org
Sent: Saturday, July 26, 2025 10:54 AM
To: OpenSCAD general discussion Mailing-list <
discuss@lists.openscad.org>
Cc: Peter Kriens peter.kriens@aqute.biz
Subject: [OpenSCAD] Re: New feature in 2025.07.11: the object()
function
I somehow missed it, nice :-)
It is a wonderful new function. It is a pity that libraries cannot use it
yet because it is experimental.
Peter
On 26 Jul 2025, at 08:28, Jordan Brown via Discuss <
discuss@lists.openscad.org> wrote:
// Best view is looking straight down at the origin.
$vpr = [0,0,0];
$vpt = [0,0,0];
// Demonstration animation. Use FPS=10 and steps=100.
// Zoom as desired.
// This vector is a description of everything that happens
// during the animation. You want a wide window to read it.
// The only thing that's defined is "t", the timestamp for that
// particular entry. The rest are up to your program.
// For this animation:
// pos1, pos2: the {red, green} stick man's position
// arm1, arm2: the {red, green} stick man's arm angle
// says1, says2: what the {red, green} stick man is saying
timeline = [
object(t=0, pos1=[-50,0,0], arm1=-30, says1="", pos2=[50,0], arm2=-30, says2="" ),
object(t=2.5, arm1=-30 ),
object(t=3, arm1=50, says1="Hey, George!" ),
object(t=3.5, arm1=-30 ),
object(t=5, says1="" ),
object(t=5.5, arm2=-30, ),
object(t=6, arm2=50, says2="Hey, Fred!" ),
object(t=6.5, arm2=-30 ),
object(t=7, says2="" ),
object(t=12, pos1=[-5,0,0], pos2=[5,0] ),
object(t=13, says1="Can I go past?" ),
object(t=14, says1="" ),
object(t=15, says2="Sorry, no." ),
object(t=16, says2="" ),
object(t=17, says1="I hate living on a number line!" ),
object(t=19, says1="" ),
object(t=19.5, says2="Me too!" ),
object(t=20.5, says2="" ),
object(t=22, pos1=[-5,0,0], arm2=-30, says1="", pos2=[5,0], arm2=-30, says2="" ),
];
// Now, create the current frame of the animation.
// Get the current values of all of the timeline columns.
a = animate(timeline);
// Using those values, create the model at this moment. There are two stick men.
translate(a.pos1) {
color("red") stickman(a.says1, a.arm1);
}
translate(a.pos2) {
color("green") stickman(a.says2, a.arm2);
}
// Create a stick man, holding his arms at the specified angle and saying what's specified.
module stickman(says, arm) {
square([1,8], center=true);
translate([0,5]) circle(2);
translate([0,2])
rotate(arm)
translate([0,-0.5])
square([4,1]);
translate([0,2])
rotate(180-arm)
translate([0,-0.5])
square([4,1]);
translate([0,-4])
rotate(200)
translate([-0.5,0])
square([1,5]);
translate([0,-4])
rotate(160)
translate([-0.5,0])
square([1,5]);
translate([0, 8]) text(says, halign="center", valign="baseline", size=3);
}
// The rest is generic support for using a timeline like that.
// Extract one column from an animation timeline, extracting only
// those entries where that column is present.
function animate_extract(list, key) = [
for (e = list) if (!is_undef(e[key])) [ e.t, e[key] ]
];
// Get the duration of the timeline, the timestamp of the
// last entry in the timeline.
function animate_duration(list) = list[len(list)-1].t;
// Given $t, a timeline and a key, interpolate the current value
// of the key.
function animate_interpolate(list, key) =
xlookup($t * animate_duration(list), animate_extract(list, key));
// Get a list of all keys used in the timeline.
function animate_keys(list) =
let (o = object(
[
for (e = list)
for (k = e)
[ k, true ]
]
))
[ for (k = o) k ];
// Given $t and a timeline, return an aggregated object with the
// current values of all of the columns of the timeline.
function animate(timeline) =
let(keys = animate_keys(timeline))
object(
[
for (k = keys) [ k, animate_interpolate(timeline, k) ]
]
);
// lookup() on steroids. Given a value and a lookup-like list,
// do the lookup and interpolation that lookup() does... but have
// it also work for strings, booleans, and identical-length lists
// of numbers.
function xlookup(val, list) =
is_num(list[0][1]) ? lookup(val, list)
: is_string(list[0][1]) ? lookup_string(val, list)
: is_bool(list[0][1]) ? lookup_bool(val, list)
: is_list(list[0][1]) ? lookup_list(val, list)
: assert(false, "don't know how to lookup that type");
// Given a value and a lookup list, return the index of the entry
// before (or matching) the value.
function lookup_prev(val, list) =
let (tmp = [ for (i = [0:1:len(list)-1]) [ list[i][0], i ] ])
floor(lookup(val, tmp));
//Given a value and a lookup list, return the index of the entry
// after (or matching) the value.
function lookup_next(val, list) =
let (tmp = [ for (i = [0:1:len(list)-1]) [ list[i][0], i ] ])
ceil(lookup(val, tmp));
// Given a value and a lookup list containing strings, return the
// string before (or matching) the value.
function lookup_string(val, list) = list[lookup_prev(val, list)][1];
// Given a value and a lookup list containing booleans, return the
// boolean before (or matching) the value.
function lookup_bool(val, list) = list[lookup_prev(val, list)][1];
// Given a value and a lookup list containing same-length lists of
// numbers, interpolate values for the list. Note that because
// lookup_prev() and lookup_next() return the same entry on an exact
// match, and that leads to 0*0/0, that case has to be handled
// specially.
function lookup_list(val, list) =
let(
p = lookup_prev(val, list),
n = lookup_next(val, list)
)
p == n
? list[p][1]
: list[p][1]
+ (list[n][1]-list[p][1])
* (val - list[p][0]) / (list[n][0] - list[p][0]);
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
It seems like automatic conversion of BOSL2 (or existing code generally) to
objects would be pretty difficult to do. Some very restricted automatic
conversion would be possible, such as looking at a data structure that is
currently an array and replacing foo[number] with foo.<name> for some
appropriate name. But things like changing all VNF references to objects
would be harder to do. And changing functions that currently return
arbitrary arrays to objects is also going to be difficult to do in an
automated way. Replacing struct() invocations may work for many things,
but I think it won't work for screws.scad because not all keys are text.
And I think for arg processing I also wanted the ability to create a new
structure where it is impossible to add fields, only change existing
fields. I don't think this exists, so probably some extra logic would be
needed.
On Sun, Jul 27, 2025 at 3:04 PM Peter Kriens via Discuss <
discuss@lists.openscad.org> wrote:
Yes, rewriting something like BOSL2 is a huge task. I’ve been thinking how
we could simplify this task. After all, inside BOSL2 you do use objects but
you map them to arrays and indexes. I think I see possibilities to easily
map them from an array to an object and vice versa of we have some layout
description of the array. I’ll be trying to come up with some ideas this
week.
Actually, BOSL2 is my primary motivation for this work. :-) I can’t live
without the attachable but nor can I live without the object abstraction.
Life puts strange challenges on our path. 😂
Peter
On Sun 27 Jul 2025 at 20:37, Adrian Mariano via Discuss <
discuss@lists.openscad.org> wrote:
This feature is something I’ve been waiting for and even if Peter doesn’t
contrive a way to make methods it will still make a big difference. But
BOSL2 supports the stable openscad with just a few very localized
exceptions for textmetrics so I have not actually tried the new feature
yet.
Also as someone else noted the task of rewriting to use the new feature
is a big one and not backwards compatible (except in the case of changes
that are entirely internal).
On Sun, Jul 27, 2025 at 09:01 Peter Kriens via Discuss <
discuss@lists.openscad.org> wrote:
Hi Nathan,
Just a heads up. I am trying to get an additional feature in so that
function objects can act as "methods". This will make it easy to add
functions to your objects that will be bound to their 'current' object and
not the original object.
Fixed binding to the original object can be very confusing. Since
OpenSCAD has no mutability, you always need to make copies. However, if
you'd copy the function object they remain bound to the original data.
a_prism = object( size=[10,20], size2=5, h=10, shift=[4,0],
function slice(begin=0,end=h) { ... }
);
You can now use a_prism.slice(2,8)
but if you make a copy:
another_prism = object( a_prism, h=20)
The function another_prism.slice(2,8)
will use the values of a_prism
sadly.
Referencing the current object is imho a necessary feature to make
objects really shine in libraries. The alternative, writing functions that
take a data-only object works of course. However, this has the disadvantage
that these functions are in the global shared namespace.
The advantage of functions in objects (aka methods) is that you can use
nice short and simple names:
prism_slice( a_prism, begin=5, end=8);
Versus
a_prism.slice(begin=4, end=15);
So I'd wait a bit before you convert any libraries if you think this is
interesting.
It might be nice if we could develop common conventions for this "object
oriented" use of objects in OpenSCAD so don't hesitate to discuss issues
and choices you encounter here.
Peter
On 26 Jul 2025, at 17:55, Nathan Sokalski via Discuss <
discuss@lists.openscad.org> wrote:
It is a feature that I have looked forward to almost ever since I
started using OpenSCAD (which I did when I first started doing 3D
printing). One thing that I (and I am guessing many others) will need to do
as well is figure out how to as well is convert & modify my existing
projects & libraries. This will definitely be time consuming, and I have
multiple libraries that I use in almost all of my projects. I am trying to
figure out the best way to do this, since it will require either making a
whole new version of the library (making a version of mylibrary.scad called
mylibrary_obj.scad) for future use or updating every project in which I
have used the library (which has human error and missing instance written
all over it). Don't get me wrong, this is by no means a complaint,
everything comes with a price, and this is a price I think is worth paying.
I am also going to mention (although it probably can't be done until this
is out of experimental stage) that I often use the Visual Studio Code
extension for editing my projects, which will need updated (although that
is obviously more a topic for their site), so if anybody has any
association with working on that, I would suggest working on updating that
(and the same probably applies to any other 3rd party editors) ASAP. So
once again, thanks and I look forward to this feature, and hopefully
everyone else does as well!
From: Peter Kriens via Discuss discuss@lists.openscad.org
Sent: Saturday, July 26, 2025 10:54 AM
To: OpenSCAD general discussion Mailing-list <
discuss@lists.openscad.org>
Cc: Peter Kriens peter.kriens@aqute.biz
Subject: [OpenSCAD] Re: New feature in 2025.07.11: the object()
function
I somehow missed it, nice :-)
It is a wonderful new function. It is a pity that libraries cannot use
it yet because it is experimental.
Peter
On 26 Jul 2025, at 08:28, Jordan Brown via Discuss <
discuss@lists.openscad.org> wrote:
// Best view is looking straight down at the origin.
$vpr = [0,0,0];
$vpt = [0,0,0];
// Demonstration animation. Use FPS=10 and steps=100.
// Zoom as desired.
// This vector is a description of everything that happens
// during the animation. You want a wide window to read it.
// The only thing that's defined is "t", the timestamp for that
// particular entry. The rest are up to your program.
// For this animation:
// pos1, pos2: the {red, green} stick man's position
// arm1, arm2: the {red, green} stick man's arm angle
// says1, says2: what the {red, green} stick man is saying
timeline = [
object(t=0, pos1=[-50,0,0], arm1=-30, says1="", pos2=[50,0], arm2=-30, says2="" ),
object(t=2.5, arm1=-30 ),
object(t=3, arm1=50, says1="Hey, George!" ),
object(t=3.5, arm1=-30 ),
object(t=5, says1="" ),
object(t=5.5, arm2=-30, ),
object(t=6, arm2=50, says2="Hey, Fred!" ),
object(t=6.5, arm2=-30 ),
object(t=7, says2="" ),
object(t=12, pos1=[-5,0,0], pos2=[5,0] ),
object(t=13, says1="Can I go past?" ),
object(t=14, says1="" ),
object(t=15, says2="Sorry, no." ),
object(t=16, says2="" ),
object(t=17, says1="I hate living on a number line!" ),
object(t=19, says1="" ),
object(t=19.5, says2="Me too!" ),
object(t=20.5, says2="" ),
object(t=22, pos1=[-5,0,0], arm2=-30, says1="", pos2=[5,0], arm2=-30, says2="" ),
];
// Now, create the current frame of the animation.
// Get the current values of all of the timeline columns.
a = animate(timeline);
// Using those values, create the model at this moment. There are two stick men.
translate(a.pos1) {
color("red") stickman(a.says1, a.arm1);
}
translate(a.pos2) {
color("green") stickman(a.says2, a.arm2);
}
// Create a stick man, holding his arms at the specified angle and saying what's specified.
module stickman(says, arm) {
square([1,8], center=true);
translate([0,5]) circle(2);
translate([0,2])
rotate(arm)
translate([0,-0.5])
square([4,1]);
translate([0,2])
rotate(180-arm)
translate([0,-0.5])
square([4,1]);
translate([0,-4])
rotate(200)
translate([-0.5,0])
square([1,5]);
translate([0,-4])
rotate(160)
translate([-0.5,0])
square([1,5]);
translate([0, 8]) text(says, halign="center", valign="baseline", size=3);
}
// The rest is generic support for using a timeline like that.
// Extract one column from an animation timeline, extracting only
// those entries where that column is present.
function animate_extract(list, key) = [
for (e = list) if (!is_undef(e[key])) [ e.t, e[key] ]
];
// Get the duration of the timeline, the timestamp of the
// last entry in the timeline.
function animate_duration(list) = list[len(list)-1].t;
// Given $t, a timeline and a key, interpolate the current value
// of the key.
function animate_interpolate(list, key) =
xlookup($t * animate_duration(list), animate_extract(list, key));
// Get a list of all keys used in the timeline.
function animate_keys(list) =
let (o = object(
[
for (e = list)
for (k = e)
[ k, true ]
]
))
[ for (k = o) k ];
// Given $t and a timeline, return an aggregated object with the
// current values of all of the columns of the timeline.
function animate(timeline) =
let(keys = animate_keys(timeline))
object(
[
for (k = keys) [ k, animate_interpolate(timeline, k) ]
]
);
// lookup() on steroids. Given a value and a lookup-like list,
// do the lookup and interpolation that lookup() does... but have
// it also work for strings, booleans, and identical-length lists
// of numbers.
function xlookup(val, list) =
is_num(list[0][1]) ? lookup(val, list)
: is_string(list[0][1]) ? lookup_string(val, list)
: is_bool(list[0][1]) ? lookup_bool(val, list)
: is_list(list[0][1]) ? lookup_list(val, list)
: assert(false, "don't know how to lookup that type");
// Given a value and a lookup list, return the index of the entry
// before (or matching) the value.
function lookup_prev(val, list) =
let (tmp = [ for (i = [0:1:len(list)-1]) [ list[i][0], i ] ])
floor(lookup(val, tmp));
//Given a value and a lookup list, return the index of the entry
// after (or matching) the value.
function lookup_next(val, list) =
let (tmp = [ for (i = [0:1:len(list)-1]) [ list[i][0], i ] ])
ceil(lookup(val, tmp));
// Given a value and a lookup list containing strings, return the
// string before (or matching) the value.
function lookup_string(val, list) = list[lookup_prev(val, list)][1];
// Given a value and a lookup list containing booleans, return the
// boolean before (or matching) the value.
function lookup_bool(val, list) = list[lookup_prev(val, list)][1];
// Given a value and a lookup list containing same-length lists of
// numbers, interpolate values for the list. Note that because
// lookup_prev() and lookup_next() return the same entry on an exact
// match, and that leads to 0*0/0, that case has to be handled
// specially.
function lookup_list(val, list) =
let(
p = lookup_prev(val, list),
n = lookup_next(val, list)
)
p == n
? list[p][1]
: list[p][1]
+ (list[n][1]-list[p][1])
* (val - list[p][0]) / (list[n][0] - list[p][0]);
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
A feature that occurred to me that I believe would make backwards compatibility (at least temporarily) easier is to have a "default" property. Normally, properties are accessed using obj.prop or obj["prop"], but having a default property would allow you to access the first property using just obj. By first, I simply mean the first name/value passed to the object() function. For example, if the following object was created:
myobj=object(prop1=value1,prop2=value2,prop3=value3);
The following would all return value1:
echo(myobj.prop1);
echo(myobj["prop1"]);
echo(myobj); //This would return value1 because value1 is the first value that was assigned by the object() function
Another possible idea would be to have a "reserved" property name ("Default", "default", "_", " ") which when assigned a value would be the default value. I think the option of having a default value would simplify the converting of existing libraries because a common technique for returning multiple values from a single function was returning an array, so if the array was assigned to the default value instances of the function would not be broken. Even though this would not replace the need to update a large amount of code, it would allow you to update a function without breaking as much code. Once again, thank you, and I very much look forward to this feature!
Nathan Sokalski
njsokalski@hotmail.commailto:njsokalski@hotmail.com
From: Adrian Mariano via Discuss discuss@lists.openscad.org
Sent: Sunday, July 27, 2025 11:41 PM
To: OpenSCAD general discussion Mailing-list discuss@lists.openscad.org
Cc: Adrian Mariano avm4@cornell.edu
Subject: [OpenSCAD] Re: New feature in 2025.07.11: the object() function
It seems like automatic conversion of BOSL2 (or existing code generally) to objects would be pretty difficult to do. Some very restricted automatic conversion would be possible, such as looking at a data structure that is currently an array and replacing foo[number] with foo.<name> for some appropriate name. But things like changing all VNF references to objects would be harder to do. And changing functions that currently return arbitrary arrays to objects is also going to be difficult to do in an automated way. Replacing struct() invocations may work for many things, but I think it won't work for screws.scad because not all keys are text. And I think for arg processing I also wanted the ability to create a new structure where it is impossible to add fields, only change existing fields. I don't think this exists, so probably some extra logic would be needed.
On Sun, Jul 27, 2025 at 3:04 PM Peter Kriens via Discuss <discuss@lists.openscad.orgmailto:discuss@lists.openscad.org> wrote:
Yes, rewriting something like BOSL2 is a huge task. I’ve been thinking how we could simplify this task. After all, inside BOSL2 you do use objects but you map them to arrays and indexes. I think I see possibilities to easily map them from an array to an object and vice versa of we have some layout description of the array. I’ll be trying to come up with some ideas this week.
Actually, BOSL2 is my primary motivation for this work. :-) I can’t live without the attachable but nor can I live without the object abstraction. Life puts strange challenges on our path. 😂
Peter
On Sun 27 Jul 2025 at 20:37, Adrian Mariano via Discuss <discuss@lists.openscad.orgmailto:discuss@lists.openscad.org> wrote:
This feature is something I’ve been waiting for and even if Peter doesn’t contrive a way to make methods it will still make a big difference. But BOSL2 supports the stable openscad with just a few very localized exceptions for textmetrics so I have not actually tried the new feature yet.
Also as someone else noted the task of rewriting to use the new feature is a big one and not backwards compatible (except in the case of changes that are entirely internal).
On Sun, Jul 27, 2025 at 09:01 Peter Kriens via Discuss <discuss@lists.openscad.orgmailto:discuss@lists.openscad.org> wrote:
Hi Nathan,
Just a heads up. I am trying to get an additional feature in so that function objects can act as "methods". This will make it easy to add functions to your objects that will be bound to their 'current' object and not the original object.
Fixed binding to the original object can be very confusing. Since OpenSCAD has no mutability, you always need to make copies. However, if you'd copy the function object they remain bound to the original data.
a_prism = object( size=[10,20], size2=5, h=10, shift=[4,0],
function slice(begin=0,end=h) { ... }
);
You can now use a_prism.slice(2,8)
but if you make a copy:
another_prism = object( a_prism, h=20)
The function another_prism.slice(2,8)
will use the values of a_prism
sadly.
Referencing the current object is imho a necessary feature to make objects really shine in libraries. The alternative, writing functions that take a data-only object works of course. However, this has the disadvantage that these functions are in the global shared namespace.
The advantage of functions in objects (aka methods) is that you can use nice short and simple names:
prism_slice( a_prism, begin=5, end=8);
Versus
a_prism.slice(begin=4, end=15);
So I'd wait a bit before you convert any libraries if you think this is interesting.
It might be nice if we could develop common conventions for this "object oriented" use of objects in OpenSCAD so don't hesitate to discuss issues and choices you encounter here.
Peter
On 26 Jul 2025, at 17:55, Nathan Sokalski via Discuss <discuss@lists.openscad.orgmailto:discuss@lists.openscad.org> wrote:
It is a feature that I have looked forward to almost ever since I started using OpenSCAD (which I did when I first started doing 3D printing). One thing that I (and I am guessing many others) will need to do as well is figure out how to as well is convert & modify my existing projects & libraries. This will definitely be time consuming, and I have multiple libraries that I use in almost all of my projects. I am trying to figure out the best way to do this, since it will require either making a whole new version of the library (making a version of mylibrary.scad called mylibrary_obj.scad) for future use or updating every project in which I have used the library (which has human error and missing instance written all over it). Don't get me wrong, this is by no means a complaint, everything comes with a price, and this is a price I think is worth paying. I am also going to mention (although it probably can't be done until this is out of experimental stage) that I often use the Visual Studio Code extension for editing my projects, which will need updated (although that is obviously more a topic for their site), so if anybody has any association with working on that, I would suggest working on updating that (and the same probably applies to any other 3rd party editors) ASAP. So once again, thanks and I look forward to this feature, and hopefully everyone else does as well!
Nathan Sokalski
njsokalski@hotmail.commailto:njsokalski@hotmail.com
From: Peter Kriens via Discuss <discuss@lists.openscad.orgmailto:discuss@lists.openscad.org>
Sent: Saturday, July 26, 2025 10:54 AM
To: OpenSCAD general discussion Mailing-list <discuss@lists.openscad.orgmailto:discuss@lists.openscad.org>
Cc: Peter Kriens <peter.kriens@aqute.bizmailto:peter.kriens@aqute.biz>
Subject: [OpenSCAD] Re: New feature in 2025.07.11: the object() function
I somehow missed it, nice :-)
It is a wonderful new function. It is a pity that libraries cannot use it yet because it is experimental.
Peter
On 26 Jul 2025, at 08:28, Jordan Brown via Discuss <discuss@lists.openscad.orgmailto:discuss@lists.openscad.org> wrote:
// Best view is looking straight down at the origin.
$vpr = [0,0,0];
$vpt = [0,0,0];
// Demonstration animation. Use FPS=10 and steps=100.
// Zoom as desired.
// This vector is a description of everything that happens
// during the animation. You want a wide window to read it.
// The only thing that's defined is "t", the timestamp for that
// particular entry. The rest are up to your program.
// For this animation:
// pos1, pos2: the {red, green} stick man's position
// arm1, arm2: the {red, green} stick man's arm angle
// says1, says2: what the {red, green} stick man is saying
timeline = [
object(t=0, pos1=[-50,0,0], arm1=-30, says1="", pos2=[50,0], arm2=-30, says2="" ),
object(t=2.5, arm1=-30 ),
object(t=3, arm1=50, says1="Hey, George!" ),
object(t=3.5, arm1=-30 ),
object(t=5, says1="" ),
object(t=5.5, arm2=-30, ),
object(t=6, arm2=50, says2="Hey, Fred!" ),
object(t=6.5, arm2=-30 ),
object(t=7, says2="" ),
object(t=12, pos1=[-5,0,0], pos2=[5,0] ),
object(t=13, says1="Can I go past?" ),
object(t=14, says1="" ),
object(t=15, says2="Sorry, no." ),
object(t=16, says2="" ),
object(t=17, says1="I hate living on a number line!" ),
object(t=19, says1="" ),
object(t=19.5, says2="Me too!" ),
object(t=20.5, says2="" ),
object(t=22, pos1=[-5,0,0], arm2=-30, says1="", pos2=[5,0], arm2=-30, says2="" ),
];
// Now, create the current frame of the animation.
// Get the current values of all of the timeline columns.
a = animate(timeline);
// Using those values, create the model at this moment. There are two stick men.
translate(a.pos1) {
color("red") stickman(a.says1, a.arm1);
}
translate(a.pos2) {
color("green") stickman(a.says2, a.arm2);
}
// Create a stick man, holding his arms at the specified angle and saying what's specified.
module stickman(says, arm) {
square([1,8], center=true);
translate([0,5]) circle(2);
translate([0,2])
rotate(arm)
translate([0,-0.5])
square([4,1]);
translate([0,2])
rotate(180-arm)
translate([0,-0.5])
square([4,1]);
translate([0,-4])
rotate(200)
translate([-0.5,0])
square([1,5]);
translate([0,-4])
rotate(160)
translate([-0.5,0])
square([1,5]);
translate([0, 8]) text(says, halign="center", valign="baseline", size=3);
}
// The rest is generic support for using a timeline like that.
// Extract one column from an animation timeline, extracting only
// those entries where that column is present.
function animate_extract(list, key) = [
for (e = list) if (!is_undef(e[key])) [ e.t, e[key] ]
];
// Get the duration of the timeline, the timestamp of the
// last entry in the timeline.
function animate_duration(list) = list[len(list)-1].t;
// Given $t, a timeline and a key, interpolate the current value
// of the key.
function animate_interpolate(list, key) =
xlookup($t * animate_duration(list), animate_extract(list, key));
// Get a list of all keys used in the timeline.
function animate_keys(list) =
let (o = object(
[
for (e = list)
for (k = e)
[ k, true ]
]
))
[ for (k = o) k ];
// Given $t and a timeline, return an aggregated object with the
// current values of all of the columns of the timeline.
function animate(timeline) =
let(keys = animate_keys(timeline))
object(
[
for (k = keys) [ k, animate_interpolate(timeline, k) ]
]
);
// lookup() on steroids. Given a value and a lookup-like list,
// do the lookup and interpolation that lookup() does... but have
// it also work for strings, booleans, and identical-length lists
// of numbers.
function xlookup(val, list) =
is_num(list[0][1]) ? lookup(val, list)
: is_string(list[0][1]) ? lookup_string(val, list)
: is_bool(list[0][1]) ? lookup_bool(val, list)
: is_list(list[0][1]) ? lookup_list(val, list)
: assert(false, "don't know how to lookup that type");
// Given a value and a lookup list, return the index of the entry
// before (or matching) the value.
function lookup_prev(val, list) =
let (tmp = [ for (i = [0:1:len(list)-1]) [ list[i][0], i ] ])
floor(lookup(val, tmp));
//Given a value and a lookup list, return the index of the entry
// after (or matching) the value.
function lookup_next(val, list) =
let (tmp = [ for (i = [0:1:len(list)-1]) [ list[i][0], i ] ])
ceil(lookup(val, tmp));
// Given a value and a lookup list containing strings, return the
// string before (or matching) the value.
function lookup_string(val, list) = list[lookup_prev(val, list)][1];
// Given a value and a lookup list containing booleans, return the
// boolean before (or matching) the value.
function lookup_bool(val, list) = list[lookup_prev(val, list)][1];
// Given a value and a lookup list containing same-length lists of
// numbers, interpolate values for the list. Note that because
// lookup_prev() and lookup_next() return the same entry on an exact
// match, and that leads to 0*0/0, that case has to be handled
// specially.
function lookup_list(val, list) =
let(
p = lookup_prev(val, list),
n = lookup_next(val, list)
)
p == n
? list[p][1]
: list[p][1]
+ (list[n][1]-list[p][1])
* (val - list[p][0]) / (list[n][0] - list[p][0]);
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.orgmailto:discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.orgmailto:discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.orgmailto:discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.orgmailto:discuss-leave@lists.openscad.org
I think having objects return a default like that sounds like a hack that
would clutter the api without having a legitimate long term use.
The way to address the type of compatibility you’re talking about is to
freeze a version of the library that returns arrays that old code can
continue to use and then write a new non compatible version.
On Mon, Jul 28, 2025 at 14:34 Nathan Sokalski via Discuss <
discuss@lists.openscad.org> wrote:
A feature that occurred to me that I believe would make backwards
compatibility (at least temporarily) easier is to have a "default"
property. Normally, properties are accessed using obj.prop or obj["prop"],
but having a default property would allow you to access the first property
using just obj. By first, I simply mean the first name/value passed to the
object() function. For example, if the following object was created:
myobj=object(prop1=value1,prop2=value2,prop3=value3);
The following would all return value1:
echo(myobj.prop1);
echo(myobj["prop1"]);
echo(myobj); //This would return value1 because value1 is the first value
that was assigned by the object() function
Another possible idea would be to have a "reserved" property name
("Default", "default", "_", " ") which when assigned a value would be the
default value. I think the option of having a default value would simplify
the converting of existing libraries because a common technique for
returning multiple values from a single function was returning an array, so
if the array was assigned to the default value instances of the function
would not be broken. Even though this would not replace the need to update
a large amount of code, it would allow you to update a function without
breaking as much code. Once again, thank you, and I very much look forward
to this feature!
From: Adrian Mariano via Discuss discuss@lists.openscad.org
Sent: Sunday, July 27, 2025 11:41 PM
To: OpenSCAD general discussion Mailing-list <discuss@lists.openscad.org
Cc: Adrian Mariano avm4@cornell.edu
Subject: [OpenSCAD] Re: New feature in 2025.07.11: the object() function
It seems like automatic conversion of BOSL2 (or existing code generally)
to objects would be pretty difficult to do. Some very restricted automatic
conversion would be possible, such as looking at a data structure that is
currently an array and replacing foo[number] with foo.<name> for some
appropriate name. But things like changing all VNF references to objects
would be harder to do. And changing functions that currently return
arbitrary arrays to objects is also going to be difficult to do in an
automated way. Replacing struct() invocations may work for many things,
but I think it won't work for screws.scad because not all keys are text.
And I think for arg processing I also wanted the ability to create a new
structure where it is impossible to add fields, only change existing
fields. I don't think this exists, so probably some extra logic would be
needed.
On Sun, Jul 27, 2025 at 3:04 PM Peter Kriens via Discuss <
discuss@lists.openscad.org> wrote:
Yes, rewriting something like BOSL2 is a huge task. I’ve been thinking how
we could simplify this task. After all, inside BOSL2 you do use objects but
you map them to arrays and indexes. I think I see possibilities to easily
map them from an array to an object and vice versa of we have some layout
description of the array. I’ll be trying to come up with some ideas this
week.
Actually, BOSL2 is my primary motivation for this work. :-) I can’t live
without the attachable but nor can I live without the object abstraction.
Life puts strange challenges on our path. 😂
Peter
On Sun 27 Jul 2025 at 20:37, Adrian Mariano via Discuss <
discuss@lists.openscad.org> wrote:
This feature is something I’ve been waiting for and even if Peter doesn’t
contrive a way to make methods it will still make a big difference. But
BOSL2 supports the stable openscad with just a few very localized
exceptions for textmetrics so I have not actually tried the new feature
yet.
Also as someone else noted the task of rewriting to use the new feature is
a big one and not backwards compatible (except in the case of changes that
are entirely internal).
On Sun, Jul 27, 2025 at 09:01 Peter Kriens via Discuss <
discuss@lists.openscad.org> wrote:
Hi Nathan,
Just a heads up. I am trying to get an additional feature in so that
function objects can act as "methods". This will make it easy to add
functions to your objects that will be bound to their 'current' object and
not the original object.
Fixed binding to the original object can be very confusing. Since OpenSCAD
has no mutability, you always need to make copies. However, if you'd copy
the function object they remain bound to the original data.
a_prism = object( size=[10,20], size2=5, h=10, shift=[4,0],
function slice(begin=0,end=h) { ... }
);
You can now use a_prism.slice(2,8)
but if you make a copy:
another_prism = object( a_prism, h=20)
The function another_prism.slice(2,8)
will use the values of a_prism
sadly.
Referencing the current object is imho a necessary feature to make objects
really shine in libraries. The alternative, writing functions that take a
data-only object works of course. However, this has the disadvantage that
these functions are in the global shared namespace.
The advantage of functions in objects (aka methods) is that you can use
nice short and simple names:
prism_slice( a_prism, begin=5, end=8);
Versus
a_prism.slice(begin=4, end=15);
So I'd wait a bit before you convert any libraries if you think this is
interesting.
It might be nice if we could develop common conventions for this "object
oriented" use of objects in OpenSCAD so don't hesitate to discuss issues
and choices you encounter here.
Peter
On 26 Jul 2025, at 17:55, Nathan Sokalski via Discuss <
discuss@lists.openscad.org> wrote:
It is a feature that I have looked forward to almost ever since I started
using OpenSCAD (which I did when I first started doing 3D printing). One
thing that I (and I am guessing many others) will need to do as well is
figure out how to as well is convert & modify my existing projects &
libraries. This will definitely be time consuming, and I have multiple
libraries that I use in almost all of my projects. I am trying to figure
out the best way to do this, since it will require either making a whole
new version of the library (making a version of mylibrary.scad called
mylibrary_obj.scad) for future use or updating every project in which I
have used the library (which has human error and missing instance written
all over it). Don't get me wrong, this is by no means a complaint,
everything comes with a price, and this is a price I think is worth paying.
I am also going to mention (although it probably can't be done until this
is out of experimental stage) that I often use the Visual Studio Code
extension for editing my projects, which will need updated (although that
is obviously more a topic for their site), so if anybody has any
association with working on that, I would suggest working on updating that
(and the same probably applies to any other 3rd party editors) ASAP. So
once again, thanks and I look forward to this feature, and hopefully
everyone else does as well!
From: Peter Kriens via Discuss discuss@lists.openscad.org
Sent: Saturday, July 26, 2025 10:54 AM
To: OpenSCAD general discussion Mailing-list <discuss@lists.openscad.org
Cc: Peter Kriens peter.kriens@aqute.biz
Subject: [OpenSCAD] Re: New feature in 2025.07.11: the object() function
I somehow missed it, nice :-)
It is a wonderful new function. It is a pity that libraries cannot use it
yet because it is experimental.
Peter
On 26 Jul 2025, at 08:28, Jordan Brown via Discuss <
discuss@lists.openscad.org> wrote:
// Best view is looking straight down at the origin.
$vpr = [0,0,0];
$vpt = [0,0,0];
// Demonstration animation. Use FPS=10 and steps=100.
// Zoom as desired.
// This vector is a description of everything that happens
// during the animation. You want a wide window to read it.
// The only thing that's defined is "t", the timestamp for that
// particular entry. The rest are up to your program.
// For this animation:
// pos1, pos2: the {red, green} stick man's position
// arm1, arm2: the {red, green} stick man's arm angle
// says1, says2: what the {red, green} stick man is saying
timeline = [
object(t=0, pos1=[-50,0,0], arm1=-30, says1="", pos2=[50,0], arm2=-30, says2="" ),
object(t=2.5, arm1=-30 ),
object(t=3, arm1=50, says1="Hey, George!" ),
object(t=3.5, arm1=-30 ),
object(t=5, says1="" ),
object(t=5.5, arm2=-30, ),
object(t=6, arm2=50, says2="Hey, Fred!" ),
object(t=6.5, arm2=-30 ),
object(t=7, says2="" ),
object(t=12, pos1=[-5,0,0], pos2=[5,0] ),
object(t=13, says1="Can I go past?" ),
object(t=14, says1="" ),
object(t=15, says2="Sorry, no." ),
object(t=16, says2="" ),
object(t=17, says1="I hate living on a number line!" ),
object(t=19, says1="" ),
object(t=19.5, says2="Me too!" ),
object(t=20.5, says2="" ),
object(t=22, pos1=[-5,0,0], arm2=-30, says1="", pos2=[5,0], arm2=-30, says2="" ),
];
// Now, create the current frame of the animation.
// Get the current values of all of the timeline columns.
a = animate(timeline);
// Using those values, create the model at this moment. There are two stick men.
translate(a.pos1) {
color("red") stickman(a.says1, a.arm1);
}
translate(a.pos2) {
color("green") stickman(a.says2, a.arm2);
}
// Create a stick man, holding his arms at the specified angle and saying what's specified.
module stickman(says, arm) {
square([1,8], center=true);
translate([0,5]) circle(2);
translate([0,2])
rotate(arm)
translate([0,-0.5])
square([4,1]);
translate([0,2])
rotate(180-arm)
translate([0,-0.5])
square([4,1]);
translate([0,-4])
rotate(200)
translate([-0.5,0])
square([1,5]);
translate([0,-4])
rotate(160)
translate([-0.5,0])
square([1,5]);
translate([0, 8]) text(says, halign="center", valign="baseline", size=3);
}
// The rest is generic support for using a timeline like that.
// Extract one column from an animation timeline, extracting only
// those entries where that column is present.
function animate_extract(list, key) = [
for (e = list) if (!is_undef(e[key])) [ e.t, e[key] ]
];
// Get the duration of the timeline, the timestamp of the
// last entry in the timeline.
function animate_duration(list) = list[len(list)-1].t;
// Given $t, a timeline and a key, interpolate the current value
// of the key.
function animate_interpolate(list, key) =
xlookup($t * animate_duration(list), animate_extract(list, key));
// Get a list of all keys used in the timeline.
function animate_keys(list) =
let (o = object(
[
for (e = list)
for (k = e)
[ k, true ]
]
))
[ for (k = o) k ];
// Given $t and a timeline, return an aggregated object with the
// current values of all of the columns of the timeline.
function animate(timeline) =
let(keys = animate_keys(timeline))
object(
[
for (k = keys) [ k, animate_interpolate(timeline, k) ]
]
);
// lookup() on steroids. Given a value and a lookup-like list,
// do the lookup and interpolation that lookup() does... but have
// it also work for strings, booleans, and identical-length lists
// of numbers.
function xlookup(val, list) =
is_num(list[0][1]) ? lookup(val, list)
: is_string(list[0][1]) ? lookup_string(val, list)
: is_bool(list[0][1]) ? lookup_bool(val, list)
: is_list(list[0][1]) ? lookup_list(val, list)
: assert(false, "don't know how to lookup that type");
// Given a value and a lookup list, return the index of the entry
// before (or matching) the value.
function lookup_prev(val, list) =
let (tmp = [ for (i = [0:1:len(list)-1]) [ list[i][0], i ] ])
floor(lookup(val, tmp));
//Given a value and a lookup list, return the index of the entry
// after (or matching) the value.
function lookup_next(val, list) =
let (tmp = [ for (i = [0:1:len(list)-1]) [ list[i][0], i ] ])
ceil(lookup(val, tmp));
// Given a value and a lookup list containing strings, return the
// string before (or matching) the value.
function lookup_string(val, list) = list[lookup_prev(val, list)][1];
// Given a value and a lookup list containing booleans, return the
// boolean before (or matching) the value.
function lookup_bool(val, list) = list[lookup_prev(val, list)][1];
// Given a value and a lookup list containing same-length lists of
// numbers, interpolate values for the list. Note that because
// lookup_prev() and lookup_next() return the same entry on an exact
// match, and that leads to 0*0/0, that case has to be handled
// specially.
function lookup_list(val, list) =
let(
p = lookup_prev(val, list),
n = lookup_next(val, list)
)
p == n
? list[p][1]
: list[p][1]
+ (list[n][1]-list[p][1])
* (val - list[p][0]) / (list[n][0] - list[p][0]);
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
On 7/28/2025 12:26 PM, Adrian Mariano via Discuss wrote:
I think having objects return a default like that sounds like a hack
that would clutter the api without having a legitimate long term use.
I don't think it's even possible. When you say "myobj" does that mean
"give me the object", or does it mean "give me the first member of the
object"? It can only mean one of those things, and it pretty much has
to mean 'give me the object".
Jordan,
You say
Formally the entries should be assumed to be in no specific order, but for aesthetic reasons they are reported in the order they were added to the object.
Why not formalise that order? Because people ARE going to rely on it.
Does len() work for objects?
From: Jordan Brown via Discuss [mailto:discuss@lists.openscad.org]
Sent: Monday, July 14, 2025 8:20 AM
To: OpenSCAD
Cc: Jordan Brown
Subject: [OpenSCAD] New feature in 2025.07.11: the object() function
Quick summary:
2025.07.11 adds the the object() function, which creates an object (in the style returned by textmetrics(), fontmetrics(), and import() of a JSON file).
It accumulates the object by processing the arguments left to right, with later settings for a particular member replacing earlier settings.
There are three forms for an argument:
name=value - sets that name (a constant identifier) to that value.
A vector with a list of [name, value] vectors, or [name] to remove a member, where the names can be any string expression.
An object has its members copied.
There is a new function has_key(obj, name) that returns true if the object contains the named key.
These functions are currently experimental and so must be enabled before you can use them.
Overview:
An "object" is a collection of names and associated values. In other languages this data structure might be called an object (JavaScript), a dictionary (Python), an associative array (some UNIX shells, awk), or a structure (C, sort of),
This change adds a function that creates an object from a series of names, values, and other objects, and a function that queries whether a particular member is present. These are mostly-normal functions; there is no new syntax introduced.
Background:
OpenSCAD has had an internal implementation of objects for several years, added to support the textmetrics() and fontmetrics() functions, and later import() of a JSON file. This existing mechanism includes mechanisms for accessing the members of the object and for walking through the entries. It does not include a mechanism for the user's program to create an object.
Given an object o, the current operations are:
Details:
The object() function constructs a new object, processing each argument in sequence from left to right, with later settings replacing earlier settings. There are three variations of arguments; they can be mixed in any way.
A named parameter name=value
Sets the specified name to the specified value. As with all named arguments to functions, the name must be an identifier.
a vector [ v1, v2, ... ]
v1, v2, ... are each two- or one-element vectors
[name, value]
Sets the specified name to the specified value. The name can be any string expression.
[name]
Removes the specified name from the object being accumulated. (Note that this is subtly different from setting it to undefined, in that it will not be reported for has_key() or when walking the names of the object.)
an object o
An object has each of its members copied into the new object.
The values contained in an object can (of course) be of any data type: numbers, strings, vectors, function references, objects, et cetera.
There is also a new function has_key(o, name) that returns true if the object has a member with the specified name.
Examples:
Future Directions and Related Projects:
This is the second phase (after the textmetrics() work) of a longer-term plan to introduce "object" features into OpenSCAD.
Object literals, object comprehension: OEP8a https://github.com/openscad/openscad/wiki/OEP8a:--Objects-(dictionaries%3F) adds a syntax for creating objects, including object comprehensions, roughly modeled on JavaScript object syntax, so that { a: 1, b: 2 } is equivalent to object(a=1, b=2). Changes echo() and str() to represent objects using this syntax.
OEP8 https://github.com/openscad/openscad/wiki/OEP8%3A-Objects-%28dictionaries%3F%29%2C-Geometry-as-data%2C-and-Module-References further adds geometry as data and module references.
No formal proposals:
Methods: If a function reference comes from an object or a vector, it should see a special variable $this that refers to the containing object or vector. Note that although there's no inheritance per se, making a modified copy of an object is a lot like the prototype-based https://en.wikipedia.org/wiki/Prototype-based_programming OO.
Variable parameter lists: a syntax for a parameter lists that says "return the rest of the parameters in this variable", as a vector (for positional arguments) or an object (for named arguments).
Spread syntax: a syntax for adding a vector to an argument list as positional arguments, or adding an object to an argument list as named arguments.
Sets: Some kind of syntactic sugar to make it easy to create an object containing boolean "true", to make it easy to define a set (in the mathematical sense) and query whether particular items are present in it.
Questions:
What should these things be called?
They're modeled on JavaScript objects, but to a Python person they look more like dictionaries and to a C person they look more like structures.
Are they really "objects", when they don't have OO features? See Future Directions about methods.
In OpenSCAD, doesn't "object" already mean a geometric figure?
Credits / History:
On 7 Aug 2025, at 11:23, Michael Marx (spintel) via Discuss discuss@lists.openscad.org wrote:
Jordan,
You say
Formally the entries should be assumed to be in no specific order, but for aesthetic reasons they are reported in the order they were added to the object.
Why not formalise that order? Because people ARE going to rely on it.
I agree. These ordering issues are very important, especially if you want to have repeatable builds.
Peter
Does len() work for objects?
From: Jordan Brown via Discuss [mailto:discuss@lists.openscad.org]
Sent: Monday, July 14, 2025 8:20 AM
To: OpenSCAD
Cc: Jordan Brown
Subject: [OpenSCAD] New feature in 2025.07.11: the object() function
Quick summary:
2025.07.11 adds the the object() function, which creates an object (in the style returned by textmetrics(), fontmetrics(), and import() of a JSON file).
It accumulates the object by processing the arguments left to right, with later settings for a particular member replacing earlier settings.
There are three forms for an argument:
name=value - sets that name (a constant identifier) to that value.
A vector with a list of [name, value] vectors, or [name] to remove a member, where the names can be any string expression.
An object has its members copied.
There is a new function has_key(obj, name) that returns true if the object contains the named key.
These functions are currently experimental and so must be enabled before you can use them.
Overview:
An "object" is a collection of names and associated values. In other languages this data structure might be called an object (JavaScript), a dictionary (Python), an associative array (some UNIX shells, awk), or a structure (C, sort of),
This change adds a function that creates an object from a series of names, values, and other objects, and a function that queries whether a particular member is present. These are mostly-normal functions; there is no new syntax introduced.
Background:
OpenSCAD has had an internal implementation of objects for several years, added to support the textmetrics() and fontmetrics() functions, and later import() of a JSON file. This existing mechanism includes mechanisms for accessing the members of the object and for walking through the entries. It does not include a mechanism for the user's program to create an object.
Given an object o, the current operations are:
o.name yields the value of the name member, where name must be an identifier (alphabetic, numeric, underscore, starting with alphabetic or underscore). This syntax is very "clean", but does not allow for names that are derived from expressions or for names that are not suitable for use as identifiers. An undefined name is not an error; it yields undefined.
o[name] also yields the value of the name member, but name can be an any string expression. This syntax is a bit more awkward than the o.name syntax, but allows for dynamically-created names and for names that are not suitable as identifiers. Again, and undefined name yields undefined.
for (name = o) ... is the object equivalent of the vector for(...). It walks the object, setting name to each member name in sequence. This mechanism is usable as both a normal statement and as a list comprehension element. Note that it yields only the name; the value is accessible as o[name]. Formally the entries should be assumed to be in no specific order, but for aesthetic reasons they are reported in the order they were added to the object.
is_object(v) returns true if v is an object.
echo(o) and str(o) produce textual representations of objects. (Note: the textual form looks sort of like syntax, but is not legal OpenSCAD syntax. It is likely to change in the future; see Future Directions below.)
Details:
The object() function constructs a new object, processing each argument in sequence from left to right, with later settings replacing earlier settings. There are three variations of arguments; they can be mixed in any way.
A named parameter name=value
Sets the specified name to the specified value. As with all named arguments to functions, the name must be an identifier.
a vector [ v1, v2, ... ]
v1, v2, ... are each two- or one-element vectors
[name, value]
Sets the specified name to the specified value. The name can be any string expression.
[name]
Removes the specified name from the object being accumulated. (Note that this is subtly different from setting it to undefined, in that it will not be reported for has_key() or when walking the names of the object.)
an object o
An object has each of its members copied into the new object.
The values contained in an object can (of course) be of any data type: numbers, strings, vectors, function references, objects, et cetera.
There is also a new function has_key(o, name) that returns true if the object has a member with the specified name.
Examples:
Create an object; access its members:
o = object(a=1, b=2);
echo(o.a, o["b"]);
Create an object with varying names, and access them:
names = [ "apple", "banana", "string bean" ];
o = object([ for (name=names) [name, 123] ]);
for (name=names) echo(name, o[name]);
Create an object, then create modified copies of that object:
// Ancient planets
planets = object(mercury=1, venus=2, earth=3, mars=4, jupiter=5, saturn=6);
planets1781 = object(planets, uranus=7); // Uranus discovered
planets1846 = object(planets1781, neptune=8); // Neptune discovered
planets1930 = object(planets1846, pluto=9); // Pluto discovered
planets2006 = object(planets1930, [["pluto"]]); // Pluto un-planeted
Check whether a member is present:
echo(has_key(planets1846, "neptune")); // true
echo(has_key(planets2006, "pluto")); // false
Future Directions and Related Projects:
This is the second phase (after the textmetrics() work) of a longer-term plan to introduce "object" features into OpenSCAD.
Object literals, object comprehension: OEP8a https://github.com/openscad/openscad/wiki/OEP8a:--Objects-(dictionaries%3F) adds a syntax for creating objects, including object comprehensions, roughly modeled on JavaScript object syntax, so that { a: 1, b: 2 } is equivalent to object(a=1, b=2). Changes echo() and str() to represent objects using this syntax.
OEP8 https://github.com/openscad/openscad/wiki/OEP8%3A-Objects-%28dictionaries%3F%29%2C-Geometry-as-data%2C-and-Module-References further adds geometry as data and module references.
No formal proposals:
Methods: If a function reference comes from an object or a vector, it should see a special variable $this that refers to the containing object or vector. Note that although there's no inheritance per se, making a modified copy of an object is a lot like the prototype-based OO https://en.wikipedia.org/wiki/Prototype-based_programming.
Variable parameter lists: a syntax for a parameter lists that says "return the rest of the parameters in this variable", as a vector (for positional arguments) or an object (for named arguments).
Spread syntax: a syntax for adding a vector to an argument list as positional arguments, or adding an object to an argument list as named arguments.
Sets: Some kind of syntactic sugar to make it easy to create an object containing boolean "true", to make it easy to define a set (in the mathematical sense) and query whether particular items are present in it.
Questions:
What should these things be called?
They're modeled on JavaScript objects, but to a Python person they look more like dictionaries and to a C person they look more like structures.
Are they really "objects", when they don't have OO features? See Future Directions about methods.
In OpenSCAD, doesn't "object" already mean a geometric figure?
Credits / History:
I did the original textmetrics() work.
Revar Desmera did the original implementation of object().
Peter Kriens drove this final integration, cleaning up the implementation, fixing a bug, and writing test cases.
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org mailto:discuss-leave@lists.openscad.org
Peter Kriens wrote:
Does len() work for objects?
as it happens i was testing this today as i checked out the len() function .. no indeed ..
len( object(this=”that”) );
gives me
[WARNING: len() parameter could not be converted: argument 0: expected string, found object ({ this = "that"; }) in file ., line 1](1,C:/Program Files/OpenSCAD (Nightly))
ECHO: undef
Workaround: len([for(x=obj)x])
On Aug 7, 2025, at 5:30 PM, vulcan_--- via Discuss discuss@lists.openscad.org wrote:
Peter Kriens wrote:
Does len() work for objects?
as it happens i was testing this today as i checked out the len() function .. no indeed ..
len( object(this=”that”) );
gives me
WARNING: len() parameter could not be converted: argument 0: expected string, found object ({ this = "that"; }) in file ., line 1 x-msg://1/1,C:/Program%20Files/OpenSCAD%20(Nightly)
ECHO: undef
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
Revar, Peter,
Revar’s suggestion returning the length of a vector of one object, which
isn’t quite the same thing as the length of the object per se.
I’m not seeing why len should produce a result when applied to an object
but if it does, wouldn’t the simple value
1
be enough?
For comparison, what does len do for any other non-vector, non-string
variable? (I’m away from home so can’t check)
Steve
On Fri, 8 Aug 2025 at 01:47, Revar Desmera via Discuss <
discuss@lists.openscad.org> wrote:
Workaround: len([for(x=obj)x])
On Aug 7, 2025, at 5:30 PM, vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
Peter Kriens wrote:
Does len() work for objects?
as it happens i was testing this today as i checked out the len() function
.. no indeed ..
len( object(this=”that”) );
gives me
WARNING: len() parameter could not be converted: argument 0: expected
string, found object ({ this = "that"; }) in file ., line 1
ECHO: undef
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org