nophead / NopSCADlib

Library of parts modelled in OpenSCAD and a framework for making projects
GNU General Public License v3.0
1.2k stars 156 forks source link

Suggestion for improvement to variants and their assembly instructions #186

Closed martinbudden closed 3 years ago

martinbudden commented 3 years ago

As I understand it, the build system searches for the module main_assembly and uses it to build the project. The build instructions are scraped from the text preceding main_assembly.

Can I suggest that instead the build system searches for the module variant_assembly and if found, uses that, and if it is not found it goes on to look for main_assembly.

This has two main benefits

  1. It allows different top level assembly instructions for different variants. Generally speaking variants can share the same top level assembly instructions, but if variants differ significantly then this becomes restrictive.
  2. I allows the creation of assembly instructions for sub-assemblies, and in particular allows variants of sub-assemblies. So, for example, a 3D printer might support two different print heads. Rather than create two variants of the printer, one with each print head, you could create one variant of the printer and a variant which just contained the alternate print head.
nophead commented 3 years ago

I don't really follow what you mean. By variant_assembly do you mean literally that or with the variant replaced, such as <$target>_assembly

Point two seems to say "instead of two variants create one variant and a variant" and that seems like two variants to me.

Possibly you can do what you want with a main_assembly() that calls one of several assemblies depending on $target. If main_assembly is just a shell it gets removed from the instructions. That is if it has no parts and only calls one assembly(). That assembly then becomes the top level assembly.

martinbudden commented 3 years ago

I'll try and explain a bit more.

For the first case, let's assume I have two 3D printer variants, BC200 and BC200CF, BC200CF being a carbon fiber version of BC200. Currently I have:

//! Generic assembly instructions here
module main_assembly()
assembly("main") {
        if ($target == "BC200")
            BC200_assembly();
        else
            BC200CF_assembly();
}

what I'd like is to have:

//! BC200 assembly instructions here
module BC200_assembly()
assembly("BC200") {
...
}

//! BC200CF assembly instructions here
module BC200CF_assembly()
assembly("BC200CF") {
...
}

Since the BC200 and BC200CF variants have enough differences to benefit from different top level instructions. So the build system should search for, in the first case, BC200_assembly rather than main_assembly.

For the second case, lets assume the standard printer uses an E3D hotend, but I'd also like the option to add say a Mosquito hotend, so I'd like to be able to generate the assembly instructions for just the Mosquito printhead without having a variant that is the whole printer with a just a different printhead.

If I had a Mosquito printhead subassembly called, say, MOSPRINTHEAD,

//! Mosquito printhead assembly instructions here
module MOSPRINTHEAD_assembly()
assembly("MOSPRINTHEAD") {
...
}

So then I could call make_all.py MOSPRINTHEAD to generate the Mosquito printhead assembly with it's build instructions, so the build system would need to search for MOSPRINTHEAD_assembly and scrape the top level build instructions from there.

So, putting it all together, from within the project I could call make_all.py BC200 to generate the BC200 variant, with its top level build instructions make_all.py BC200CF to generate the BC200CF variant, with its top level build instructions make_all.py MOSPRINTHEAD to generate just the Mosquito printhead subassembly and its build instructions, BOM etc

nophead commented 3 years ago

Well you can already do that with another if statement. E.g.

// No instructions here
module main_assembly()
assembly("main") {
       if ($target == "BC200")
            BC200_assembly();

       if($target == "BC200CF_assembly")
            BC200CF_assembly();

       if($target == "MOSPRINTHEAD")
             MOSPRINTHEAD_assembly();
}

No need for elses because only one if at a time can be true.

In your example I don't think the //! Generic assembly instructions here will end up in the manual as the main_assembly has no parts and only one sub-assembly, so it is removed.

Your //! Mosquito printhead assembly instructions here will appear as the top level instructions when selected and if MOSPRINTHEAD_assembly() only includes the printhead that is all that will be in that version of the instructions.

Are you saying you just want to avoid the if statements by the build system looking for <target>_assembly and using that in preference to main_assembly if it is found? I don't think it adds anything that can't be already done, it just removes the need for a main_assembly() with its list of ifs.

martinbudden commented 3 years ago

No, the purpose was not to avoid the if statements, it was to enable different top level instructions. I was not aware that the main assembly would be removed if it had no parts and only one sub-assembly.

So it looks like the system currently supports my needs. I try this out and report back.

martinbudden commented 3 years ago

