Draw a new component with axes at original pick point?
-
I'm progressing slowly with my planned Ruby to help draw timber frames, based on Jim Foltz's CLineTool, and with help from TIG in response to a couple of previous posts.
So far, I can get it to draw a 'box' shaped component correctly aligned with any World axis, with a bounding box properly aligned around the component.
But I can't see how (in Ruby) either to draw the component with its axes at the first pick point, or to re-locate the component's axes from the World origin to the original pick point after it's been drawn. Whatever I try, I seem to draw the component with its origin at the World origin.
The component is drawn by adding a face from an array of Points3d that define the cross-section of the timber relative to the pick point in the desired orientation, drawing them in World location by 'adding' the pick point position, then switching to the built-in PushPull tool to pull the face out to the desired length.
I've searched the forum, but can't find any examples of code that quite address this issue, except one old one which basically says 'you can't use Ruby to relocate a component's axes.'
Even if that is still true, is it possible to draw the first instance in Ruby with the component axes at the pick point? If so, how?
Alternatively, do I have to draw the face (preferably without displaying it) at the origin, then move it to the pick point in preparation for the user to make it into a box using the PushPull tool?
I think I could probably get that to work but it seems a bit inelegant.
Thanks if anyone can advise me.
John McC
-
You don't move the origin per se. You move all the geometry inside the component so that the point you want is moved to 0,0,0. THEN you probably have to move the component itself by the reverse transformation to put it back to where it was, but with the origin now moved. so it should work, its just a 2 step process. Let me know if that works or not?
Chris
-
When you first "draw" a component, you are creating the primitives that will be held by the
Sketchup::ComponentDefinition
's entities collection. You do this with the origin at 0,0,0.Later, when someone (incl. you,) wants to insert a
Sketchup::ComponentInstance
of that definition, the picked point is used.
WITHIN the context of the component, objects are located from the component's origin.The API dictionary has sample code that shows how a
Sketchup::ComponentInstance
is added to the model's entities, using aGeom::Transformation
with a point argument, (which you will pass as the picked point.)
See: Entities.add_instance()The main difference from the sample, is that it is loading a definition from an external skp file, whereas I think you are creating the definition realtime.
So also read: DefinitionList.add() for creating a new emptySketchup::ComponentDefinition
, into it'sentities
, you would add the primitives.Now IF you already have the instance IN the model (say, you went the group route, and used
Group.to_component
which leaves the instance,...) you can just apply a transformation to it using: ComponentInstance.transform!Regardless, moving things around requires you to get comfy with the Geom::Transformation class.
-
@johnwmcc said:
..., is it possible to draw the first instance in Ruby with the component axes at the pick point? If so, how?
I suppose, you could create an empty definition, add an instance of it at the pickpoint (which will be empty, as it's definition's entities contain nothing,) then begin adding primitives etc. to the definition's entities collection.
-
As Chris says...
You can't relocate the axes of a component [or group] using the API.
BUT you can transform all of its geometry and then make an inverse transformation of its instance so that it looks exactly the same in the model, as it dis but its axes appear to have changed: in face the axes have stayed [they are always at 0,0,0 inside the container's context], so in the same 'place', but everything else has changed around them, like the geometry and the instances transformation that mirror it...Look at this http://forums.sketchucation.com/viewtopic.php?p=398885#p398885 it relocates the group's origin at the ORIGIN... expand it with a rotation transformation too if needed...
-
component_instance.transformation.origin
The component definition is based around [0,0,0]. -
Well, I'm ALMOST there, following the advice so generously and quickly given.
My first two points are the initial picked point @ip1, and the current mouse position @ip. I pick on a corner of an existing component, NOT at its origin, drag the mouse to place the cross-section in the correct orientation, then click again.
In the method onLButtonDown on the first click I set a transform @tf = @ip1.transformation.
I pass the position of these points to the method 'draw_geometry(pt1, pt2, view)' where pt1 = @ip1.position and pt2 = @ip.position.
I copy the points defining the cross-section into an array @pts0[] with one corner at the origin. I then transform these points to @pts[] using
@pts0.each_index {|i| @pts[i] = @pts0[i].transform(@tf)}
I can then draw the cross-section in the method 'draw_geometry(pt1, pt2, view)' in the right orientation using view.draw_polyline(@pts), and also draw the cross-section as a component @comp (initially just a face) at the origin in 'create_geometry(pt1, pt2, view)', then move and rotate it back to (almost) where I want it using
But in both cases, the cross-section is drawn NOT at the location @ip1 (which is where I expected it to go), but at the origin of the existing component on whose corner I clicked.
If I click somewhere in the model not on a component, the cross-section is drawn correctly oriented at the World origin, not at @ip1.position.
I thought that the @ip1.transformation would transform a point [0,0,0] to the location of the pick point @ip1, NOT to the origin of the component it is located in (or World origin if the pick point is not located in existing geometry). I seem to have misunderstood this.
How can I retrieve the coordinates of this component origin, so as to define a vector from there to @ip1.position, and locate the cross-section correctly?
Thanks again in advance if you can help further.
John McC
-
I don't think I have a handle for the component_instance that I click on - just the @ip1 pick point - so I'm not sure how I'd use your suggestion, TIG.
But I think Martin Rinehart's Edges to Rubies might have the clue - the translate elements of the @tf.to_a array elements 12, 13, and 14 will contain the coordinates of the picked-on component's origin, I think. (Must look it up to be certain.) Will try this later today, and see if it works.
John McC
-
@johnwmcc said:
I don't think I have a handle for the component_instance that I click on - just the @ip1 pick point - so I'm not sure how I'd use your suggestion, TIG.
But I think Martin Rinehart's Edges to Rubies might have the clue - the translate elements of the @tf.to_a array elements 12, 13, and 14 will contain the coordinates of the picked-on component's origin, I think. (Must look it up to be certain.) Will try this later today, and see if it works.
John McC
If you get the object.transformation, make it an array [.to_a] and extract the elements [12][13][14] for the x/y/z and then assemble a point from those..., it's the same as using object.transformation.origin which return the point ready made.Why don't you have the transformation ?
You can use a view pickhelper 'best_picked' or 'picked_element' method to get the object under the cursor and then from there its transformation...
https://developers.google.com/sketchup/docs/ourdoc/pickhelper#best_picked
https://developers.google.com/sketchup/docs/ourdoc/pickhelper#picked_element -
I'll try the pick_helper, then. Thanks again.
-
I was hoping this discussion might resolve an issue I've been wrestling with. I created a group at some odd angle and position in a model, and then turned it into a component (group.to_component). When I created a new instance using an identity transformation it showed up in the same place (which is what I wanted).
brace = brace.to_component braces = ents.add_group() dummy = Geom::Transformation.new() braces.entities.add_instance(brace.definition,dummy)
In this case the component instance and the component definition seem to be in the same place
brace.bounds.corner(0)
Point3d(-94.3149, -12.7862, -48.5601)
brace.definition.bounds.corner(0)
Point3d(-94.3149, -12.7862, -48.5601)But in another example I have a component created the same way, but the component origin (i.e., the corner of the boundingbox is relocated to [0,0,0]. In this case a new instance is created at the origin, using an identity transformation. How does SU decide, when it creates a component, where to place the origin of the component definition?
-
A hand built component always has its axes at the bottom left of the component's bounding box.
You can of course change it at the time of creation or later on using the context-menu.
A group is similarly axis-ed but there is no adjustment to the axes manually later...
To use code to change e.g. a group's axes you need to work out the transformation needed to move the group back to the model-axes, oriented as desired, and then apply the same transformation.inverse to the group.entities so the contents are moved back to where they appeared in the model before, but of course the group#s axes [aka its insertion-point] might well be in a different place and/or the group itself might be rotated in 3d too... -
@tig said:
A hand built component always has its axes at the bottom left of the component's bounding box.
You can of course change it at the time of creation or later on using the context-menu.
A group is similarly axis-ed but there is no adjustment to the axes manually later...
To use code to change e.g. a group's axes you need to work out the transformation needed to move the group back to the model-axes, oriented as desired, and then apply the same transformation.inverse to the group.entities so the contents are moved back to where they appeared in the model before, but of course the group#s axes [aka its insertion-point] might well be in a different place and/or the group itself might be rotated in 3d too...Thx, Tig -- this is helpful. But what does "hand built" mean? The example I cited was a component built in code. Contrast this example:
` ents = model.entities
Build a parallelogram centred on the origin
a = ents.add_line([-75,-25,0],[25,-25,0])
b = ents.add_line([25,-25,0],[75,25,0])
c = ents.add_line([75,25,0],[-25,25,0])
d = ents.add_line([-25,25,0],[-75,-25,0])
d.find_faces
shape = ents.add_group(d.all_connected)
shape_as_component = shape.to_component
shapedef = shape_as_component.definition
dummy = Geom::Transformation.new()
ents.add_instance(shapedef,dummy)`Here the new instance is located with its corner at the origin, not overlaying the original. Hence my confusion.
-
By 'hand built' I mean 'made manually', i.e. without using any code... that's all...
If you make a group that straddles the origin, then its origin [axes] are located at its bounds.min...
As I tried to explain... you can then move that group from its insertion_point back to the model ORIGIN, and then apply the same translation transformation BUT as an 'inverse', to the group's entities... so that it appears in the same place in the model, BUT its actual axes have been relocated to the ORIGIN...
Advertisement