So, Rhino defines a block always in the World-Cplane but then inserts it in the current CPlane.
Am I the only one who finds this counter-intuitive? Or just the only one who wants to define a block somewhere in space and than insert it flat on the XY-Plane?
Yep, that is indeed how it works. This way you can insert say your screw in the correct orientation. But always assume World when the block is defined.
Your screw-example would still work fine when the CPlane was regarded during definition, wouldn’t it? You would just need to make sure that CPlane=World to get the current behavior.
But what doesn’t work now is to “pick up” the screw in its original oriention somewhere in space and still create a screw-block where the screw is aligned with, say, the x-axis. I cannot just set the CPlane’s x-axis to match the screw and define the block. First I have to Orient3D the screw to the world x-axis, then define the block there and then insert it in the original location.
Haven’t worked with ACAD in a while, but I think I remember it works there - which actually provides more flexibility without any real drawbacks?
Well, imagine a screw being as a file, though - that has to work consistently - so a plane other than the World is not part of the block… but if your goal is to make a World oriented block from the current CPlane, this may help, for now:
Thanks for the Python example! That saves me some time
I understand that there is no plane other than WorldXY stored in a block, but this isn’t actually necessary!
Since you mention it: I just found out that _ExportWithOrigin actually works exactly the way I described above. It takes the active CPlane as the new WorldXY-Plane for the exported file. When I then re-insert the file as block-instance in the same CPlane, I get exactly what I expect: it’s oriented the same as before.
Now the question: why can’t _Block do just the same? “Implicitly” re-orient the objects from CPlane to WorldXY, define the block as it does now and re-insert it in the CPlane. When this is done from WorldXY, the result is precisely the same as it is now. From any other CPlane it adds functionality and comfort.
And consistency: Now, when I create a block in a view with a CPlane, I get a block instance exactly where my objects have been. When I then - without changing the CPlane! - try to insert this same block a second time, the orientation is different!! WTF?! I have to find our discussion here to understand it…
Wow this scripts is really close to what I need! Hope you don’t mind if I build upon your code to make it so one can specific an origin point rather than always uinsg Wxy.Origin.
One question: I’m having a hard time understand this line: bId = sc.doc.Objects.AddInstanceObject(idx, xform2.TryGetInverse()[1] )
why is there a [1] at the end, why is it important here to extract part of the transformation matrix?
In the end I made it work like this, but not really sure why it worked:
def DefineWorldBasedBlock():
ids = rs.GetObjects("Select objects to block.", preselect=True)
if not ids: return
bPlane = rs.ViewCPlane()
if bPlane is None: return
Wxy = Rhino.Geometry.Plane.WorldXY
Wxy_n = Rhino.Geometry.Plane.WorldXY
xform1 = Rhino.Geometry.Transform.PlaneToPlane(bPlane, Wxy)
point = rs.GetPoint("Select Block Origin Point")
if( point == None):
point = Wxy.Origin
else:
bPlane.Origin = point
Wxy_n.Origin = point
xform2 = Rhino.Geometry.Transform.PlaneToPlane(bPlane, Wxy_n)
xform3 = Rhino.Geometry.Transform.PlaneToPlane(bPlane, Wxy)
name= rs.StringBox("Block name", title="World Block")
if not name: return
objs = [sc.doc.Objects.Find(id) for id in ids]
geos = [obj.Geometry for obj in objs]
attrs = [obj.Attributes for obj in objs]
for geo in geos:
geo.Transform(xform2)
sc.doc.InstanceDefinitions.Add(name, "", point, geos, attrs)
print "Block " + name + " defined."
idx = sc.doc.InstanceDefinitions.Find(name).Index
bId = sc.doc.Objects.AddInstanceObject(idx, xform3.TryGetInverse()[1] )
rs.DeleteObjects(ids)
I’m also trying to solve for the case where the name of the block already existed and enable the script to overwrite the old block definition.
In the Rhino common I did not find out a method to overwrite an existing instance definition, delete definition does not work because it will delete all reference too. ModifyGeometry is very close, but after testing I did not find out how this method actually determines the orientation of the geometry being modified in relation to the origin point of the old definition, which makes it kind of useless.
The method Transform.TryGetInverse() returns a tuple (boolean, xform) with a success flag and the resulting matrix (if any). Actually you should check the boolean first before using the result, or you might run into an exception if the method failed. But here the inversion should be rather straightforward, therefore the second item in the returned tuple is accessed directly by its index.
Current behaviour (block plane definition) is not very universal since its locked to world. I have come to its bottleneck today:
i modelled geometry in some place and made a block
…created many instances in different orientations…
i modified the block (explode and define new block)
i replaced selected instances
their orientations were totally off (of those replaced ones)
i guess i modified and defined the block not in the same plane when i made a modified version.
Subsequent replacement was therefore very off.
I think only solution for this is to define plane instead of an insertion plane when defining a block.