Sum up Area for material in Components, Google Sketchup - ruby

I am making a plugin to sum up the area of all the material in a Sketch.
I have succeeded in getting all the faces and such, but now the Components come into the picture.
Im using the term single or multi leveled component as i dont know any better way to explain the occurence of having a component inside an component and so on.
I have noticed that some components also have more to i than just 1 level. So if you go inside one component there may be components embedded inside this component that also have materials. So what i want is to sum up all of the material of a specific component and get all the "recursive" materials, if any, inside the component.
So, how do I count the area of all the material inside an component(single or multileveled)?

Here is what I would do, let's suppose that you loop through all entities and check for type of entity.
if entity.is_a? Sketchup::ComponentInstance
entity.definition.entities.each {|ent|
if ent.is_a? Sketchup::Face
#here do what you have to do to add area to your total
end
}
end
You can do the same with a Group with:
if entity.is_a? Sketchup::Group
entity.entities.each {|ent|
if ent.is_a? Sketchup::Face
#here do what you have to do to add area to your total
end
}
end
Hope it helps
Ladislav

Ladislav's example doesn't dig into all the levels.
For that you need a recursive method:
def sum_area( material, entities, tr = Geom::Transformation.new )
area = 0.0
for entity in entities
if entity.is_a?( Sketchup::Group )
area += sum_area( material, entity.entities, tr * entity.transformation )
elsif entity.is_a?( Sketchup::ComponentInstance )
area += sum_area( material, entity.definition.entities, tr * entity.transformation )
elsif entity.is_a?( Sketchup::Face ) && entity.material == material
# (!) The area returned is the unscaled area of the definition.
# Use the combined transformation to calculate the correct area.
# (Sorry, I don't remember from the top of my head how one does that.)
#
# (!) Also not that this only takes into account materials on the front
# of faces. You must decide if you want to take into account the back
# size as well.
area += entity.area
end
end
area
end

Related

Unable to get the location bound of Sketchup model using Sketchup Ruby API

