• Login
sketchucation logo sketchucation
  • Login
⚠️ Libfredo 15.4b | Minor release with bugfixes and improvements Update

Group moves to origin when I doubleclick to select component

Scheduled Pinned Locked Moved Developers' Forum
20 Posts 4 Posters 863 Views 4 Watching
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    bobdiya
    last edited by 8 May 2014, 12:04

    Here are two groups, each group containing 2 rectangle boxes

    Groups.png

    When I double click on one of the rectangle box, the group moves to the origin. In the above image the origin is out of sight but in below image the groups are moved to the origin.
    BoundingBox.png

    Why does this happen?

    I then printed the bounding box of each of the rectangle boxes in both the groups. I am surprised to see that RectangleBox 1 and RectangleBox 3 have same values. Similarly 2,4. As if to indicate they are same objects. (See the bounds listed below)

    COMPARE 1 and 3, 2 and 4 . They are same

    1 - Rectangle box
    Point3d(0, 21.455, 0) - Point3d(19.8533, 21.455, 0) - Point3d(0, 22.1636, 0)
    Point3d(19.8533, 22.1636, 0) - Point3d(0, 21.455, 4.72441) - Point3d(19.8533, 21.455, 4.72441)
    Point3d(0, 22.1636, 4.72441) - Point3d(19.8533, 22.1636, 4.72441)

    2 - Rectangle box
    Point3d(19.1446, 0, 0) - Point3d(19.8533, 0, 0) - Point3d(19.1446, 21.455, 0)
    Point3d(19.8533, 21.455, 0)- Point3d(19.1446, 0, 4.72441) - Point3d(19.8533, 0, 4.72441)
    Point3d(19.1446, 21.455, 4.72441) - Point3d(19.8533, 21.455, 4.72441)

    3 - Rectangle Box
    Point3d(0, 21.455, 0) - Point3d(19.8533, 21.455, 0)- Point3d(0, 22.1636, 0)
    Point3d(19.8533, 22.1636, 0)- Point3d(0, 21.455, 4.72441) - Point3d(19.8533, 21.455, 4.72441)
    Point3d(0, 22.1636, 4.72441) - Point3d(19.8533, 22.1636, 4.72441)

    4 - Rectangle box
    Point3d(19.1446, 0, 0) - Point3d(19.8533, 0, 0) - Point3d(19.1446, 21.455, 0)
    Point3d(19.8533, 21.455, 0) - Point3d(19.1446, 0, 4.72441) - Point3d(19.8533, 0, 4.72441)
    Point3d(19.1446, 21.455, 4.72441) - Point3d(19.8533, 21.455, 4.72441)

    What I'd like to do?
    I'd like to get the (unique) coordinates of each of the rectangle boxes in 3D space.

    Attached the sketchup file.


    simple1.skp

    1 Reply Last reply Reply Quote 0
    • D Offline
      Dan Rathbun
      last edited by 8 May 2014, 15:53

      It happens because when you are in the group's edit context, you are within the group's co-ordinate system. So the SketchUp interface shows you the axis anchored on the group's origin.

      BUT... the world origin remains unchanged.

      I'm not here much anymore.

      1 Reply Last reply Reply Quote 0
      • D Offline
        Dan Rathbun
        last edited by 8 May 2014, 15:56

        See group.transformation.origin
        and the Geom::Transformation class, in the online docs.

        I'm not here much anymore.

        1 Reply Last reply Reply Quote 0
        • B Offline
          bobdiya
          last edited by 10 May 2014, 08:26

          Dan, Thank you for your reply.

          group.png

          Origin of the group g.transformation.origin is (150, 10, 3). One of the corners' (say P) coordinate of RectangleBox is (19, 0, 0). (RectangleBox is ComponentInstance and belongs to the group.

          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?

          Let me give you the actual picture.
          Problem I am trying to solve: Find all the faces that are touching (in contact with) other faces in the selection.

          The approach

          1. I get all the plys (RectangleBoxes i.e., ComponentInstances) in the selection (Recursive solution).
          2. Then I get all the faces of the plys
          3. 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.

          **Another query:**In get_extremes_of_face method face.vertices returns vertices with respect to the Group's origin. And this is where i will need to offset the origin of the group to get the 'absolute coordinates'. But is there a better way to do this?

                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
                
                def is_face_coplanar_to_another?(face1, face2)
                  face1_extremes = get_extremes_of_face(face1)
                  face2_extremes = get_extremes_of_face(face2)
                  if(face1.area > face2.area)
                    val = check_if_one_extremes_contain_another(face1_extremes, face2_extremes)
                  else
                    val = check_if_one_extremes_contain_another(face2_extremes, face1_extremes)
                  end
          
                  return val
                end
          
                def get_extremes_of_face(face)
                  vertices = face.vertices
                  i = 0
                  array = []
                  x_value1 = []
                  y_value1 = []
                  z_value1 = []
                  for ii in (0..3)
                     array[i] = vertices[ii].position
                     x_value1[i] = array[i][0]
                     y_value1[i] = array[i][1]
                     z_value1[i] = array[i][2]
                     i += 1
                  end
                  x_min = x_value1.min
                  x_max = x_value1.max
                  y_min = y_value1.min 
                  y_max = y_value1.max 
                  z_min = z_value1.min 
                  z_max = z_value1.max
                  value = {;x_min => x_min,;x_max => x_max,;y_min => y_min,;y_max => y_max,;z_min => z_min,;z_max => z_max}
                  return value
                end
          
                def check_if_one_extremes_contain_another(ex1, ex2)
                  if (ex1[;x_min] <= ex2[;x_min] && ex1[;y_min] <= ex2[;y_min] && ex1[;z_min] <= ex2[;z_min])
                    if(ex2[;x_max] <= ex1[;x_max] && ex2[;y_max] <= ex1[;y_max] && ex2[;z_max] <= ex1[;z_max])
                      return true
                    else
                      return false
                    end
                  end
                  return false
                end
          
          
          1 Reply Last reply Reply Quote 0
          • D Offline
            Dan Rathbun
            last edited by 10 May 2014, 13:43

            OK some basic Ruby. Most Ruby collection classes have the Enumerable mixin module included 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 module Kernel is mixed into every Ruby class, by it being mixed into Object.)

            So, use the grep() method from module Enumerable, 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
              }
            }
            

            💭

            I'm not here much anymore.

            1 Reply Last reply Reply Quote 0
            • D Offline
              Dan Rathbun
              last edited by 10 May 2014, 14:48

              @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)

              I'm not here much anymore.

              1 Reply Last reply Reply Quote 0
              • D Offline
                Dan Rathbun
                last edited by 10 May 2014, 15:08

                @bobdiya said:

                The approach

                1. I get all the plys (RectangleBoxes i.e., ComponentInstances) in the selection (Recursive solution).
                2. Then I get all the faces of the plys
                3. 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'm not here much anymore.

                1 Reply Last reply Reply Quote 0
                • D Offline
                  Dan Rathbun
                  last edited by 10 May 2014, 15:16

                  I would think it faster to use Face.classify_point() to check if vertices of one face, lie upon another face.

                  I'm not here much anymore.

                  1 Reply Last reply Reply Quote 0
                  • T Offline
                    tt_su
                    last edited by 11 May 2014, 12:51

                    @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.

                    1 Reply Last reply Reply Quote 0
                    • D Offline
                      Dan Rathbun
                      last edited by 11 May 2014, 14:54

                      @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.

                      I'm not here much anymore.

                      1 Reply Last reply Reply Quote 0
                      • T Offline
                        tt_su
                        last edited by 12 May 2014, 10:38

                        Like an optional argument in Vertex.position perhaps - that takes a transformation argument?

                        1 Reply Last reply Reply Quote 0
                        • T Offline
                          tt_su
                          last edited by 12 May 2014, 10:41

                          Also, do anyone have a test script where current solution is slow?

                          1 Reply Last reply Reply Quote 0
                          • D Offline
                            Dan Rathbun
                            last edited by 12 May 2014, 12:52

                            @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 for Group and ComponentInstance classes.
                            But this may not be the most helpful.

                            Maybe think more of working in "Local" and "World" modes.
                            And we gave a block form world 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
                            }
                            

                            ?

                            I'm not here much anymore.

                            1 Reply Last reply Reply Quote 0
                            • S Offline
                              slbaumgartner
                              last edited by 12 May 2014, 16:12

                              @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 for Group and ComponentInstance classes.
                              But this may not be the most helpful.

                              Maybe think more of working in "Local" and "World" modes.
                              And we gave a block form world 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

                              1 Reply Last reply Reply Quote 0
                              • D Offline
                                Dan Rathbun
                                last edited by 12 May 2014, 18:57

                                Steve, my original idea is morphing into something overly complicated.

                                I never wanted to imply a "world edition" of any Group or ComponentInstance 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.

                                I'm not here much anymore.

                                1 Reply Last reply Reply Quote 0
                                • D Offline
                                  Dan Rathbun
                                  last edited by 12 May 2014, 20:22

                                  @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 for Group and ComponentInstance classes.

                                  Oh.. sorry (brain fart)

                                  Yes the position() method is on the Vertex class, so the proposed world_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 default false arg, that we need to pass true 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 new Geom::Point3d instance, but it's x, y, z would be set to world co-ordinates.

                                  I'm not here much anymore.

                                  1 Reply Last reply Reply Quote 0
                                  • S Offline
                                    slbaumgartner
                                    last edited by 13 May 2014, 00:10

                                    @dan rathbun said:

                                    Steve, my original idea is morphing into something overly complicated.

                                    I never wanted to imply a "world edition" of any Group or ComponentInstance 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?

                                    1 Reply Last reply Reply Quote 0
                                    • D Offline
                                      Dan Rathbun
                                      last edited by 13 May 2014, 02:09

                                      There is NO rule that states that code blocks must be iterative!

                                      For example, normal begin ... end or do ... end code blocks are not themselves iterative. Nor are def ... 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 a for expression that calls a passed code block using a yield 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.

                                      I'm not here much anymore.

                                      1 Reply Last reply Reply Quote 0
                                      • T Offline
                                        tt_su
                                        last edited by 13 May 2014, 08:09

                                        @dan rathbun said:

                                        Yes the position() method is on the Vertex class, so the proposed world_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?

                                        1 Reply Last reply Reply Quote 0
                                        • D Offline
                                          Dan Rathbun
                                          last edited by 13 May 2014, 11:34

                                          @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 created Geom::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 a Geom::Transformation instance, a Geom::Vector3d in world context, OR either an Array or Geom::Point3d offset from ORIGIN.) 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() and Array#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 they respond_to?(:transform). (Add: Any element that does not "respond_to" is returned unchanged.))

                                          💭

                                          I'm not here much anymore.

                                          1 Reply Last reply Reply Quote 0
                                          • 1 / 1
                                          1 / 1
                                          • First post
                                            5/20
                                            Last post
                                          Buy SketchPlus
                                          Buy SUbD
                                          Buy WrapR
                                          Buy eBook
                                          Buy Modelur
                                          Buy Vertex Tools
                                          Buy SketchCuisine
                                          Buy FormFonts

                                          Advertisement