Complex scripting/plugin - to create a component library
-
The
img.explode
ought to auto-intersect the image's face and the rest of the edges [I assume that the component 'outline' consists of just edges ??]
However, as you suggest... the following code inserted immediately after the 'img.explode' line of code, should ensure proper intersection, if it's possible...
t=Geom::Transformation.new() d.entities.intersect_with(false, t, d.entities, t, true, d.entities.to_a)
Things that might stop this being always effective are:
that the outline edges are not quite coplanar and therefore don't split the newly added image face:
the outline contains edges that are tiny in length, and thereby the Sketchup intersect method fails as both ends are deemed to be coincident and therefore the edge is not recognized and therefore no full loop is made as needed to hold a face...
If that's the case we can try to 'heal' tiny edges... but let's first see how the current fix-up ideas work... -
Yeah the components are just faces after being importer as dwg.
I tried just to add the textures them intersect them individually and that worked hence the suggestion of an intersect.
So that should work!But I did this and got an error:
LukeR.importer 0001 vyonyx_couple_001.dwg Error; #<NoMethodError; undefined method `transformation' for Geom;Module> C;/Program Files/Google/Google SketchUp 8/Plugins/component_import.rb;32 C;/Program Files/Google/Google SketchUp 8/Plugins/component_import.rb;20;in `each' C;/Program Files/Google/Google SketchUp 8/Plugins/component_import.rb;20;in `importer' (eval);0
I obviously put them in wrong! Sorry
UPDATE: My guess is it needs a capital T on transformation
testing now### Usage; Ruby Console type LukeR.importer require('sketchup.rb') module LukeR def self.importer() model=Sketchup.active_model defs=model.definitions ### PURGE UNUSED COMPONENTS [NOT NEEDED IF YOU DO IT MANUALLY FIRST] #defs.purge_unused ### SETUP LIST OF NAMES etc cnames=[] defs.each{|d|cnames << d.name if File.extname(d.name).downcase == '.dwg'} cnames.sort! fj='P;\02 General\Resource Library\People\JPEG' ### for images ff='P;\02 General\Resource Library\People\Sketchup People' ### for saved SKPs cnames.each{|name| puts name ### SET UP defn etc d=defs[name] bb=Geom;;BoundingBox.new d.entities.each{|e|bb.add(e.bounds)} ### ADD IMAGE TEXTURES img=d.entities.add_image(File.join(fj, File.basename(name, ".*")+".jpg"), [-1.mm,-1.mm,0], bb.width+2.mm, bb.height+2.mm) img.explode t=Geom;;transformation.new() d.entities.intersect_with(false, t, d.entities, t, true, d.entities.to_a) ### REMOVE img's UNWANTED PERIMETER togos=[] d.entities.each{|e|togos << e if e.valid? and e.is_a?(Sketchup;;Edge) and e.faces.length<2} d.entities.erase_entities(togos) ### ENSURE FACES ARE 'UP' d.entities.each{|e|e.reverse! if e.is_a?(Sketchup;;Face) && e.normal!=Z_AXIS} ### STAND COMPONENT'S CONTENTS TO BE VERTICAL tr=Geom;;Transformation.rotation(ORIGIN, X_AXIS, 90.degrees) d.entities.transform_entities(tr, d.entities.to_a) ### MAKE IT A FACE-ME COMPONENT d.behavior.always_face_camera=true ### REMOVE OUTER EDGES LEFT FROM EXPLODED IMAGE togos=[] d.entities.each{|e|togos << e if e.valid? and e.is_a?(Sketchup;;Edge) and e.faces.length<2} d.entities.erase_entities(togos) ### REMOVE ANY 'HOLES' faces=[] d.entities.each{|e|faces << e if e.is_a?(Sketchup;;Face)} (faces.length-1).times{ for face in faces if face.valid? for edgeuse in face.outer_loop.edgeuses if not edgeuse.partners[0] ### outermost face faces = faces - [face] loops = face.loops for loop in loops for fac in faces if fac.valid? and (fac.outer_loop.edges - loop.edges) == [] faces = faces - [fac] fac.erase! if fac.valid? ### fac abutts kept face so it must be erased... end #if fac end #for fac end #for loop end #if outermost end #for edgeuse end #if valid end #for face }#times ### FINALLY... SAVE IT INTO 'PEOPLE' AS A' SKP' d.save_as(File.join(ff, File.basename(name, ".*")+".skp")) }#names end#def end#module
It didnt do that before I added the intersect command
-
You are right it needs to be
t=Geom::**T**ransformation.new()
-
WOW!
))))))))))
Ok So a few things, very nearly perfect now!
I have attached an image showing a new model which i dropped approx 50 people into (in less than a minute )))))))Issues:
1 - I believe some of the jpegs need cutting out better as some have tiny artifacts where they should be transparent. This is causing the bounding box of the outline and the jpeg of some images to be off. I will look into a quick way of doing this i photoshop. (They have all been trimmed but not sure how to batch trim/crop/remove the artifacts and images when all the image sizes are different etc... Might go to the photoshop forums for this.)2 - I removed some of the script to try and correct it as it was not working before. It seems to delete everything so all the skp files are empty. I thought this was the removing holes script but I beleive it to be either: "### REMOVE img's UNWANTED PERIMETER", "### REMOVE OUTER EDGES LEFT FROM EXPLODED IMAGE" or "### REMOVE ANY 'HOLES'"
Further testing is needed to determine the culprite of the mass deleting issue.
HOWEVER (dont ask me why, I will look into it though)
All the components have successfully saved as perfect components with the exception of off centered jpeg textures - issue one as above. Something deleted the img borders but not sure what!-
Would be useful to get better thumbnails of the components. As they are currently shown in the component library window as the original dwg outlines - needs updating? (see attached image)
-
The odd few are out of scale for example groups of people and people sitting down. This is not an issue as they are just odd ones and will be scaled down manually after
### Usage; Ruby Console type LukeR.importer require('sketchup.rb') module LukeR def self.importer() model=Sketchup.active_model defs=model.definitions ### PURGE UNUSED COMPONENTS [NOT NEEDED IF YOU DO IT MANUALLY FIRST] #defs.purge_unused ### SETUP LIST OF NAMES etc cnames=[] defs.each{|d|cnames << d.name if File.extname(d.name).downcase == '.dwg'} cnames.sort! fj='P;\02 General\Resource Library\People\JPEG' ### for images ff='P;\02 General\Resource Library\Sketchup People' ### for saved SKPs cnames.each{|name| puts name ### SET UP defn etc d=defs[name] bb=Geom;;BoundingBox.new d.entities.each{|e|bb.add(e.bounds)} ### ADD IMAGE TEXTURES img=d.entities.add_image(File.join(fj, File.basename(name, ".*")+".jpg"), [-1.mm,-1.mm,0], bb.width+2.mm, bb.height+2.mm) img.explode t=Geom;;Transformation.new() d.entities.intersect_with(false, t, d.entities, t, true, d.entities.to_a) ### ENSURE FACES ARE 'UP' d.entities.each{|e|e.reverse! if e.is_a?(Sketchup;;Face) && e.normal!=Z_AXIS} ### STAND COMPONENT'S CONTENTS TO BE VERTICAL tr=Geom;;Transformation.rotation(ORIGIN, X_AXIS, 90.degrees) d.entities.transform_entities(tr, d.entities.to_a) ### MAKE IT A FACE-ME COMPONENT d.behavior.always_face_camera=true ### FINALLY... SAVE IT INTO 'PEOPLE' AS A' SKP' d.save_as(File.join(ff, File.basename(name, ".*")+".skp")) }#names end#def end#module
This code worked:
-
-
I noticed you aren't 'hiding edges' anywhere...
In this code
d.entities.each{|e|e.reverse! if e.is_a?(Sketchup::Face) && e.normal!=Z_AXIS}
adjust it to read like this
d.entities.each{|e|e.reverse! if e.is_a?(Sketchup::Face) && e.normal!=Z_AXIS**; e.hidden=true if e.is_a?(Sketchup::Edge)**}
This saves iterating everything another time, to optimize speed...Using a PNG image with a transparent background avoids the 'white-fringe' issues of poorly trimmed JPG images. It also allows potential holes to remain 'see-through', although the cutout's shadows won't then match, and PNG textured materials on faces never 'receive shadows' either...
You should also add...
**d.refresh_thumbnail**
to get an updated thumbnail image ?
Insert it just before the line that saves the SKP externally...
d.save_as(...)
Hope this helps...
-
I'm not sure I want to hide edges. Will have a think about that one!
Mainly because I can always turn edges off on the style for the whole model.
I will ask a collegue what they would prefer to do but nice to have the code there if he wants them hidden so thanks again! - I quite like the option of having them as silhuettes as well for more concept images (change style to hidden line)I will add the refresh thumbnail, cheers!
I'm trying to think if I need them to receive shadows or not! - i dont think it matters, in which case I will probably change to png images. However it will solve half the issue; fill the white with transparency. But it wont get rid of the small artifacts so there will still be lots of images with off axis images. - a photoshop issue. But yeah I think your right, use pngs!
-
As an alternative to hiding edges, Is it easy to move the edges to another layer to allow easy on/off of them? Just a thought.
-
hidn=model.layers.add("HIDN-LINE")### or whatever name you like! d.entities.each{|e|e.reverse! if e.is_a?(Sketchup::Face) && e.normal!=Z_AXIS; e.layer=hidn if e.is_a?(Sketchup::Edge)}
-
So just to confirm is this right:
Will this keep the lines visible? I would like it to save the component with the lines as visible then hide the layer is needed. The name should probably be "People Outlines"Also while were at it is might be good to have the components on they're own layer "People"
is this right?
I haven't added anything to move the component default layer though.hidn=model.layers.add("People Outlines")### or whatever name you like! d.entities.each{|e|e.reverse! if e.is_a?(Sketchup;;Face) && e.normal!=Z_AXIS; e.layer=hidn if e.is_a?(Sketchup;;Edge)}
-
The layer 'hidn' is visible by default.
You can name a layer whatever you like!
I don't recommend that you put the 'faces' on another layer.
Let the user decide how to layer their own model. With the face on Layer0 inside the component it assumes the instance's current layer anyway - that way you can then have two separate sets of 'people' assigned to different layers and switch between them...
BUT if you really insist it's simple enough...hidn=model.layers.add("People Outlines")### or whatever name you like! peep=model.layers.add("People Images")### or whatever name you like! d.entities.each{|e|e.reverse! if e.is_a?(Sketchup;;Face) && e.normal!=Z_AXIS; e.layer=hidn if e.is_a?(Sketchup;;Edge); e.layer=peep if e.is_a?(Sketchup;;Face);}
-
ABSOLUTELY BRILLIANT!
This is SO good now!
LONG STORY SHORT I THINK YOU'VE DONE IT!comments (some for me )/
1 - deleted a duplicate (it was in the scrit twice!) of the img perimeter deleting - this caused everything to be deleted (a page back) ("### REMOVE img's UNWANTED PERIMETER", "### REMOVE OUTER EDGES LEFT FROM EXPLODED IMAGE")2 - I will need to sort out the images with small artifacts causing off axis
3 - I am yet to try the hole deleting part but its not causing any issues on people - doing the trees tomorrow so will try then!
4 - The layers work perfectly - will have a think whether to leave as default layer or change to People. Thinking of leaving as default as you said.
5 - Changed to png which works much cleaner
6 - Thumbnail refresh works well
-
Ok TIG,
I think it is deleting all the edges except the external outline. I.e. it is deleting all of the internal edges. I have tried removing the "remove holes" ruby script and I dont think this is what is deleting it. Is some of the script earlier on deleting all of the internal edges before the holes script gets to work?
-
If I don't have your current code then how can I comment on what it's doing ?
Can you PM me with the current code, and two or three dwg/image pairs [all zipped], so I test this properly my self.
It's almost impossible to debug at arms length like this... -
I've managed to mock some files up and test it...
I think this works now### usage; LukeR.importer module LukeR def self.importer() model=Sketchup.active_model defs=model.definitions #model.start_operation("importer",true) ### Bugsplats with image.explode !!! ### PURGE UNUSED COMPONENTS [NOT NEEDED IF YOU DO IT MANUALLY FIRST] defs.purge_unused ### SETUP LIST OF NAMES etc cnames=[] defs.each{|d|cnames << d.name if File.extname(d.name).downcase == '.dwg'} cnames.sort! ### ADJUST TO YOUR OWN PATHS fj='P;\02 General\Resource Library\People\JPEG' ### for images ff='P;\02 General\Resource Library\People' ### for saved SKPs #fj='C;\Users\TIG\Desktop\SKPs\PNGs' #ff='C;\Users\TIG\Desktop\SKPs' ### PROCESS NAMES... cnames.each{|name| puts name ### SET UP defn etc ### COUNT EDGES d=defs[name] count=0 bb=Geom;;BoundingBox.new d.entities.each{|e| bb.add(e.bounds) count+=1 if e.is_a?(Sketchup;;Edge) } ### ADD IMAGE TEXTURES NB; '.png' img=d.entities.add_image(File.join(fj, File.basename(name, ".*")+".png"), [-1.mm, -1.mm, 0], 2.mm+bb.width, 2.mm+bb.height) next unless img img.explode t=Geom;;Transformation.new() count.times{ d.entities.intersect_with(true, t, d.entities, t, true, d.entities.to_a) } ### repeat to ensure all splits... ### REMOVE img's UNWANTED PERIMETER togos=[] d.entities.each{|e|togos << e if e.valid? and e.is_a?(Sketchup;;Edge) and e.faces.length<2} d.entities.erase_entities(togos) ### ENSURE FACES ARE 'UP' & SET LAYERING hidn=model.layers.add("People Outlines")### or whatever name you like! peep=model.layers.add("People Images")### or whatever name you like! d.entities.each{|e|e.reverse! if e.is_a?(Sketchup;;Face) && e.normal!=Z_AXIS; e.layer=hidn if e.is_a?(Sketchup;;Edge); e.layer=peep if e.is_a?(Sketchup;;Face);} ### STAND COMPONENT'S CONTENTS TO BE VERTICAL tr=Geom;;Transformation.rotation(ORIGIN, X_AXIS, 90.degrees) d.entities.transform_entities(tr, d.entities.to_a) #=begin ### REMOVE ANY 'HOLES' faces=[] d.entities.each{|e|faces << e if e.is_a?(Sketchup;;Face)} (faces.length-1).times{ for face in faces if face.valid? for edgeuse in face.outer_loop.edgeuses if not edgeuse.partners[0] ### outermost face faces = faces - [face] loops = face.loops for loop in loops for fac in faces if fac.valid? and (fac.outer_loop.edges - loop.edges) == [] faces = faces - [fac] fac.erase! if fac.valid? ### fac abutts kept face so it must be erased... end #if fac end #for fac end #for loop end #if outermost end #for edgeuse end #if valid end #for face }#times #=end ### ALL FACES GET MAIN MATERIAL faces=[] d.entities.each{|e|faces << e if e.is_a?(Sketchup;;Face)} mat=nil faces.each{|e|mat=e.material if e.material} faces.each{|e| e.material=mat e.back_material=mat } ### MOVE ORIGIN TO BOTTOM-CENTER OF OUTLINE tr=Geom;;Vector3d.new(-bb.center.x, 0, 0) d.entities.transform_entities(tr, d.entities.to_a) ### MAKE IT A FACE-ME COMPONENT #d.behavior.always_face_camera=true ### FINALLY... SAVE IT INTO 'PEOPLE' AS A' SKP' d.refresh_thumbnail d.save_as(File.join(ff, File.basename(name, ".*")+".skp")) }#names #model.commit_operation puts puts 'Done.' puts return true end#def end#module
NOTE: the image files' PNG and folder names you might want to adjust...
-
Brilliant thanks TIG, sorry I didnt get back to you - not much free time yesterday.
Will test today at some point.I have also thought of a better solution for matching the images up with the geometry. originally the image and the dwg are the same - the dwg is just live traced from the image. So if I keep the image border the bounding box of the dwg when imported will perfectly match the texture image in sketchup. I originally removed this frame in illustrator but realised this is removed in su anyway and therefore it is making it worse not better silly me.
Just wondering, is there anyway to scale the geometry with constrained proportions to a set height? (ok if not) but at the moment illustrator is sort of a half manual, half automatic process for scaling which takes time.
-
scrap the scaling !
Managed to add the scaling in the images (canvas size in photoshop) before it goes to a dwg - I think it will keep its scaling -
OK TIG!
This looks like it could be the last edit! (sorry there was a lot of work!! - I really appreciate it and will make sure I share the work on the warehouse after getting permission from vyonyx)
The images are all mapping perfectly now!
So the issue is that now I have added a frame from the original jpeg for the bounding box to reference its size correctly to (as notede 2 post ago). I think the could requires editing to delete this external frame as well. At the moment I believe that the following is happening:
- The image is made slightly bigger (good as it just makes them a little sharper at least).
- The image is exploded and frame removed but as it is 1mm bigger the other frame now in the components is not deleted.
Summary - I think we need to add another delete for the outer frame as well as the img exploded frame.
Good news is the remove holes seem to be working well! (just inverted at the moment as the second outer frame remains)
-
If you duplicate this code
### REMOVE img's UNWANTED PERIMETER togos=[] d.entities.each{|e|togos << e if e.valid? and e.is_a?(Sketchup;;Edge) and e.faces.length<2} d.entities.erase_entities(togos)
immediately after itself... then first time it runs the outer perimeter edges of the original image are deleted - it was actually added 1mm bigger all round than the original outline's size to ensure its edges were separated from the outline's. The second go it erases the 'frame' of the outline so you have the 'real' outline left - frankly this 'frame' seems superfluous to me
The potential problem is that if this 'frame' shares any horizontal/vertical short edges [that you will actually want to be kept because they are outlining the desired form] then it simply screws up, because they'll go too and no face loop is left for later processing, AND we have no way of knowing which of these single-faced edges is which... so don't even ask how we can do it...
If you don't have this 'frame' the result is fine without the second step.
Alternatively add the 'frame' as being a discontinuous loop - perhaps just the max-x/y dims on two sides only, which then gets erases in the first run to the perimeter deletion code anyway, because these edges can never have any faces so ..faces.length<2 applies
BUT if you must have this continuous looped 'frame', then its edges can only touch the main outline's edges at points, any 'coincident' edges - no matter how short - will get removed in the second run, and then the facing of the discontinuous outline's edge will fail dismally... -
sorry just posted at the same time you did so didnt see the above
- The extra frame is to make the bounding box of the component match the texture. As you saw before, because these textures (pngs) are not cut out properly, the bounding box is different to the bounding box of the geometry component. so causing a mis match in the mapping of the texture to the geometry. This solves this.
However yes you are right in the fact that this extra frame would mess things up IF the geometry we want to keep is the same. however this only happens when the image is cut off at the edge of the png file. And I don't care for these images as they wont be used. i.e. is you have a person that is cropped off and not whole, I wouldnt put this in a model. Some of the trees do this, but they wouldnt be used anyway and would be deleted.
A SOLUTION - if we needed one, i.e. if some of the geometry is the same as the frame.
I could extent the canvas size of the original image via photoshop at the start. Hence extending the frame beyond the boundaries of the image we want to keep.EDIT: Also just noticed the face me code wasnt put in again. Copied this back in
NOW TO PROCESS THESE! (overnight) -
EDIT!
SORTED TIG, I just copied it againheres the final Code.
One LAST thing - How easy is it to apply this to a folder of seperate Skp files rather than active components? Onyl because it takes about 15 minutes to process a single component and when they are all (or some of them about 50 or so) in one model it slows down considerably and I would leave it on overnight to do a batch. Probably not necesary but just be interested in your thoughts to improve the processing speed?
FINAL CODE:
### usage; LukeR.importer module LukeR def self.importer() model=Sketchup.active_model defs=model.definitions #model.start_operation("importer",true) ### Bugsplats with image.explode !!! ### PURGE UNUSED COMPONENTS [NOT NEEDED IF YOU DO IT MANUALLY FIRST] defs.purge_unused ### SETUP LIST OF NAMES etc cnames=[] defs.each{|d|cnames << d.name if File.extname(d.name).downcase == '.dwg'} cnames.sort! ### ADJUST TO YOUR OWN PATHS fj='P;\02 General\Resource Library\People\PNG' ### for images ff='P;\02 General\Resource Library\Sketchup People' ### for saved SKPs #fj='C;\Users\TIG\Desktop\SKPs\PNGs' #ff='C;\Users\TIG\Desktop\SKPs' ### PROCESS NAMES... cnames.each{|name| puts name ### SET UP defn etc ### COUNT EDGES d=defs[name] count=0 bb=Geom;;BoundingBox.new d.entities.each{|e| bb.add(e.bounds) count+=1 if e.is_a?(Sketchup;;Edge) } ### ADD IMAGE TEXTURES NB; '.png' img=d.entities.add_image(File.join(fj, File.basename(name, ".*")+".png"), [-1.mm, -1.mm, 0], 2.mm+bb.width, 2.mm+bb.height) next unless img img.explode t=Geom;;Transformation.new() count.times{ d.entities.intersect_with(true, t, d.entities, t, true, d.entities.to_a) } ### repeat to ensure all splits... ### REMOVE img's UNWANTED PERIMETER togos=[] d.entities.each{|e|togos << e if e.valid? and e.is_a?(Sketchup;;Edge) and e.faces.length<2} d.entities.erase_entities(togos) ### REMOVE outline's UNWANTED PERIMETER togos=[] d.entities.each{|e|togos << e if e.valid? and e.is_a?(Sketchup;;Edge) and e.faces.length<2} d.entities.erase_entities(togos) ### ENSURE FACES ARE 'UP' & SET LAYERING hidn=model.layers.add("People Outlines")### or whatever name you like! peep=model.layers.add("People")### or whatever name you like! d.entities.each{|e|e.reverse! if e.is_a?(Sketchup;;Face) && e.normal!=Z_AXIS; e.layer=hidn if e.is_a?(Sketchup;;Edge); e.layer=peep if e.is_a?(Sketchup;;Face);} ### STAND COMPONENT'S CONTENTS TO BE VERTICAL tr=Geom;;Transformation.rotation(ORIGIN, X_AXIS, 90.degrees) d.entities.transform_entities(tr, d.entities.to_a) ### MAKE IT A FACE-ME COMPONENT d.behavior.always_face_camera=true #=begin ### REMOVE ANY 'HOLES' faces=[] d.entities.each{|e|faces << e if e.is_a?(Sketchup;;Face)} (faces.length-1).times{ for face in faces if face.valid? for edgeuse in face.outer_loop.edgeuses if not edgeuse.partners[0] ### outermost face faces = faces - [face] loops = face.loops for loop in loops for fac in faces if fac.valid? and (fac.outer_loop.edges - loop.edges) == [] faces = faces - [fac] fac.erase! if fac.valid? ### fac abutts kept face so it must be erased... end #if fac end #for fac end #for loop end #if outermost end #for edgeuse end #if valid end #for face }#times #=end ### ALL FACES GET MAIN MATERIAL faces=[] d.entities.each{|e|faces << e if e.is_a?(Sketchup;;Face)} mat=nil faces.each{|e|mat=e.material if e.material} faces.each{|e| e.material=mat e.back_material=mat } ### MOVE ORIGIN TO BOTTOM-CENTER OF OUTLINE tr=Geom;;Vector3d.new(-bb.center.x, 0, 0) d.entities.transform_entities(tr, d.entities.to_a) ### MAKE IT A FACE-ME COMPONENT #d.behavior.always_face_camera=true ### FINALLY... SAVE IT INTO 'PEOPLE' AS A' SKP' d.refresh_thumbnail d.save_as(File.join(ff, File.basename(name, ".*")+".skp")) }#names #model.commit_operation puts puts 'Done.' puts return true end#def end#module
Advertisement