Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add ability to set solid name when exporting STL #4986

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

DeepHorizons
Copy link

Attempt at #4977

Still need to hook this up to something that will determine the solid name. As suggested in the issue a special variable could be set, $solid_name, but I'm not sure where to look to start modifying that.

This still needs to hook up to something that can read the file and
determine the proper name
@gsohler
Copy link
Member

gsohler commented Feb 14, 2024

I like the movement!
But to get this really useful, it must become possible to set the solid name individually for every sub-tree which probably results
in adding an additional member to AbstractNode which needs to get processed correctly in the whole enginge ...

@t-paul
Copy link
Member

t-paul commented Feb 14, 2024

As mentioned many times before, using $ variables will get my veto, the case @gsohler mentions is just one of those. I think the way to solve that topic is by adding a declarative way of expressing this information. Some ideas collected in https://github.com/openscad/openscad/wiki/Meta-Data-Use-Cases

@jordanbrown0
Copy link
Contributor

I'm not a fan of using $ variables here either, but mostly I consider that a design detail.

I think the more obvious structure is to follow the pattern established by color(), that a part designation is just a node in the tree:

part("a") { ... }
part("b") { ... }

Even that simple case presents a few problems: you have to convince the processing to not union them. (But what should happen if they overlap? The strongly-related problem of assigning color for multi-color prints really wants an answer to that question.)

More complex cases lead quickly to constructs where I don't know what they mean:

part("a") { ... part("a1") ... }
difference() {
    part("a") { ... }
    part("b") { ... }
}
part("a") { ... }
part("a") { ... }

Note that those cases are questions independent of whether the subtrees are designated using $ variables or modules or some new kind of annotation.

What I would like to do is to find a solution for the "one part" problem, where we just want to control the internal name of an STL or the base file name of any export, in a way that will extend naturally to the multi-part case.

@DeepHorizons
Copy link
Author

It sounds like a section of the part(...) syntax should be implemented then. Something that is forward compatible with what is wanted. I was thinking for a single part:

part("my_part");

This would set the solid name for the file, not worrying about the multi-part section. This should be able to be expanded to handle the part("my_part") {...} syntax. The question becomes how do we search for this during the export? Im not sure if we can search for it during the export phase as it seems it only has access to Geometry and the like which does not seem to expose this kind of information. This means we will probably have to poke the hole in exportFile(...) in export.cc and all the variants in the calls. Im still wrapping my head around do_export(...) in openscad.cc to get an idea of what work would need to be done to get this in and any pointers would be great. Am I going to have to modify the parser? How do I make a new node?

@t-paul
Copy link
Member

t-paul commented Feb 15, 2024

There is no "setting" of values on geometry, there is no program running as in for example python. The part() would be referenced in the AST, the syntax tree representing the description (source code) of the geometry.

And that's why I'm still not convinced nesting tree elements is a good idea. It certainly will work for geometry, but it can't distinguish between module declaration and module instantiation, covering only the latter.

The problem with part is that it will take the name out of the global namespace, extending this for all sorts of other information will end up with a horrible mess of names taken in the global module namespace. This could be reduced by using one module name and giving it maybe key/value or something. Thing is pretty much all languages, including new designed ones have some sort of annotations exactly for that.

@jordanbrown0
Copy link
Contributor

To be clear: part(...) would turn into an AST node and then, when executed, would turn into a CSG node. (Just like color(...) and most other module invocations.)

Yes, it eats into global namespace. Ideally, a bunch of related concepts would be covered by that one module: color, material, separate sub-models to be printed separately, maybe slicing hints, et cetera, using named parameters. But it doesn't require new syntax. Any scheme would require either new syntax or eating into global namespace.

part("a"); does not seem like a viable stepping-stone, since in a later version with multi-part support it would need to yield a part that has no content. But maybe if you want part naming, and we're in that interim one-part state, you must have exactly one part() invocation and it must be at the top level, and the entire model must be a child.

Allows the following syntax

```
part("part1") {
	sphere(3);
}
```

Which will generate a `part1.<filename>.<extension>` (`part1.output.stl`
for example)
@DeepHorizons
Copy link
Author

I have a working example for part(...) {...} syntax. The do_export function had to be broken up so I could call the export functionality multiple times for multiple nodes. I do a search for part in the children of the root node and those are exported as their own files with the name of the part prepended to the filename. One issue with the implementation is that it only handles part(...) {...} at the top level, parts that are not at the top level are ignored.

The following part.scad code compiles into two parts, part1.output.stl and part2.output.stl

part("part1") {
    sphere(3);
}
part("part2") {
    cube(3);
}

With a command line:

./openscad -o output.stl part.scad

@t-paul
Copy link
Member

t-paul commented Feb 18, 2024

While I'm still not convinced it's the best idea to use part() as it will not cover any of the many other requested use cases, it should be able to cover one useful case.