OK I've tried this out and realised I didn't quite explain things properly, mainly because I'd failed to differentiate between the blurb for the project and the blurb for the main assembly. To be clear, let's define the "project blurb" as the blurb that comes before the table of contents, and which contains the title, description and optionally a picture. The main assembly blurb is the blurb associated with main_assembly. There is currently certainly sufficient flexibility in handling the main assembly blurb.

The suggestion I am making is to allow different "project blurb" for variations.

Currently the build system finds the project blurb as follows:

  1. Search for a file containing main_assembly
  2. Scrape the project blurb from this file.

What I suggest is:

  1. Search for a file containing <target>_assembly, eg `BC200_assembly
  2. If file found,scrape the project blurb from this file.
  3. If file not found, search for a file containing main_assembly and scrape the project blurb from this file.

The problem is that for the MOSPRINTHEAD variant described above, the project blurb is that for the whole printer, not just some blurb for the printhead.

nophead commented 3 years ago

I see, so MOSPRINTHEAD is really a different but related project sharing some files, rather than a variant.

It would be more flexible and less clunky to do what you propose. Variants could share blurb or not by placing them in separate files or the same file.

martinbudden commented 3 years ago

Well, I would regard MOSPRINTHEAD as a subproject rather than a project, but the mechanism I propose would allow the build system to support separate project that shared a number of files, which is actually also very useful.

I think this would be quite straightforward to implement.

nophead commented 3 years ago

There is a mechanism for making accessories to a project that was inherited from my Mendel90 code. It has probably bit-rotted as I haven't tested it, so I can remove that. and replace it with this.

nophead commented 3 years ago

Having implemented this I realise you need a config_MOSPRINTHEAD.scad file. to make it work.

In my 3D printer project most assemblies would need some values from the target specific config but if the target is set to a sub assembly that isn't selecting a printer config. To allow MOSPRINTHEAD as a command line target it needs to have a config file and in my case that would need to include one of my printer configs. Granted some parts might not change from one version of the printer to another not much would compile without the config for its parent printer.

martinbudden commented 3 years ago

Yes, I assumed a MOSPRINTHEAD_config.scad file would be required. I don't see that as problematic.

nophead commented 3 years ago

Another issue is if you have target independent platters and panels it tries to make those and fails because they aggregate parts of the parent 3D printer that don't exists in a tool head variant. So it it works but is messy because it isn't a normal target variant of the project, it is a subproject that is trying to hijack the target mechanism.

I don't know how it can know to not build the platters and panels. I think what is needed is a mechanism to have a sub-project.

martinbudden commented 3 years ago

Clearly there are some restrictions on how this can be used. But it does address some useful use cases.

I'm not clear, are you saying that because there are some problematic cases, that you don't want to merge your changes in?

nophead commented 3 years ago

I am willing to merge it because it allows the top level project description to vary with the target, which might be useful.

I don't think it really works for making sub-projects. With Mendel90 I had the concept of accessories, which were assemblies not included in the main project. For example camera and lights. They had to remain separate because we were selling kits and the main source had to represent what was in the kit.

Accessories where orthogonal to machine variants as, for example, the lighting brackets depended on the machine's frame dimensions. They got their own BOM and STLs in subdirectories of the bom and stl directories called accessories but they weren't segregated further, accessories was a flat directory with all of them in it. There were no assembly views or instructions because that wasn't automatic in Mendel90. I produced them manually and blogged about them.

Not hijacking the target system makes it more work to implement and if your tool head works for all your machine variants isn't really correct either. I am not sure how a general version would work.

martinbudden commented 3 years ago

I am willing to merge it because it allows the top level project description to vary with the target, which might be useful.

Great, since I think that is the primary use case.

If this works for sub-projects that would be a bonus. I'll experiment and see if it supports my use cases.

nophead commented 3 years ago

Merged 823f3b936e6c33897445d3f3272b69237f013537

martinbudden commented 3 years ago

I've tried this now. It works very well. I've created both a variant with different instructions and subproject. I had no problems creating the subproject, possibly because I had already done quite a bit on organising the project parameters.

I think this issue can be closed.

martinbudden commented 3 years ago

I now have a published project using this feature. It's a small CoreXY 3D printer

Here's the main variant: https://github.com/martinbudden/BabyCube/tree/main/BC200

And here's a subproject variant, a separate printbed https://github.com/martinbudden/BabyCube/blob/main/PRINTBED120/readme.md

nophead commented 3 years ago

Very nice. I might make one to take to Tenerife if I don't get my design finished this year.