I have a Sketchup 3d model that is geo-located. I can get the geo-location of the model as follows :-
latitude = Sketchup.active_model.attribute_dictionaries["GeoReference"]["Latitude"]
longitude = Sketchup.active_model.attribute_dictionaries["GeoReference"]["Longitude"]
Now i want to render this model on a 3D globe. So i need the location bounds of the 3d model.
Basically i need bounding box of the model on 2d map.
Right now i am extracting the same from the corners of a model(8 corner).
// This will return left-front-bottom corner.
lowerCorner = Sketchup.active_model.bounds.corner(0)
// This will return right-back-top corner.
upperCorner = Skectup.active_model.bounds.corner(6)
But it returns simple geometrical points in meters, inches depending upon the model.
For example i uploaded this model in sketchup. Following are the values of geo-location, lowerCorner and upperCorner respectively that i'm getting by using the above code for the above model.
geoLocation : 25.141407985864, 55.18563969191 //lat,long
lowerCorner : (-9483.01089", -6412.376053", -162.609524") // In inches
upperCorner : (-9483.01089", 6479.387909", 12882.651999") // In inches
So my first question is what i'm doing is correct or not ?
Second question is If yes for the first how can i get the values of lowerCorner and upperCorner in lat long format.
But it returns simple geometrical points in meters, inches depending upon the model.
Geom::BoundingBox.corner returns a Geom::Point3d. The x, y and z members of that is a Length. That is always returning the internal value of SketchUp which is inches.
However, when you use Length.to_s it will use the current model's unit settings and format the values into that. When you call Geom::Point3d.to_s it will use Length.to_s. On the other hand, if you call Geom::Point3d.inspect it will print the internal units (inches) without formatting.
Instead of tapping into the attributes of the model directly like that I recommend you use the API methods of geo-location: Sketchup::Model.georeferenced?
By the sound of it you might find Sketchup::Model.point_to_latlong useful.
Example - I geolocated a SketchUp model to the town square of Trondheim, Norway (Geolocation: 63°25′47″N 10°23′36″E):
model = Sketchup.active_model
bounds = model.bounds
# Get the base of the boundingbox. No need to get the top - as the
# result doesn't contain altiture information.
(0..3).each { |i|
pt = bounds.corner(i)
latlong = model.point_to_latlong(pt)
latitude = latlong.x.to_f
longitude = latlong.y.to_f
puts "#{pt.inspect} => #{longitude}, #{latitude}"
}

How to Draw BoundingBox or Tag Objects Which Has Less Area Than Others

In my image I have 5 Objects in black-white form. Some are respectively small, some are bigger.
So what i am trying to do is drawing a BoundingBox or tag the objects which has less area than others (ex. under 10pixels/area) .
I couldn't make this happen, can anyone help?
That's two separate problems. The first is to select only objects above a certain area. So simply remove all objects below it:
clean = bwareaopen (im, 10); # remove all objects with area below 10
Then for the second problem there are many possibilities. You can get their borders:
borders = bwperim (clean);
imshow (borders);
You can label them:
labeled = bwlabel (clean);
imshow (labeled);
Or you can get their bounding box (which depending on the shape of your objects may overlap):
props = regionprops (clean, 'BoundingBox');
all_bb = props.BoundingBox;
boxes = false (size (clean));
for i = 1:numel (all_bb)
bb = all_bb{i};
bb(round (bb(2):bb(2)+bb(4), bb(1):bb(1)+bb(3))) = true;
end
imshow (boxes);
Note: this was written out of my head, no testing. There may be small oversights, but nothing major.

Creating THREE.Line's with different endpoints using THREE.BufferGeometry

I am creating several THREE.Lines using THREE.BufferGeometry. Initially my app had them all starting at the origin and things worked as expected. Now, I would like to be able to start (and end) them at any point.
This fiddle (http://jsfiddle.net/9nVqU/) illustrates (I hope) how changing one end of the line away from the origin causes unexpected results.
I wondered if it was because any given line follows on from the previous one - switching the start/end order didn't change anything though so if that were true, I'd expect it to break.
Maybe I have the arrays set up incorrectly or the attributes that tell THREE.js how to interpret it - I think I need 2 * 3 verts for each line but changes I made to buffer_geometry.attributes = { seemed to make things worse.
FWIW, the actual effect I'm trying to achieve is to selectively turn on and off the lines based on user input. I can do that already by changing the end position but then I lose that value and I don't want to store it elsewhere. I thought that I could move the start point to the end point to switch it off and then move the start point to the origin again to re-enable it. If there is a way to enable/disable lines individually with BufferGeometry, then that would clearly be better.
First of all, you would have to do this:
var line = new THREE.Line( buffer_geometry, material );
line.type = THREE.LinePieces;
Second, this is not supported in r.58 , but should be.
As a work-around, you can hack WebGLRenderer.renderBufferDirect() like so:
// render lines
setLineWidth( material.linewidth );
var position = geometryAttributes[ "position" ];
primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
_gl.drawArrays( primitives, 0, position.numItems / 3 );
_this.info.render.calls ++;
_this.info.render.points += position.numItems;
three.js r.58

Google maps polygon optimization

I extracted country outline data from somewhere and successfully managed to convert it into an array of lat-lng coordinates that I can feed to Google maps API to draw polyline or polygons.
The problem is that that there are about 1200+ points in that shape. It renders perfectly in Google maps but I need to reduce the number of points from 1200 to less than 100. I don't need a very smooth outline, i just need to throw away the points that I can live without. Any algorithm or an online tool that can help me reduce the number of points is needed.
Found this simple javascript by Bill Chadwick. Just feed in the LatLng to an array and pass in to the source arguments in a function here Douglas Peucker line simplification routine
it will output an array with less points for polygon.
var ArrayforPolygontoUse= GDouglasPeucker(theArrayofLatLng,2000)
var polygon=new google.maps.Polygon({
path:ArrayforPolygontoUse,
geodesic:true,
strokeColor:"#0000FF",
strokeOpacity:0.8,
strokeWeight:2,
fillColor:"#0000FF",
fillOpacity:0.4,
editable:true
});
theArrayofLatLng is an array of latlng that you collected using google maps api.
The 2000 value is kink in metres. My assumption is, the higher the value, more points will be deleted as an output.
For real beginners:
Make sure you declare the js file on your html page before using it. :)
<script type="text/javascript" src="js/GDouglasPeucker.js"></script>
I think MapShaper can do this online
Otherwise, implement some algorithm
If you can install postgis which i think is easy as they provide an installer then you can import the data and execute snaptogrid() or st_simplify() for which i cannot find an equivalent in mysql.If you decide to go with postgis which i recommend cause it will help you down the road i can provide you with the details.
Now for an easy custom solution you can reduce size by cutting or rounding some of the last digits of the coords and then merge the same coords resulting actually in a simple snaptogrid().
Hope it helps
I was looking for exactly the same thing and found Simplify.js. It does exactly what you want and is incredibly easy to use. You simply pass in your coordinates and it will remove all excess points.
simplify(points, tolerance, highQuality)
The points argument should contain an array of your coordinates formatted as {x: 123, y: 123}. (Afterwards you can convert it back to the format you wish.)
The tolerance should be the precision in decimal degrees. E.g. 0.0001 for 11 meters. Increasing this number will reduce the output size.
Set highQuality to true for better results if you don't mind waiting a few milliseconds longer.
Mostly likely what you want to divide the points into 2 half and want to try my Javascript function:
function shortenAndShow ( polyline, color ) {
var dist = 0, copyPoints = Array ( );
for ( var n = 0, var end = polyline.getVertexCount ( ) - 1; n < end ; n++ ) {
dist += polyline.getVertex ( n ).distanceFrom ( polyline.getVertex ( n +1 ) );
copyPoints.push ( polyline.getVertex (n) );
}
var lastPoint = copyPoints [copyPoints.length-1];
var newLine = new GPolyline (copyPoints, color, 2, 1);
gmap2.addOverlay ( newLine );
}
I agree the Unreason's anwser,The website support GeoJson,I used it in my website,and it cut down my geoJson ,But I think you also need this world country geo Json

Algorithm for heat map?

I have a list of values each with latitude and longitude. I'm looking to create a translucent heatmap image to overlay on Google Maps. I know there are server side and flash based solutions already, but I want to build this in javascript using the canvas tag.
However, I can't seem to find a concise description of the algorithm used to turn coordinates and values into a heatmap. Can anyone provide or link to one?
Thanks.
The basic idea would be to create a grid and project every lat,lng coord to that grid. I would use a 2D array of ints.
The psuedo-code would be:
for each coord
cell = coord projected to grid
increment cell value
end
for 0 to # of passes
for each row
for each col
if grid[row,col] > 0 then
grid[row,col] += 1
increment_adjacent_cells(row, col)
end
end
end
end
So, the idea is that the higher the int value, the hotter that cell is. increment_adjacent_cells should increment the values in all 8 adjacent cells.
I have tried to solve this in javascript using the canvas element, here is my current result:
http://gist.github.com/346165
I have to fix the gaussian filter and the color mapping, because it doesn't give good results currently.
A faster way of building a heatmap could be to use a queue:
Pseudocode:
Add an element to queue (first in heatmap(x,y, val))
While (!queue.isEmpty())
{
elem = queue.pop()
queue.push(elem.x + 1, elem.y, val-1)
queue.push(elem.x - 1, elem.y, val-1)
queue.push(elem.x, elem.y + 1, val-1)
queue.push(elem.x, elem.y - 1, val-1)
}
This saves on tons of iterations!
Look at this project if you are looking for something that looks more like 'tv weather maps':
https://github.com/optimisme/javascript-temperatureMap

Resources