Some, but not all, definition attributes are set
-
I have another little problem that I just can't figure out (after perusing the forums for a few hours).
I have a solid component (the "original") that I add a few attributes to the definition before I copy it, perform some solid "trim" operations on the copy and then attempt to save the volume of the copy as an definition attribute of the original.
` original = Sketchup.active_model.selection[0]
if original.volume != original.definition.set_attribute("timber", "volume_uncut", original.volume)
print("ERROR: Unable to set original's attribute\n")
endtheCopy = model.entities.add_instance(original.definition, original.transformation)
theCopy.make_uniqueperform a number of "trim" operations on theCopy and other solid components
if theCopy.volume != original.definition.set_attribute("timber", "volume", theCopy.volume)
print("ERROR: Unable to write data: #{theCopy.volume}")
end`Using the Eneroth Attribute Editor, I can select the original component and see only the "volume_uncut" attribute -- the "volume" attribute is missing.
The console doesn't report any of the ERROR messages and I've verified the the values for original.volume and theCopy.volume are valid numbers just before invoking the set_attribute methods.
Nothing else is done with the original component than shown here.
I'm at a loss as to where I've screwed up and don't have any more ideas on how to fix it. Any hints?
Thanks
-
Solid operations create a resultant Group. A Group is a special component that has a group flag set and does not appear in the Component Browser. But like any component has a definition.
So a group is really a "special" component instance.Both instances and definition can be assigned attributes. Normally an instance type data attribute would be assigned to an instance's dictionary, not a definition's.
This is because instances can be scaled to change the volume. Sometimes the scaling is along a single axis resulting in a "stretch" of the original. (Different instances of the same definition can be scaled and stretched differently.)Volume: Manifold solid instances of group and components have a built in API method to return the volume, named
volume()
, so there is no real need to waste time computing it and saving it to a dictionary attribute.
http://ruby.sketchup.com/Sketchup/Group.html#volume-instance_method
Now, Jim Foltz created a plugin called Trim and Keep that works around some of the annoying creating of new group from solid operations.
Look it up in the PluginStore.
-
I notice some errors in your coding:
if original.volume != original.definition.set_attribute("timber", "volume_uncut", original.volume)
You cannot both test and set in the same statement like above. This statement will always be true because it sets the attribute every time.
Same for the second attribute volume test later.
You would need to first set the attribute when the instance is first created. Then test later something like:
original.definition.set_attribute("timber", "volume_uncut", original.volume) if original.volume != original.definition.get_attribute("timber", "volume_uncut")
-
Hi, Dan. Thanks for taking the time to respond to my question.
@dan rathbun said:
Both instances and definition can be assigned attributes. Normally an instance type data attribute would be assigned to an instance's dictionary, not a definition's.
This is because instances can be scaled to change the volume. Sometimes the scaling is along a single axis resulting in a "stretch" of the original. (Different instances of the same definition can be scaled and stretched differently.)In the problem domain in which this code will be used (Timber Framing), there isn't usually the need to scale any of the instances but I didn't state that figuring I'd keep it simple (sorry about that!). If a timber is different in dimensions, a new component is typically created.
The extension I'm writing allows me to select any one instance of the timber and it then trims the beams (with tenons), braces (also with tenons), pegs, roof rafters, etc. that intersect with it. The resultant solid is then formatted as a shop drawing, saved to a separate .skp file, and then deleted from the model (thus, losing the any attributes that would have been saved with the instance). I want to use the difference between the volume of the selected component and the volume of the resultant solid to determine the amount of wood that needs to be removed -- this would be used to arrive at a rough estimate of the effort required to cut the timber. Saving this information in the definition makes much more sense in this case since it's the same for all instances using the same definition.
Moreover, if the dimensions of a timber were to change, a new shop drawing would be generated and the definition attributes related to the dimensions would then be updated.
@dan rathbun said:
Volume: Manifold solid instances of group and components have a built in API method to return the volume, named
volume()
, so there is no real need to waste time computing it and saving it to a dictionary attribute.
http://ruby.sketchup.com/Sketchup/Group.html#volume-instance_methodPoint taken. I used volume as an example for the code but there are a number of other attributes (section modulus, moment of inertia, etc.) that I plan on saving.
@dan rathbun said:
Now, Jim Foltz created a plugin called Trim and Keep that works around some of the annoying creating of new group from solid operations.
Look it up in the PluginStore.
Kewl, I'll look this one up.
In light of all this, might you have an idea on why the second set_attribute would not work?
Also, I (re)discovered Layout Tables, maybe this might be a better way to collect up the data associated with the timbers and get it into the 2D drawing. Thoughts?
-
@dan rathbun said:
I notice some errors in your coding:
if original.volume != original.definition.set_attribute("timber", "volume_uncut", original.volume)
You cannot both test and set in the same statement like above. This statement will always be true because it sets the attribute every time.
I think you meant "This statement will always be false ...", right (the comparison is !=)?
Yes. I had read the documentation of set_attribute and it mention that it returns "the newly set value if successful". It doesn't mention what it'll return if unsuccessful so I assumed 'nil' or a null string (in Python, they're different but I'll have to look it up for Ruby). My assumptions were incorrect so I'll change the code to your suggestion (besides, I like your code better) --- THANKS!
Great to have people like you, Dan, to help us neophytes out!
-
I have resolved the issue I originally described. The code that performed the trimming and second
original.set_attribute()
operations were between amodel.start_operation()
andmodel.commit_operation()
. When I moved the secondoriginal.set_attribute()
after themodel.commit_operation()
, I got an error on the console implyingoriginal
was deleted. I modified the code to save a reference tooperation.definition
and then using this to set the attributes,.
After doing this, the code worked as I first expected (I hope that all makes sense). -
@klpauba said:
I think you meant "This statement will always be false ...", right (the comparison is !=)?
yes.
@klpauba said:
I had read the documentation of set_attribute and it mention that it returns "the newly set value if successful". It doesn't mention what it'll return if unsuccessful so I assumed 'nil' or a null string (in Python, they're different but I'll have to look it up for Ruby). My assumptions were incorrect so I'll change the code to your suggestion (besides, I like your code better) --- THANKS!
Since v2016 if the key is nil or empty, it will raise an exception:
Error: #<ArgumentError: Key cannot be empty>
Otherwise the method will convert any argument to a string if it can, and if it cannot for the value, then it'll just silently store
"nil"
in the dictionary.So, basically do validation in your code. (Ie, test for
nil
values etc.)Ruby core has a
#nil?
instance method for all objects that is preferable to:
obj == nil
ie:
obj.nil?
or!obj.nil?
-
@klpauba said:
I got an error on the console implying
original
was deleted.It probably was. As I said, boolean operations create a new original instance that is always a
Sketchup::Group
object, which is an instance of a new clonedSketchup::ComponentDefinition
object (with it's#group?
flag settrue
.)This has bothered users and coders in the past who'd rather like the instances to remain as
Sketchup::ComponentInstance
objects. (Hence Jim's Trim and Keep plugin.)@klpauba said:
The extension I'm writing allows me to select any one instance of the timber and it then trims the beams (with tenons), braces (also with tenons), pegs, roof rafters, etc. that intersect with it.
The problem is that in SketchUp instances do not have geometry collections, only definitions do. So every instance of a certain definition must be geometrically identical. When the user double-clicks to enter the editing context of an instance, they are actually then within it's definition's local geometry collection. So the user is actually editing every one of the definition's instances simultaneously.
To get around this, a user or coder can (like sometimes the Dynamic Component engine does,) make the instance unique, which clones the original picked instance's definition to a new unique definition. Then the user or coder can modify that definition's geometry without effecting any instance's of the original definition.
http://ruby.sketchup.com/Sketchup/ComponentInstance.html#make_unique-instance_method@klpauba said:
I want to use the difference between the volume of the selected component and the volume of the resultant solid to determine the amount of wood that needs to be removed -- this would be used to arrive at a rough estimate of the effort required to cut the timber.
Certainly doable as both definition's would be in the model. You can save the original's volume as you've been doing in the "trimmed" definition, or save an attribute that points at the original definition object.
@klpauba said:
The resultant solid is then formatted as a shop drawing, saved to a separate .skp file, and then deleted from the model (thus, losing the any attributes that would have been saved with the instance).
Right, because saving a component saves it as a component definition SKP.
But usually coders do not separate out the components into separate files. It is easier to insert a new instance perhaps off to the side of the assembly, and have it assigned to a scene specific layer that is visible only on that scene, then this scene will correspond to a viewport in LayOut.
DaveR (who designs furniture) has explained this a myriad of times in the forums.
-
Dan asked me to chime in.
I'm curious about this:
@klpauba said:
The resultant solid is then formatted as a shop drawing, saved to a separate .skp file, and then deleted from the model (thus, losing the any attributes that would have been saved with the instance).
That doesn't seem like an especially good way to work for creating shop drawings. If you need to edit the shop drawings to make changes to dimensions or other details, it sounds like you have a lot of work to do to make sure everything gets updated. If I applied your process to the plans I create for furniture and other woodworking projects, they'd never get completed before deadlines and the inevitable edits would be killers.
As Dan mentioned, it's easier if you make copies of the components and keep them in the same model. And don't delete them from anywhere. For example, this is what the overall model looks like for a recent plan for a workbench.
This is the second sheet of the plan. Each viewport is from a scene created in the SketchUp model.
Since every single part of the bench is a component, modifications to the model and the plan are trivial. I can change something in the assembled version of the model, at the origin, and they are made throughout the model. When the reference is updated in LayOut, the dimensions update as do the labels with dimensions. It's automatic and no errors.
-
Dan & Dave,
Thanks for the feedback you've provided. I'll be re-reading your comments (probably many times) as the methods you both describe would probably work better for me.
-
Dan & Dave,
I was wondering if your collective experience could help me work out a more efficient workflow. The one I've been using is based off of the way the "Timber Framing Rubies" extension worked. That extension works with the free version of Sketchup but I think the Pro version (with its solid tools and Layout), and with the help of some custom code, is much better suited for timber framing design.
Here's a simple "bent" (collection of joined timbers) for the sake of discussion. This bent consists of six components: right and left posts, a beam, right and left braces and a peg (six instances). Only the "male" parts of the joinery are drawn (the tenons) so, in this case, the post is simply a rectangular volume.
Let's say the design is pretty much finished and I wish to generate a shop drawing for the left post. I would select the post and choose the "Make Shop Drawing" item from the context menu. The script would copy the selected post, make it unique and trim the post with each intersecting component (the beam, right brace and two pegs).
It would then create a group (maybe in a layer named "Shop Drawing" perhaps) with four copies of the post (now with all of the mortises and peg holes). The four copies give the parallel projection view of each of the sides of the post that can be brought into a layout viewport for dimensioning. I have the ruby script doing these steps right now (short of creating the new layer) but the shop drawings are saved to a separate file. I was thinking of modifying the script to create a "Post Shop Drawing" Scene showing just the "Shop Drawing" layer the group of four copies positioned in the window with all the right settings (if the script doesn't do that for me).
I would like to keep the original (unmodified) post in place so that if I later decide to, say, replace the 30-60-90 degree brace that's shown with a more conventional 45 degree brace, I can just replace the one that's there, select the post and execute the script once again (presuming I can replace the group in the "Post Shop Drawing" layer with the new one).
I do like the idea of keeping all of this in a single skp file and doing away with the separate shop drawings. Does the workflow I describe above seem reasonable? I welcome any suggested improvements.
-
@klpauba said:
Let's say the design is pretty much finished and I wish to generate a shop drawing for the left post. I would select the post and choose the "Make Shop Drawing" item from the context menu. The script would copy the selected post, make it unique and trim the post with each intersecting component (the beam, right brace and two pegs).
This is correct.
(Not a timber person, but am a fan of "BarnBuilders". Wouldn't the peg holes be drilled in place after the bent is assembled on site? I could see starting a pilot hole in the post.)
@klpauba said:
It would then create a group (maybe in a layer named "Shop Drawing" perhaps) with four copies of the post (now with all of the mortises and peg holes). The four copies give the parallel projection view of each of the sides of the post that can be brought into a layout viewport for dimensioning.
...
I was thinking of modifying the script to create a "Post Shop Drawing" Scene showing just the "Shop Drawing" layer the group of four copies positioned in the window with all the right settings (if the script doesn't do that for me).NO. This is not how it is done for LayOut. ONE copy is made and moved off to the side, and perhaps set to use it's own "detail" layer.
Then, four scenes (camera positions) are created for the 4 viewports in LayOut, each showing one of the post's sides (in each of the 4 scenes the "detail" layer is set "visible" whilst all others are "off" [except for "Layer0" the primitives layer which must always be visible].)@klpauba said:
I have the ruby script doing these steps right now (short of creating the new layer) but the shop drawings are saved to a separate file.
You can have many pages in a LayOut document each with as many viewports that correspond to a scene page in the SketchUp model. It is not really necessary to have separate documents.
BUT, you may if you choose to. A single LayOut document may have scene viewports from multiple SKP models, or a multiple LayOut documents can have scenes from a single SKP model. (The latter seems to be the closest to what you are doing now.)
@klpauba said:
I would like to keep the original (unmodified) post in place so that if I later decide to, say, replace the 30-60-90 degree brace that's shown with a more conventional 45 degree brace, I can just replace the one that's there, select the post and execute the script once again (presuming I can replace the group in the "Post Shop Drawing" layer with the new one).
The original "unmods" could be associated with the "working" layer up until the point when you do the intersection, and make them unique. The code could create a "unmod" copy and put it on a hidden layer, but in proper position for possible mod later.
OR, the code could have a command to recreate (instantiate) an "unmod" post at a later date using the current transform of the "moded" post. (This way you don't have unused geometry bloating the model. The same would need to be done with the beams. I prefer this workflow.)
Another alternative is to build the frame and copy the entire thing perhaps as a unique assembly component, before adding the bracing ? This could be all on a hidden later "in limbo" so to say. So that any timber can be copied "in place" (already having the correct transform) to the main "working" layer.
-
@klpauba said:
That extension works with the free version of Sketchup but I think the Pro version (with its solid tools and Layout), and with the help of some custom code, is much better suited for timber framing design.
Yesterday SketchUp 2018 Pro was released, and with it we now have a Ruby API for creating and accessing LayOut documents from SketchUp. (A live in LayOut API with application hooks has not yet been implemented, but is likely within a version or two.)
Doc: LayOut API overview
Doc: Layout moduleThis is important for you to know that SketchUp Make is discontinued (and will not be updated beyond v2017.) It is replaced by the cloud SketchUp Free (formerly my.sketchup) which as yet has no API, and when it gets one it'll likely be Javascript rather than Ruby.
-
@dan rathbun said:
(Not a timber person, but am a fan of "BarnBuilders". Wouldn't the peg holes be drilled in place after the bent is assembled on site? I could see starting a pilot hole in the post.)
You're correct ... the holes are typically laid out in pencil and not drilled until shortly before raising when the joints are "fit up" to ensure all the joints fit together. Posts are drilled first and then the beam and braces are removed and drilled separately with the hole slightly offset ("draw-bored") so that pounding the peg in will draw the joint tightly together.
@dan rathbun said:
NO. This is not how it is done for LayOut. ONE copy is made and moved off to the side, and perhaps set to use it's own "detail" layer.
Then, four scenes (camera positions) are created for the 4 viewports in LayOut, each showing one of the post's sides (in each of the 4 scenes the "detail" layer is set "visible" whilst all others are "off" [except for "Layer0" the primitives layer which must always be visible].)I could imagine twenty or thirty unique timbers that would need shop drawings in a more complex timber frame. That's a lot of scenes! Automating it would make it easier to generate the scenes/pages but importing all those models and arranging the viewports in Layout would be a pain. However, I'll experiment with generating the scenes as you suggest.
@dan rathbun said:
The original "unmods" could be associated with the "working" layer up until the point when you do the intersection, and make them unique. The code could create a "unmod" copy and put it on a hidden layer, but in proper position for possible mod later.
OR, the code could have a command to recreate (instantiate) an "unmod" post at a later date using the current transform of the "moded" post. (This way you don't have unused geometry bloating the model. The same would need to be done with the beams. I prefer this workflow.)
Another alternative is to build the frame and copy the entire thing perhaps as a unique assembly component, before adding the bracing ? This could be all on a hidden later "in limbo" so to say. So that any timber can be copied "in place" (already having the correct transform) to the main "working" layer.
All excellent suggestions. Your preferred method would be my choice also. Beams (and sometimes posts), however, often have tenons (or other complex geometries) at the ends that would probably make the first suggestion more viable (although I'll think about the others a bit more).
I don't expect to take up more of your time, Dan, but if you have more suggestions please keep them coming!
-
@dan rathbun said:
Yesterday SketchUp 2018 Pro was released, and with it we now have a Ruby API for creating and accessing LayOut documents from SketchUp. (A live in LayOut API with application hooks has not yet been implemented, but is likely within a version or two.)
Thanks, I'll be reading the docs to see how they might automate the workflow we've discussed.
@dan rathbun said:
This is important for you to know that SketchUp Make is discontinued (and will not be updated beyond v2017.) It is replaced by the cloud SketchUp Free (formerly my.sketchup) which as yet has no API, and when it gets one it'll likely be Javascript rather than Ruby.
This will be a disappointment to those who currently use (or plan to use) the TF Rubies extension. I suspect they will continue to use the 2017 version until the web-based version allows the use of extensions. I don't have much concern since I have Pro.
-
Wanted to point you towards a tutorial that is posted on the "official" Trimble forums:
[Tutorial] Ten Fundamentals About LayOut for Architects
[Create LayOut File from Scenes] talk, bugs & feature requests
-
Thanks for your continued help, Dan.
I've been closely following the methods outlined in "Sketchup & Layout for Architects" by Sonder and Donley (see http://sketchupbook.com). I use the Sketchup and Layout templates that are provided with great success -- the documents that are generated from all of the scenes look great (although I'm still learning -- there's a lot of information provided in the book that I have yet to understand and/or utilize). The methods in the book appear to expand the concepts outlined in the "Ten Fundamentals about LayOut for Architects" tutorial.
Modern, stick-built structures don't require the architect to detail each unique lumber component, fortunately. Timber Frame builders, however, often require detailed dimensions for each unique timber. This is where I think the methods for timber construction might need to add some new steps to the process.
A frame I'm currently working on (a 744 sf garage) has 24 different frame members. Using the same type of methods in the book I would have to create 76 different scenes (24 * 4) for the four views of each member. I would then create 24 layout pages each having four viewports for each of the members so that dimensions can be added. I'm just trying to wrap my head around how I might automate these tasks in an effective way -- especially when there are several design iterations that would require regenerating some scenes and layout pages.
Advertisement