Using View.draw_polyline in rotated component
-
I'm trying to develop a plugin to help in drawing simple timber frames in standard UK timber sizes. I'm starting by extending the CLineToolplugin written by Jim Folger
I've got as far as being able to draw a polyline on a component 'box' to show where the cross-section of the timber will be placed, provided that the component is orthogonalto the world axes.
I can also draw a line to show the long axis of the wood, using the View.draw_line method between the two points (pt1 + @lAxis) and (pt1 - @lAxis). (@lAxis has only one non-zero coordinate).
I have found the InputPoint.transform method to return the transform of the component at the original pick point, in the onLButtonDown method.
@ip1.pick view, x, y if( @ip1.valid? ) # call the transformation method to get the component/group instance Transform vector# @tf = @ip1.transformation
I can then rotate the long axis line to match by applying the transform to the line defined by the draw_geometry method (which is called from the draw method in my new Class replacing the originals in CLineTool).
But I'm stumped in trying to apply the transform to the rectangular polyline.
This works when the component axes are parallel to the world axes:
I set#Direction of long axis of wood (normal to plane of cross-section) @lAxis = Geom;;Vector3d.new ... view.drawing_color = "magenta" ; view.line_width = 2; view.line_stipple = "" @lAxis[n] = d*2.0 # Set long axis direction normal to @plane, and twice the depth of the wood @lAxis.transform! @tf view.draw_line(pt1 - @lAxis, pt1 + @lAxis) # to show direction of long axis of wood pts = [] pts = [pt1, pt1 + cross_sect[n][0], pt1 + cross_sect[n][1], pt1 + cross_sect[n][2], pt1] view.draw_polyline(pts)
(cross_sect[n][0]..[2] each contain a 3-element array with the coords of the three corner points of the wood which aren't at the pick point. The value of n is the direction of the normal to the cross section - 0 = R direction, 1 = G direction, 2 = B direction.)
The onMouseMove event orients the cross section polyline in the view, before the second pick, according to the relation between the first pick point and the current mouse position, to change the plane of the rectangle in the draw method of the tool, and also the direction of the longer cross section.
But I simply CANNOT work out how to apply the same transform to to the polyline points (or the whole polyline) so as to rotate the rectangle around the first pick point to match the component's orientation. At best, I can get it in the right orientation, but a long way from the pick point - as if it has rotated round the global origin rather than the pick point.
This is the best I've achieved:
pts = [(pt1.transform @tf), ((pt1 + cross_sect[n][0]).transform @tf), ((pt1 + cross_sect[n][1]).transform @tf), ((pt1 + cross_sect[n][2]).transform @tf), (pt1.transform @tf)] view.draw_polyline(pts)
I thought that since the @lAxis.transform seemed to work to generate a vector relative to the first pick point, the polyline points would behave the same. But they don't seem to.
Earlier attempts were plagued by syntax errors, and semantic nonsenses, but I finally got at least the syntax of this right so that it drew SOMETHING - it now changes orientation correctly as the mouse moves, and pivots round the same point, but that point isn't in the right place.
Is there a simpler way to achieve the reorientation and relocation of the drawn points to match the rotated component axes AND get them in the right place?
Perhaps I need somehow to 'subtract' the translation element of the pick point from the origin, but I can't work out how to. And since I'm relocating each of the polyline points individually, in a similar way to the @lAxis points, I thought the transform would correctly relocate the polyline.
@lAxis is a Vector3d (and didn't work until I made it one - before that, I could draw a line from pt1 to (pt1 + @lAxis), but not to (pt1 - @lAxis)).
The elements of cross_sect are just arrays, not Vector3d objects. Does that matter?
Or should I try to do it all with the draw_linecommand rather than draw_polyline?
Any help, or a pointer to code that does the same thing, would be most welcome.
Thanks if anyone can help. In the meantime, I'll re-read Martin Rinehart's later chapters in Edges to Rubies on transformations, to see if I can work out where my logic is going wrong.
-
If you have an array of points in 'model space' that would be right if drawn there, then simply transform them before using them, something like:
pts.each{|pt|pt.transform!(@tf)}
Now the collection of 'pts' is reset to suit their container's transformation ?
No need to mess on with adjusting individual ones.
It IS best to make your points as Point3d objects rather than arrays, as some methods [like offset] prefer them... -
Thanks very much, TIG, for such a quick and helpful response.
It's helped a lot to clean up the code, and it now works for two cases: when @ip1 is clicked at the global origin, or on a component origin.
But is still isn't right if you click on a point or intersection or another component, inside the original component. The long axis line moves to the clicked location and correct orientation in all cases, but the rectangular polyline defined by the point array in cross_sect is still mislocated, though correctly oriented, though it's now closer to the clicked point, instead of a long distance away.
I suspect it's a problem with the way I've defined the points in the cross_sect[][] array, and will try a few more test cases to see if I can get to the bottom of it. I'll also try defining the cross section by a series of lines defined as vectors, instead of an array of points, to see if I can understand better just what's happening.
Can one declare an array of 3d Points as follows?
cross_sect = [Geom::Point3d.new]
I've tried both with this definition (which doesn't error, but may not be meaningful), and just declared as an Array, but get the same not-quite-right results.
I'd post an image here if I could work out how, or perhaps upload a copy of the Ruby if that would be useful, but not just yet until I've worked on it a bit more myself.
Thanks again for your support to a Ruby programming newbie, and for writing so many useful plugins yourself!
John
-
A Point3d behaves much as an Array and vice versa.
You can make a point and adjust it's x/y/z values on the fly just as you can with an Array.
So
pt=Geom::Point3d.new()
or even
pt=ORIGIN.clone
gives you
Geom::Point3d.new(0, 0, 0)
then
pt.x=123.4
OR
pt[0]=123.4
gives you
Geom::Point3d.new(123.4, 0, 0)
A point or array will take .x ,y and .z; as well as [0], [1] and [2]...
This lets you read AND set values...
pz=pt.z
returns
0.0
and
pt.z=1
gives us
Geom::Point3d.new(123.4, 0, 1.0)
etc...I suspect you are over complicating things...
To get the z axis of the current 'container', test if it's the model, and if so useZ_AXIS
; if not use thecontainer.transformation.zaxis
Let's assume we have an object in a container that is called 'obj'
if obj.parent==Sketchup.active_model z_axis=Z_AXIS else#it's a group or component z_axis=obj.parent.instances[0].transformation.zaxis end
IF you already know where the 'obj' is - e.g. you've made it inside a 'group' then it's even easier...
z_axis=group.transformation.zaxis
Advertisement