Group moves to origin when I doubleclick to select component
-
OK some basic Ruby. Most Ruby collection classes have the
Enumerable
mixin moduleinclude
d within them.You can always check via the
ancestors()
method (which will list mixed in modules, at the point in the class hierarchy when they were mixed in.)
Sketchup::Entities.ancestors %(#004040)[>> [Sketchup::Entities, Enumerable, Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]]
(Notice also that moduleKernel
is mixed into every Ruby class, by it being mixed intoObject
.)So, use the
grep()
method from moduleEnumerable
, because it is very fast.
It can replace this entire method:def get_all_faces(ply) entities = ply.definition.entities all_faces = [] for entity in entities if entity.instance_of?(Sketchup;;Face) all_faces << entity end end return all_faces end
with:
all_faces = obj.entities.grep(Sketchup::Face)
If
grep()
does not find any objects of that class, or it's subclasses, then it returns an empty array.But,
grep()
also has an optional block form, in which the result array is passed into a block if supplied:all_faces = obj.entities.grep(Sketchup;;Face) {|faces| faces.each {|face| # check something about each face here } }
-
@bobdiya said:
Seeing the value (19,0,0) and the position of the point P, it is clear the coordinate is with respect to the Group's coordinate system. So I need to add the group's origin to get the coordinates of P in Sketchup coordinate system. That would be - (169, 10, 3).
Question: This looks like the hard way. Is there any direct way?
Nope. (It is something coders have asked for forever, but has not been added to the API.)
So coders need to apply each nested level's transformation to get the world co-ordinate:
worldcoord = inst.transformation.origin.transform(grp.transformation)
-
@bobdiya said:
The approach
- I get all the plys (RectangleBoxes i.e., ComponentInstances) in the selection (Recursive solution).
- Then I get all the faces of the plys
- I go on to compare each face against every other face to see if they are in contact. If they are then I add it to the resulting array.
Component Instances do not "have" faces, because they do not "have" entities collections.
It is the Component Definition that has the entities collection.
ALL instances of THAT definition shares the entities. Edit one of the instances, and you edit them ALL.
So you need only get the faces once, and hence the relative vertices once.
-
I would think it faster to use Face.classify_point() to check if vertices of one face, lie upon another face.
-
@dan rathbun said:
@bobdiya said:
Seeing the value (19,0,0) and the position of the point P, it is clear the coordinate is with respect to the Group's coordinate system. So I need to add the group's origin to get the coordinates of P in Sketchup coordinate system. That would be - (169, 10, 3).
Question: This looks like the hard way. Is there any direct way?
Nope. (It is something coders have asked for forever, but has not been added to the API.)
Given a single vertex you cannot return a world coordinate since it could belong to an Entities collection that might appear several places in the model.
You need to have the combined transformation of the specific path you want the world coordinate for. I don't see what type of method would improve over what one already do (collecting the nested transformation.) Suggestions are welcome. -
@tt_su said:
You need to have the combined transformation of the specific path you want the world coordinate for. I don't see what type of method would improve over what one already do (collecting the nested transformation.) Suggestions are welcome.
You know that is exactly what we'd like.
We would like the heavy work done on the C-side, so it is fast, and we only need call a single method.
-
Like an optional argument in Vertex.position perhaps - that takes a transformation argument?
-
Also, do anyone have a test script where current solution is slow?
-
@tt_su said:
Like an optional argument in Vertex.position perhaps - that takes a transformation argument?
Brainstormin'Umm. I was thinking more of a
.world_position()
instance method forGroup
andComponentInstance
classes.
But this may not be the most helpful.Maybe think more of working in "Local" and "World" modes.
And we gave a block formworld
and (maybe)local
methods.
(Sort of the old "with this do that" construct.)component_instance.world {|inst| # any vertice, point3d, vector3d, etc., # accessed within block is in world co-ordinates }
?
-
@dan rathbun said:
@tt_su said:
Like an optional argument in Vertex.position perhaps - that takes a transformation argument?
Brainstormin'Umm. I was thinking more of a
.world_position()
instance method forGroup
andComponentInstance
classes.
But this may not be the most helpful.Maybe think more of working in "Local" and "World" modes.
And we gave a block formworld
and (maybe)local
methods.
(Sort of the old "with this do that" construct.)component_instance.world {|inst| > # any vertice, point3d, vector3d, etc., > # accessed within block is in world co-ordinates > }
?
Seems to me that to avoid complete chaos for Components, it would need to be necessary either to make any "world" version of the Component read-only or else to intercept all of the setter methods on Entities and apply the reverse transformation back to the ComponentDefinition's "local" coordinates. Otherwise, any modifications made to the "world" version within the code block would create a mess in the ComponentDefinition! This seems like a terrible amount of complexity and risk of bugs!
Here's a different idea: what if there was a "export_to_world" method on ComponentInstance that would create a new Group containing copies of the ComponentDefinition's Entities transformed into "world" coordinates per that ComponentInstance's Transformation(s)? Because the copy's Entities would no longer be shared with the ComponentDefinition, there would be no risk of messing it up. Alterations made to the Group would not affect the ComponentDefinition or any of its ComponentInstances.
Steve
-
Steve, my original idea is morphing into something overly complicated.
I never wanted to imply a "world edition" of any
Group
orComponentInstance
instance object.I really meant that the
origin
(which is actually a property of a transformation object,) be returned in world co-ordinates instead of local co-ordinates. (So the instance object is not changed.)But this only within some scope mechanism. (A method block is only one such example.)
Copying an instance into the world model entities, is easy enough now.
I did not intend something that modified the model directly. I was aimed at simplifying virtual calculations.
-
@dan rathbun said:
@tt_su said:
Like an optional argument in Vertex.position perhaps - that takes a transformation argument?
Umm. I was thinking more of a
.world_position()
instance method forGroup
andComponentInstance
classes.Oh.. sorry (brain fart)
Yes the
position()
method is on theVertex
class, so the proposedworld_position()
, would also have to be upon this class.
I do not known what would be better, a new method, or adding an optional:world
symbol (or"world"
string,) argument. (I DO know I hate positional boolean args, so I'd rather not see a defaultfalse
arg, that we need to passtrue
in order to get world co-ordinates.)Above, where I ponder about a scope (or method block,) ... creating any of the "virtual" dimension classes (in module
Geom
,) would return new object using world co-ordinates,. instead of local co-ordinates.So in such a block
edge.start.postion
(for an edge that was nested at some level,) would return a newGeom::Point3d
instance, but it's x, y, z would be set to world co-ordinates. -
@dan rathbun said:
Steve, my original idea is morphing into something overly complicated.
I never wanted to imply a "world edition" of any
Group
orComponentInstance
instance object.I really meant that the
origin
(which is actually a property of a transformation object,) be returned in world co-ordinates instead of local co-ordinates. (So the instance object is not changed.)But this only within some scope mechanism. (A method block is only one such example.)
Copying an instance into the world model entities, is easy enough now.
I did not intend something that modified the model directly. I was aimed at simplifying virtual calculations.
So I guess I miss the point of the code block you suggested...what would it be iterating over?
-
There is NO rule that states that code blocks must be iterative!
For example, normal
begin
...end
ordo
...end
code blocks are not themselves iterative. Nor aredef
...end
code blocks iterative.A block of code is a scope mechanism. It can be iterated by calling it multiple times from an iterative expression such as
for
. Or a method block, can contain afor
expression that calls a passed code block using ayield
expression.What I am proposing is most like the new Ruby 2.0
using
keyword, and the refinement construct. In fact I said that above, where I called it a "with this do that" concept. -
@dan rathbun said:
Yes the
position()
method is on theVertex
class, so the proposedworld_position()
, would also have to be upon this class.But you cannot return a single world position for a vertex unless you have some extra info. If the vertex belong to a definition with multiple instances there are multiple possible world positions.
And there was some mention of performance concern - did anyone have a sample script of this?
-
@tt_su said:
But you cannot return a single world position for a vertex unless you have some extra info.
RIGHT.. I gotcha'
We would need to let Ruby know the parent instance's transform. (Not automatic unless in user edit mode.)
I guess we can do this now. We just create a copy of the instance's transformation, call it
**t**
:
world_pt = edge.start.position.transform(t)
I guess I was pondering how to automatically call#transform(t)
upon all newly createdGeom::Point3d
instances, within a code block scope.So, yes I suppose an optional "tranform" class argument for
Vertex#position
would be handy. (It could be aGeom::Transformation
instance, aGeom::Vector3d
in world context, OR either anArray
orGeom::Point3d
offset fromORIGIN
.) So it could look like:
world_pt = edge.start.position(t)
(2) So lets say you have collected a series of vertices (in an array
verts
.) And you want their world co-ordinates:
world_pts = verts.map {|v| v.postion(t) }
We have to use map, because the API's
Array#tranform()
andArray#tranform!()
methods, refuse to apply a transform to an array with anything other than than 3 numerics.
Even though each individual element has a transform method.Example, you have an array of
Geom::Point3d
objects, and you want to transform ALL elements the same.
The Array class transform methods should do this IF the the elements are not numeric, and theyrespond_to?(:transform)
. (Add: Any element that does not "respond_to" is returned unchanged.))
Advertisement