Hi,
For each scene, I need to align the view with the biggest face (programmatically).
How could I code that?
Thanks for your suggestions!
Hi,
For each scene, I need to align the view with the biggest face (programmatically).
How could I code that?
Thanks for your suggestions!
Collect all of the faces:
model = Sketchup.active_model
faces = model.active_entities.grep(Sketchup::Face)
Find the largest one:
area = 0
face = nil
faces.each{|f| (face = f; area = f.area) if f.area > area }
Find its center and normal
center = face.bounds.center
normal = face.normal
Make a new camera with its target=center
, eye=center.offset(normal)
and up=Z_AXIS
cam = Sketchup::Camera.new(eye, target, up)
Get a handle to the current view and change its camera.
view = model.active_view
view.camera = cam
Zoom extents and save scene.
new_view = view.zoom_extents
page = model.pages.add('Scene 1') # Choose name ?
# Also choose the page settings too ?? line use_camera = true
Thank you TIG for the advices.
For the moment, I am stuck at the very first step: how to go from a âpageâ to its âfacesâ?
A Page has no âentitiesâ. Model does.
Iâve tried to assign the page to the model, but without any succes: I always get the same set of entities (while each of my scenes shows different parts of the whole model)âŚ
@mod.pages.each {|page|
@mod.pages.selected_page = page
puts ââ#{page.name}ââ
@mod.active_entities.each{|e| puts â\t#{e.class}â}
}
Any idea about what I am doing wrong?
Thx.
You are right, pages do not own collections of Entities, they just remember various settings such as camera, style, etc. The model itself and ComponentDefinitions (associated with Groups and Components) are the only things with Entities collections. So, you need to choose some way to find the face you want to align with. For example, have the user select it first. Or code some logic to find it.
Thank you slbaumgartner for your reaction.
In each scene, I need to select (programmatically) the biggest face.
My starting point is then something like this:
@mod.pages.each {|page|
# find the biggest face of the scene
⌠??? âŚ
⌠??? âŚ
}
@TIG or another of the code gurus may have ideas of how to proceed efficiently, but I fear this is going to be very complicated! I canât quite get my head around the needed explicit code, so Iâll just discuss concepts. Others may correct me if they know better - I like to learn .
By âbiggest faceâ I assume you mean âbiggest visible faceâ? Otherwise the goal doesnât make sense to me. Why would you want to align to something that canât be seen? In the same vein, I canât imagine you mean âbiggest face in the modelâ because then all your scenes would be aligned the same way.
Some aspects of âvisibleâ are easy to test in Ruby, e.g. the hidden flag or the visible setting of the associated layer. Others are much harder, such as whether the face is off-screen or is obscured by something nearer to the camera in a particular View.
A Page (aka scene) object does not have an associated View per-se. Rather, it has a collection of parameters that tell SketchUp how to position the camera and adjust other settings to re-create a View. So far as I know, the only practical way to steer SU to the desired View is to make the desired Page âselectedâ via the Pages#selected_page= method. And, of course, this will actually cause that Page to display to the user.
Once a desired View is active, the Ruby API doesnât contain any method along the lines of âall visibleâ. Rather, it provides a few methods by which to probe the model in the View to see what is there. Some of these apply only within a Tool when the user clicks on a desired point on the screen. Since you donât want user control, I wonât discuss those. Model#raytest will probe to see what is found on a specific ray through the active View in the active model. You can get the ray through a screen pixel from the current camera via View#pickray. But to test the whole model for visibility it seems to me you would have to iterate these tests over the entire screen, which would be very slow!
@Arbiorix, Some snippets that work for the top level model entities ONLY:
def viewable?(obj)
view = @mod.active_view
obj.vertices.all? {|v|
coords = view.screen_coords(v.position)
coords.x.round >= 0 && coords.y.round >= 0
}
end
def find_biggest_face(faces)
faces.max_by {|f| f.area }
end
def find_viewable_faces(ents)
allfaces = ents.grep(Sketchup::Face)
allfaces.select {|face| viewable?(face) }
end
def task()
@mod = Sketchup::active_model
ents = @mod.entities
faces = find_viewable_faces(ents)
biggie = find_biggest_face(faces)
end
Sketchup.active_model.selection.add(task)
It is much more complex if you need to test faces nested inside group or components.
@slbaumgartner, thanks again for your implication.
In order to clarify the context, Iâll describe here the situation.
Iâve completed the âdrawingâ of my project (something to hold music partitions).
For the moment, notice that there is no scene, nor (extra) layer.
Before this becomes a real object, I need to dimension each piece.
My current strategy is:
Up to now, with the following script, I am satisfied.
@mod = Sketchup.active_model # Open model
def groupToScene(group,prefix)
if group.visible?
# Create the name
@name = if prefix.empty?
group.name
else
(prefix.dup << group.name).join(" - ")
end
# Create a layer and assign the group to it
group.layer = @mod.layers.add(@name)
# Create a scene
@mod.pages.add(@name)
end
end
def groupsToScenes(root,prefix)
root.entities.grep(Sketchup::Group)
.sort { |a, b| a.name <=> b.name }
.each { |parent|
if parent.entities.grep(Sketchup::Group).count > 0
groupsToScenes(parent,prefix << parent.name)
else
groupToScene(parent,prefix)
end
}
end
@mod.options["PageOptions"]["ShowTransition"]= false
# Create an 'All' scene
@mod.pages.add("All")
# Create a Scene for each (leaf) group
groupsToScenes(@mod,[])
# Format all Scene
@mod.pages.each {|page|
if page.name != "All"
# Set Layer visibility for each scene
@mod.layers.each {|layer|
page.set_visibility(layer,page.name == layer.name)
}
end
# Zoom on the biggest face
# TBC
@mod.pages.selected_page = page
@mod.active_view.zoom_extents
page.update(1)
}
After running the code, the layers and scenes have been created.
Ouuups, new users can only post 1 image⌠(see image 2 in next post!)
But I want more! I would like each piece to be âalignedâ by the script, so I can directly start to dimension it. As you said, by convention, if the script align the piece with the biggest face area, itâs Ok for me.
To summaryse, how to complete my code with the equivalent of this manual operation?
Ouuups, new users can only post 1 image⌠(see image 3 in next post!)
Thanks for reading - and thinking !
Please edit your code post (above) to wrap code in triple backtick lines:
```ruby
# code here
```
It will lexed like the snippets I posted above:
How to "align view" in ruby - #7 by DanRathbun
Iâve âblockquotedâ the code part, but I am not sure that it is exactly what you want (I donât really see the difference!)
It isnât. I explained how to do it properly.
We all see the difference. Your âblockquoted textâ does not show proper indents, nor correct color lexing.
Yes, of course, I see it now !
Iâll try again âŚ
Now all that aside. Your going about this the âhard wayâ. What we do is, make the objects components.
The assembly would be associated with an assembly layer. Then switch off that layer when making the scenes.
Then insert new instances off to the side somewhere aligned with the global axes. (Actually they can be inserted at the ORIGIN
if theyâll all be associated with a unique layer that is only on for one scene page.)
Then just use the standard Top, Side, Front, etc. views to set up scenes. You can associate the second instances each with a layer of itâs own.
See the Sketchup::send_action()
method for using the standard views:
Module: Sketchup â SketchUp Ruby API Documentation
If doing this by code, youâll need to temporarily set scene transitions off, and use a FrameChange or View observer to know when the view is ready to be updated, ie, calling view.update()
.
Itâs a bit better now. But I still canât get the colors (so stupid!)âŚ
The strings are lexing in color now.
The first line of triple backticks needs a language name (ruby, text, javascript, C, html, css, xml, ⌠etc.) immediately following the three backticks, in order to be lexed correctly.
BTW, Ruby uses 2 space indents. (Tabs do not transfer well to the forum.)
Thank you @DanRathbun for your energy!
For the formating of the code on the forum, I think I finally got it !
About making each piece a component, [quote=âDanRathbun, post:15, topic:41621â]
What we do is, make the objects components.
[/quote]
this goes against my current understanding of what a Component is. I though a Component is something that has multiple instances. But Ok, letâs discover a new usage of the Component!
So, instead of splitting the whole object in groups, I should split it in components.
Then, to dimension the various pieces, I do it on a new instance of each component. Right?
Can you confirm this strategy? Do you know any good tutorial about this process?
Jumping back inâŚ
Yes, a primary purpose of components is to allow multiple instances that all continue to match if you edit any one of them. But there is nothing that says there have to be multiple instances. Groups and Components are the two ways you isolate edges and faces so that they donât interact with ones from other groups or components or loose ones in the model.
For creating âexplodedâ diagrams, views of individual parts, doors in open vs closed position, etc. it is very common practice to make each part a component and to place a copy aside from the location where it fits into the completed assembly. You can rotate the copy in whatever way makes it show as you desire. By associating different layers with these copies, you can create scenes that each show any required set of component pieces and their dimensions (also associate the dimensions with layers so they arenât clutter on scenes where they arenât appropriate).
I do that all the time in my furniture designs.
Hereâs an example that has annotations rather than dimensions, but you can see the idea
Ok, thank you @slbaumgartner.
You put me back on the right way!