Raytest Alternative?
-
I'm using code to iterate through a series of faces, taking each center point, and testing each individually using raytest to see if the face is "visible" to another far away test point, given the other geometry present in the model (ie, if the ray "hits" something else, it isn't visible).
It works great EXCEPT that it is excruciatingly slow, especially when there are many faces and/or test points.
Is there a better way?
Here's a snippet:
def self.test_points(face) center = face.bounds.center normal = face.normal counter = 1 0.upto(test_points.length-1) {|s| ray = [center,test_points[s]] item = @model.raytest(ray,false) #puts item if item == nil sv = center.vector_to test_points[s] an = normal.angle_between sv counter += Math.cos(an) end
-
I don't fully follow your code
You suddenly seem to recall the 'test_points' method from within itself to no affect ?
Where does the 'test_points' come from that you use - it is used as an array BUT you also have a method with the same name that is passed a (face)...
If you are declaring an array of 'test_points' then name it '@test_points' and use that - because the reference is loss across methods without the @ !!
You also seem to be passing points as vectors in the raytest
And when the test fails incrementing a counter by an[gle] that unused elsewhere anyway -
You're absolutely correct...I pulled the code out of context of a longer section of code, re-(mis-)labeled a few things, all in an effort to minimize the reading required by anyone attempting to help me out, since RAYTEST seems to be the crux of the problem (I think).
In case you care to read my entire code, it is below, though I've added a big comment block with #RAYTEST SECTION# if you want to skip to that.
The test point array is '@suns'
Thank you for trying to help me
module MC_sun_tools @model = Sketchup.active_model @ents = @model.entities @sel = @model.selection def MC_sun_tools.generate_suns # Initialize some variables tz = -5.0 altitude = 1000.m #We'll generate points 1km away @suns = [] # An array to hold our "suns" si = @model.shadow_info ct = si["ShadowTime"] #the class of ct is Time yr = ct.year mon = 9 #ct.month day = ct.day # The points we add will be part of a new group #my_verts = @sel[0].vertices new_ent = @ents.add_group new_ent.name = "{mon}-{day}" 0.upto(24*4-1) {|hour| t = Time.local(yr, mon, day, hour/4, (hour.to_f/4 % 1)*60) si['ShadowTime'] = t v = si["SunDirection"] v.length = altitude pt = ORIGIN + v new_ent.entities.add_cpoint(pt) new_ent.entities.add_text(((hour/4-tz) % 24).to_i.to_s, pt) @suns << pt #if pt.is_a? Sketchup;;Point3d } end def MC_sun_tools.get_faces my_faces = [] @sel.each {|e| if e.is_a? Sketchup;;Face my_faces << e end } my_faces.each {|e| MC_sun_tools.test_suns(e) } puts my_faces.length end ###################################################### #########RAYTEST SECTION############################## def MC_sun_tools.test_suns(face) center = face.bounds.center normal = face.normal face.material= @color_hash[1] counter = 1 0.upto(@suns.length-1) {|s| ray = [center,@suns[s]] item = @model.raytest(ray,false) #puts item if item == nil sun_vector = center.vector_to @suns[s] an = normal.angle_between sun_vector counter = counter + Math.cos(an)*0.8 #puts cos(an) #@ents.add_cline(center, @suns[s]) end } face.material = @color_hash[[counter.to_i, 25].min] end ######################################################## @color_hash = [[0, 0, 180], [0, 30, 180], [0, 60, 180], [0, 90, 180], [0, 120, 180], [0, 150, 180], [0, 180, 180], [0, 180, 150], [0, 180, 120], [0, 180, 90], [0, 180, 60], [0, 180, 30], [0, 180, 0], [30, 180, 0], [60, 180, 0], [90, 180, 0], [120, 180, 0], [150, 180, 0], [180, 180, 0], [180, 150, 0], [180, 120, 0], [180, 90, 0], [180, 60, 0], [180, 30, 0], [180, 0, 0]] end plug_menu = UI.menu("Plugins") plug_menu.add_item("Generate Suns") {MC_sun_tools.generate_suns} plug_menu.add_item("Test Suns") {MC_sun_tools.test_suns} plug_menu.add_item("Get Faces") {MC_sun_tools.get_faces}
-
Why do you mess with si["SunDirection"] ?
Why not pass that as a vector to the @suns array...
Messing on adding it to the ORIGIN etc just causes me confusion ?
I deduce you are testing sun-angles across time and seeing if a face's center can see the sun...
I don't think you need to make it quite so convoluted... -
You're right about what the code is attempting to do, and no doubt it needs to be cleaned up and modularized a bit. To answer your question: I call si["SunDirection"] when generating the @sun array because the Point3ds (generated from vectors about ORIGIN) will depend on the geolocation and date of the model. Hence, Colorado's suns will differ from London's.
Regardless, the ShadowInfo calls within the sun array routine aren't the time constraint within the code-- RayTest seems to be the culprit. Given the profusion of rendering programs which are undoubtedly doing MANY more collision calculations, I'm wondering if there's an obvious, less computationally expensive substitute to calling RayTest.
-
It seems to work fast enough for me ?
Also remember that the sun's vector is the same irrespective of where you 'place it' ?
Setting it's length to 1km does nothing to it when you use it in the raytest - I realize you want it for the 'sun location...
Adding it .to_a to the ORIGIN returns a point.A simple raytest is
model.raytest([point,vector])
So for any 'point' you pass to the raytest using the sun_direction vector [unaltered] will return 'nil' if it hits nothing or a two element array otherwise - which in your case you can just ignore.I assume your code should then 'color' faces depending on if their center fails the raytest and its angle towards the sun...
The raytesting itself should be relatively quick - how many faces are you testing ?
-
Well I hadn't thought of the sun vector that way, but that is great advice-- should certainly speed things up, a little at least.
As for the number of faces...I'd like to take a surface and run it with arbitrary granularity; so, thousands. Running my code as it stands for 1000 faces takes on the order of 15 minutes to run.
Making the change you suggested now...
Advertisement