Intersecting and Attribute inheritance
-
Hi there!
Please forgive my bad english, I'll try hard to explain my issue. I would like to achieve a very specific goal in SketchUp-Ruby. Assume you have 2 different groups with different 2D-content.
The first group contains for example plots. Each plot contains information (in attributes) like owner,address, area etc.
The second group contains zones, for example noisezones, with attributes like development potential.My goal is to intersect the 2 groups and put the result in a 3rd group. The 3rd group should inherit the attributes from both groups. This way, you'll have the information like the development potential of Mr. Smith's plot.
I tried the intersect_with method (worked great with solids, couldn't get it working with flat content) and exploding the groups into a 3rd group. But in no way i could get the attributes from both groups into the 3rd. So I'm pretty stuck right now.For this issue I would like some guidance to push me in the right direction. I would be delighted to recieve short examples if possible. Please, can anyone help me?
-
You might have difficulties getting 2d flat shapes to intersect.
If you PushPull the forms into 3d this might help [maybe even in just one of the groups...].So let's assume the two things that will be intersected are called '
group1
' and 'group2
' - and that they are both in the same entities context - which is also the active context...
Make a neutral transformation:
tr=Geom::Transformation.new()
To get the intersected result into a separate group use:
group=model.active_entities.add_group()
This will make an empty group into which you'll add the new geometry in the next step.
model.active_entities.intersect_with(true, tr, **group.entities**, tr, true, [group1, group2])
The intersection results should now be within 'group
'.
-
Thanx for your reply TIG!
I saw you already wrote a lot about intersect_with on different forums. Makes you a kind of expert huh
I already achieved what you replied. The result is that I have the intersection lines in a different group (group3). But actually I need faces...
Probably its possible to add faces to the lines, but how do these faces inherit the attributes from the faces in group1 and group2?Should I loop through all the faces in group3 (the new group) and check which attributes they should inherit from group1 and 2 (somehow, I don't know...yet)? Or is there a more obvious, easier and less-time-consuming way to achieve this?
Any help or push in the right direction is appreciated!
-
My method will only ever produce edges...
Before you intersect you could try adding a 'clone' of the group1 etc*** to the new group and exploding it.
tr=Geom::Transformation.new()
To get the intersected result into a separate group use:
group=model.active_entities.add_group()
in1=group.entities.add_instance(group1.entities.parent, group1.transformation)
in1.explode
***Not sure that you want to do the group2 stuff too with a test...
Get the current group entities...
entsa=group.entities.to_a
Now add the intersection geometry in the next step.
ents_new=model.active_entities.intersect_with(true, tr, group.entities, tr, true, [group1, group2, group])
***Not sure if group1 is needed in here either
You now have two arrays of objectsentsa
=entities from before the intersection andents_new
objects formed by it. Because that second array also contains non-geometry [loops etc] you'll need to extract just the geometry with something likee.is_a?(Sketchup::Entity)
when you iterate that array...Assuming you only want the 'new' stuff you can make two arrays of entities and get the difference and erase that ?
Needs testing...
-
Wow... I need to get in to this! Looks like it could be the push and direction I asked for. Can't see yet in which way the combination of attributes are passed to the new group, but I will work this out first.
I'll get back at this after the weekend...TIG, thnx again!
-
You talk of 'attributes'.
These have a specific meaning in the API.
If you just mean geometry etc then that's OK, but if the two groups in question have 'attributes' attached to them then you need to clone any reapply them to any new container group...
For example: ads1=[] group1.attribute_dictionaries.each{|d| ads1 << d}if group1.attribute_dictionaries
Now you can check the dictionaries by name and if they are to be transferred do something like...testname_attributes=[] ads1.each{|d|next unless d.name=='testname' d.each_pair{|k.v| testname_attributes << [k, v] } }
Then to reapply that dictionary to 'group' use:
testname_attributes.each{|a| group.set_attribute('testname', a[0], a[1]) }
If that's not hat you meant ignore me -
Wow...and thanks...again! I was still chewing on your previous post.
TIG, I think you touched the essence of the problem: yes, I'm talking about attached attributes to the entities in the groups, in particular faces. An attribute could be the owner of a plot(attribute of an entity Face in group1) or whether in a zone it's allowed to develop housing or not(attribute of an entity Face in group2).The result could be that Mr. Smith can only develop housing on a part of his plot, because the intersection of the 2 groups will tell us that. (a face in the third group now have the attributes from plot and zone!)
I'm still trying to understand and testing your previous post... Today I will try to get it to work. Then I will post my code I made so far. Hope it will make it easier to talk about.
-
Attributes usually persist in new copies/clones/duplicates.
It's a bad idea to attach attributes to things like faces and edges because these do get overwritten/replaced when geometry explodes or is intersected, or even edits are 'undone'.
You are better to attach it to enduring things like component definitions, or groups [e.g. an undo will replace a group with a new instance BUT it will retain the originals properties - like name, layer, material, hidden/shadow-cast/receive behaviors and any attributes].Combining two groups into a new one and exploding them might retain attributes from both sets of faces, but is uncertain depending on the overlap/merging of those faces.
Perhaps we'd understand better if you posted a simple example SKP containing two such groups, explaining their special attributes and how they might be recombined in a resultant 'merged' group... If the two groups each had sets of attributes from the same Dictionary but using different Keys, it'd be straight forward to re-add all of those to one merged result.
If you used different colored materials [named after the zone/plot-owner etc] wouldn't that give similar visual results ?? -
TIG, thanks for the warning...but I really need to attach information to the level of faces...I think...unless there is a better way to achieve what I need of course! This picture shows 2 groups with each his own information (attributes) attached to plots/zones (faces) and a third group which shows the intersect result I would like to achieve.
Data I would like to attach to every plot (read: face) is something like owner, plotnumber, address, prize, date of purchase, grounduse etc.
In that case, showing information by material wouldn't be enough...although it would be nice to make kind of themes like 'most-valuable-plots'(but that will be step 21 or so).
I hope this picture will help you to understand the goal I'm trying to achieve.
BTW, I'm still working on a 'postable' piece of code...
-
It's easy to sort out visually but in code somewhat more convoluted... but I think I have an idea...
Group the 'plots' faces [group1].
Attach your 'Plot_Info' Attribute Dictionary to those faces with various keys and values as desired.
Group the 'zones' faces [group2].
Attach your 'Zone_Info' Attribute Dictionary to those faces with various keys and values as desired.
Now [in code] combine the two into a new group.
Do this by making a new empty group [
` group3=group1.parent.entities.add_group()
gp1=group3.entities.add_instance(group1.entities.parent, group1.transformation)
gp2=group3.entities.add_instance(group2.entities.parent, group2.transformation)
gp1.explode
gp2.explode`
At this point 'recombine' the faces' attributes to suit...
To do this we need to pick a point just inside each face in group3, test for its equivalent location in group1 and get that group's face and its 'Plot_Info' attributes; and the same for group2 and get that group's face and its 'Zone_Info' attributes. Combine these into a new 'Combo_Info' Attribute Dictionary set of keys/values and attach it to that face in group3.
Here's some cod-code...
` faces1=[];group1.entities.each{|e|faces1<<e if e.is_a?(Sketchup::Face)}
faces2=[];group2.entities.each{|e|faces2<<e if e.is_a?(Sketchup::Face)}
faces3=[];group3.entities.each{|e|faces3<<e if e.is_a?(Sketchup::Face)}`
then
` faces3.each{|face|
vs=face.outer_loop.vertices
vt=vs[0]
pt=vt.position
pt.offset!(pt.vector_to(vs[1].position), 1)
while not face.classify_point(pt)==Sketchup::Face::PointInside
tr=Geom::Transformation.rotation(vt.position, Z_AXIS, 1.degrees)
pt.transform!(tr)
end#while_notpt should now be on the 'face'
attribs1=[]
faces1.each{|f|
if f.classify_point(pt)==Sketchup::Face::PointInside
f.attribute_dictionary("Plot_Info").each_pair{|k,v|attribs1 << [k,v]}if f.attribute_dictionary("Plot_Info")
break
end
attribs2=[]
faces2.each{|f|
if f.classify_point(pt)==Sketchup::Face::PointInside
f.attribute_dictionary("Zone_Info").each_pair{|k,v|attribs1 << [k,v]}if f.attribute_dictionary("Zone_Info")
break
end
}now you have two arrays of attributes add them to 'face' as you wish, say thus:
(attribs1+attribs2).each{|a|face.set_attribute("Combo_Info", a[0], a[1])}
or in other ways ???
}`
This lets you combine the plots and zones geometry, and then assign attribute info to each new face that is a combo of the plot and zone groups' attributes... -
TIG! Wonderfull! That's it!!!
I just tried it in a small example and it worked! It was exactly what I was thinking about but couldnt achieve...
Now I have to find out how the magic works. Especially the way you loop through faces3 and collect the attributes from faces1 and faces2. There is some new stuff in the code for me (classify_point(pt)?PointInside? Transformation.rotation(vt.position, Z_AXIS, 1.degrees)?).
Thanks for the great help!
-
Put very simply we need to see what face is under what point.
We find all of the 'combined faces' in group3 [after the explosion of copies of group1 and group2 we added into it] - an array which I named faces3.
Each face in faces3 has vertices, we consider the first one in each case = vertices[0], we find it's .position, we offset that position a little and rotate it around the Z_AXIS and the vertex.position by steps of 1.degree until we know it's on the 'face'.
That's the classify_point method.
So now we have a point we know is on the face.
We test all of the faces in faces1 until we get on that 'contains' the point - we remember its 'Plot' attributes.
We test all of the faces in faces2 until we get on that 'contains' the point - we remember its 'Zone' attributes.
We add new attributes [Combo] to 'face' combining these two sets of attributes...
You could probably do with testing group1 and group2 faces for both attribute dictionaries, since you can't be sure which group is which in the selection - unless they have 'names' that tell us?
Advertisement