If this is going to include multi-file export (which would be very nice, even if it's initially for STL only!), please update the title of the PR and note that this will require some additional work as that PR will need to have at least some test cases and this scenario may not be fully covered by the test suite yet. I assume it's not a complicated thing, but still some extra topic to look at.

Copy link
Member

@t-paul t-paul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if ".." is the best order of names. It feels like maybe swapping the first two would be better, so -o model.stl would result in model.part1.stl, model.part2.stl.


Parameters parameters = Parameters::parse(std::move(arguments), inst->location(), {"name"});

const auto& solid_name = parameters["name"];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "name" is the better, more general choice here. I'd suggest dropping the "solid" part everywhere, although it's not critical as long as it's not visible to the user.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally used name but name() is already used on the AbstractNode, resulting in a compile error.

src/core/primitives.cc Outdated Show resolved Hide resolved
src/core/primitives.cc Outdated Show resolved Hide resolved
src/core/primitives.h Outdated Show resolved Hide resolved
@@ -2113,6 +2113,7 @@ ExportInfo createExportInfo(FileFormat format, const QString& exportFilename, co
exportInfo.format = format;
exportInfo.fileName = exportFilename.toLocal8Bit().constData();
exportInfo.displayName = exportFilename.toUtf8().toStdString();
exportInfo.solidName = "fromMainWindowcc";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a holdover from when I was debugging how exporting works. All my testing is in the CLI path and I have not done any testing exporting from the GUI yet. This is in the path of the GUI export and will probably be removed once I start getting to GUI exports.

{
// FIXME: In lazy union mode, should we export multiple solids?
if (binary) {
char header[80] = "OpenSCAD Model\n";
output.write(header, sizeof(header));
output << solidName << "\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is a user specified value, this must be cleaned and size limited to the allowed 80 characters header. This also should be checked to not start with "solid " as that's the beginning of ASCII STL.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we want to limit the solid name to 80 characters for both binary and ascii STL?

src/io/export_stl.cc Show resolved Hide resolved
src/openscad.cc Outdated

std::string solid_name = "OpenSCAD_Model";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to go to a place that can be called later from GUI too. It's ok if we start with command line support, but it needs to anticipate that this same feature will be available from GUI at some point without rewriting everything.

}


int render_and_export(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If multiple output files are generated, that needs to be available for the dependency generator too (the -d command line parameter).

I wonder if that may even be broken currently as I''m not sure this is covered by test cases.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything special I need to do to make them available? Where do I tell it the filename?

src/openscad.cc Outdated Show resolved Hide resolved
@gsohler
Copy link
Member

gsohler commented Feb 21, 2024 via email

The header needs to be 80 characters long acording to the spec
@DeepHorizons
Copy link
Author

I added the suggested changes and left comments on the ones still open. I would like to see if I can export just one STL with multiple parts, it seems I have to modify exportFileByNameStream() to change the openmode from trunc to app to append to the end of the file. I think I will have to add another field to the ExportInfo struct in a similar way useStdOut is used.

I fixed most of the build errors, there is now just one on windows. Ill have to try to cross-compile and run the tests to see whats going on there.

@gsohler The original code had a newline after the name followed by null characters, this follows the same [now]. Should the newline be removed?

@jordanbrown0
Copy link
Contributor

When I last thought about this, I believe I was thinking of a two-level namespace, where the arguments to part() would be objects. Each parameter would provide metadata for one consumer. Thus, for instance, the stl argument to part() would be an object containing STL-export-specific parameters, slice would contain slicing hints, et cetera.

This requires #4478 for a user-space syntax for creating objects.

@jordanbrown0
Copy link
Contributor

I would like to see if I can export just one STL with multiple parts,

Do you have any consumers that can read such a file?

@DeepHorizons
Copy link
Author

I would like to see if I can export just one STL with multiple parts,

Do you have any consumers that can read such a file?

I have been using OpenFOAM to do simulations that consume STL files. I have tested multi-region with ASCII STL by cating the files together and that worked as expected. I dont know about binary.

@gsohler
Copy link
Member

gsohler commented Feb 22, 2024

Hi DeepHorizons,
In my yesterday's result I was able to assign an individual color to each single triangle and it even works for F6 rendering and for 3MF export of the colored Solid. Look here: https://imgur.com/a/Ux9griH its ready for export.
Its quite versatile already, for every PolySet I maintain a Material array and each Triangle has ah Material index into this table. We could easily add more properties next to color like part name and all that information would also be available for your stl export. You could presort the triangles before actually exporting. What do you think ?

@DeepHorizons
Copy link
Author

@gsohler I would want to try it out but it sounds like it could work.

@UBaer21
Copy link
Contributor

UBaer21 commented Apr 13, 2024

I just like to mention that there is a distinction between Objects and Parts - while the 3mf only know "objects" there are objects with in a model file (parts) and separate object files (objects)

So it will be important to allow hierarchy to group parts to objects and have multiple objects.
e.g. when slicer open 3mf files then parts positions are not changed as this can be different material colors of one object - or modifier/ supports etc.
But objects will be automatically arranged or centered. Also you can print selective objects but not parts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants