How I could get the actual toposhape data in OpenCascade? - python

All
have file from CAD (SW) in STEP format and was able to read it via Python OCC binding:
importer = aocxchange.step.StepImporter(fname)
shapes = importer.shapes
shape = shapes[0]
# promote up
if (shape.ShapeType() == OCC.TopAbs.TopAbs_SOLID):
sol = OCC.TopoDS.topods.Solid(shape)
I could display it, poke at it, check flags etc
t = OCC.BRepCheck.BRepCheck_Analyzer(sol)
print(t.IsValid())
print(sol.Checked())
print(sol.Closed())
print(sol.Convex())
print(sol.Free())
print(sol.Infinite())
So far so good. It really looks like small tube bent along some complex path.
Question: how I could extract geometry features from what I have? I really need tube parameters and path it follows. Any good example in Python and/or C++ would be great

In OpenCASCADE there's a separation between topology and geometry. So, usually your first contact will be the topological entities (i.e.: TopoDS_Wire or a TopoDS_Edge), that can give you access to the geometry (take a look here for more details).
In your case, after reading the STEP file you ended up with a TopoDS_Shape. This is the highest level topological entity and most probably is formed by one or more sub-shapes.
Assuming that your shape is formed by a bspline curve (it seems to be!), you could explore the shape, looking for TopoDS_Edge objects (they are the topological entities that map to geometric curves):
TopExp_Explorer myEdgeExplorer(shape, TopAbs_EDGE);
while (myEdgeExplorer.More())
{
double u0, u1;
auto edge = TopoDS::Edge(myEdgeExplorer.Current());
auto curve = BRep_Tool::Curve(edge, u0, u1);
// now you have access to the curve ...
// to get a point lying on it, check
// the method curve->Value(u);
myEdgeExplorer.Next();
}

Related

IfcOpenShell find coordinates from IfcWall

I would like to find the length between two points from an IFC model.This is an example of an IfcWall from the IFC model.
#26322= IFCWALL('3vpWoB_K1EZ8RCaYmNGsB2',#42,'Basiswand:Bestand 08.0:162343',$,'Basiswand:Bestand 08.0:161894',#25861,#26318,'162343',.NOTDEFINED.);
#26325= IFCPROPERTYSET('3vpWoB_K1EZ8RCcT4NGsB2',#42,'Pset_WallCommon',$,(#787,#788,#848,#25851));
#26327= IFCRELDEFINESBYPROPERTIES('0rDc6OePf5NBrNT2GfJ3hm',#42,$,$,(#26322),#26325);
#26331= IFCCARTESIANPOINT((12.5832056790716,5.54096330043285,0.));
#26333= IFCAXIS2PLACEMENT3D(#26331,#20,#18);
#26334= IFCLOCALPLACEMENT(#140,#26333);
#26335= IFCCARTESIANPOINT((4.24,0.));
#26337= IFCPOLYLINE((#10,#26335));
#26339= IFCSHAPEREPRESENTATION(#102,'Axis','Curve2D',(#26337));
The IFCPOLYLINE has two Points (#10=0,0 and #26335=4.24,0.) and i would like to find out the distance between these two points.
The other walls have a length deposited, but this one wall does not. Here is an example of the other walls:
#730= IFCWALL('1ZwJH$85D3YQG5AK5ER10a',#42,'Basiswand:Bestand 50.0:148105',$,'Basiswand:Bestand 50.0:150882',#701,#726,'148105',.NOTDEFINED.);
#745= IFCQUANTITYLENGTH('Height',$,$,4.99,$);
#746= IFCQUANTITYLENGTH('Length',$,$,16.675,$);
This is my code example:
import ifcopenshell
walls = ifc_file.by_type('IfcWall')
print(len(walls))
import math
p1 = [0.,0.]
p2 = [16.765,0.]
distance = math.sqrt( ((p1[0]-p2[0])**2)+((p1[1]-p2[1])**2) )
print(distance)
To apply the mathematical formula, I have to extract the coordinates from the wall for p1 and p2. I am not getting further here.
thank you in advance!
You need to work your way through the object graph, starting at the wall:
#26322 IfcWall.Representation (attribute 7) references #26318
#26318 is not included in your snippet, but likely an IfcProductDefinitionShape
From there you would likely find another polyline similar to the one you have included in the snippet. See below for how to get there. There is likely another wall which is represented by the polyline #25337. Starting from there you arrive at the polyline as follows:
#XXXXX IfcWall.Representation (attribute 7) references #YYYYY likely IfcProductDefinitionShape
#YYYYY IfcProductDefinitionShape.Representations (attribute 4) likely references #26339 (the 2D representation) and a 3D representation
#26339 IfcShapeRepresentation.Items (attribute 4) references #26337
#25337 IfcPolyline.Points (attribute 1) references #10 and #26335
You can study the IFC specification to find out how the entities are connected via their attributes and how the attributes are called.
It might be easy for a this particular case to trace the object graph. The hard part is the semantic richness of the schema with a variety of types which share some attributes and vary in others, which is organized through inheritance. For example, the Item attribute of an IfcShapeRepresentation entity references entities of type IfcRepresentationItem, which has many subtypes, IfcPolyline being only one of those. You will have to check which type you encounter and only if it is an IfcPolyline, your method of calculation would be applicable - not if it would be an IfcBSplineCurve, for example.
Libraries such as IfcOpenShell have invested a good deal of work into covering all or at least most of the schema, particularly the geometry, and can also calculate measures such as length, area, volume, if I am not mistaken.

Questions about the Gmsh Python API

I am trying to write a script that will automatically mesh geometries for CFD analysis using the Gmsh Python API. There are a few issues I am running into:
First of all, I would like to be able to write Gmsh script files (.geo) for debugging purposes. I looked through the source code of the Gmsh API and found that the .geo_unrolled extension is supported for the gmsh.write() function, but not just .geo. This extension does the trick mostly, but it seems that any meshing operations (such as marking curves as transfinite) or transformations (such as dilate) are not written to the output file when using gmsh.write('test.geo_unrolled'). I assume this has something to do with the _unrolled part. But is there any way to get the full Gmsh script out of the API?
Secondly, when I try to make a copy of a spline like in this example:
p1 = gmsh.model.geo.addPoint(-1, 0.5, 0, 0.1)
p2 = gmsh.model.geo.addPoint(0, 1, 0, 0.1)
p3 = gmsh.model.geo.addPoint(1, 0.5, 0, 0.1)
s1 = gmsh.model.geo.addSpline([p1, p2, p3])
s2 = gmsh.model.geo.copy([s1])
I get ValueError: ('gmshModelGeoCopy returned non-zero error code: ', 1). The error code, 1, seems to indicate that the tag of the original spline (s1) cannot be found when copy() is called. Am I missing something here? I have tried, for example, to call gmsh.model.geo.synchronize() before attempting to call copy(), but this had no effect.
Finally, when I use the dilate transformation in the Gmsh GUI using Modules - Geometry - Elementary entities - Transform - Scale, checking the Apply scaling on copy option in the dialog, on the example spline from above, I indeed get a scaled version of the curve as expected, including the three points. Assuming I was able to accomplish the same with the API, how do I then refer to the three new points that the scaled spline goes through, for example, if I wanted to draw a line between the start point of the original spline and that of the scaled spline?
In the end, what I want to accomplish is the following: draw a spline through a list of points, create a scaled copy of this spline, draw lines between the start and end points, and create a plane surface bounded by the two splines and lines. Is there a better way to do this than what I am trying to do with the dilation?
It's probably too late, but you never know.
I've never had to create .geo files using the API. But I found this discussion in the Gmsh mailing list archive, which may be helpful.
Regarding your error with copy, you have to specify the dimension of the entity to be copied, and not just the tag (check the documentation, which refers to dimTag). It's the same thing with transformations such as rotate, symmetrize etc. Using the following should work:
s2 = gmsh.model.geo.copy([(1, s1)])
NB: when copying only one entity, I think either the inner parentheses or the brackets are superfluous, and otherwise you have to provide a list of tuples of the form [(dim_1, tag_1), (dim_2, tag_2), ..., (dim_n, tag_n)].
Keep in mind that copy will return a variable of the same kind (list of tuples), i.e. in your case the variable s2 will be [(1, tag_s2)]. Therefore you might not want to use the same kind of variable name, since in order to get the tag you'll have to use s2[0][1] instead of simply s2.
Here you have a partial answer to the following question, as the tags of copied entities will be contained in your return variable.
Hope that helps you or others!

How to classify the points by a specific polylline

There is a boundary inside China, which divide the region as North-South. I have drawn this boundary as a polyline format shapefile Download link.
I want to divide those points in the following figures into "North" and "South". Is there any useful function in Python can achieve this.
fiona has point.within function to test points within/out a polygon, but I have not searched a suitable function to divide multiple points by polyline.
Any advices or tips would be appreciated!
updated
According to the valuable suggestion made by Prune, I worked it out. The codes are provided as follows:
from shapely.geometry import shape
from shapely.geometry import LineString
# loading the boundary layer
import fiona
fname = './N-S_boundary.shp'
line1 = fiona.open(fname)
line1 = shape(line1.next()['geometry'])
# set a end point which is the southernmost for all stations.
end_point = (dy[dy['lat']==dy['lat'].min()]['lon'].values[0],dy[dy['lat']==dy['lat'].min()]['lat'].values[0])
# loop all monitoring stations for classification
dy['NS']= np.nan
for i in range(0,len(dy),1):
start_point = (dy['lon'].iloc[i],dy['lat'].iloc[i])
line2 = LineString([start_point, end_point])
if line1.intersection(line2).is_empty:
dy["NS"].iloc[i]='S'
else:
dy["NS"].iloc[i]='N'
color_dict= {'N':'steelblue','S':'r'}
dy['site_color']=dy['NS'].map(color_dict)
You can apply a simple property from topology.
First, make sure that your boundary partitions the universe (all available points you're dealing with). You may need to extend the boundary through the ocean to finish this.
Now, pick any reference point that is labeled as to the region -- to define "North" and "South", you must have at least one such point. w.l.o.g. assume it's a "South" point called Z.
Now, for each point A you want to classify, draw a continuous path (a straight one is usually easiest, but not required) from A to Z. Find the intersections of this path with the boundary. If you have an even quantity of intersections, then A is in the same class ("South") as Z; other wise, it's in the other class ("North").
Note that this requires a topological property of "partition" -- there are no tangents to the boundary line: if your path touches the boundary, it must cross completely.

NurbsCurve MatrixMath Maya api Python

I am creating a toolset for creating nurbs curves/surfaces inside maya using python.
I have a set of dictionaries that include cvPositions, knots, form etc. each of which describe a preset 3d shape (cube, circle, pyramid etc).
I also have a 3d matrix stored in the nodes metadata that is used as an offset for the shape. This allows you to scale/move/rotate the shape without moving the transform.
The problem is in the way I am applying this matrix is very slow:
First I will create a new (edit)transform at the position of the (orig)transform containing the curves.
Next I will transfer cv positions in world space from (orig)transform to (edit)transform
Next i will move the (edit)transform into the matrix position.
Finally I will transfer the cvPositions back to the (orig)transform
When creating hundreds of shapes, this is becoming prohibitively slow...
Can someone describe a mathematical way to apply a matrix to a set of 3d points?
Perhaps using one of the math modules or numpy?
Alternatively,
Is there a way using OpenMaya api functions to do this? Perhaps with MPointArray? This is as far as I have gotten on that front:
crv = OpenMaya.MFnNurbsCurve( self.dagPath )
cvs = OpenMaya.MPointArray()
space = OpenMaya.MSpace.kWorld
crv.getCVs(cvs, space)
positions = []
for i in range(cvs.length()):
pt = cvs[i]
positions.append( (pt[0], pt[1], pt[2]) )
The easiest method is to use pymel's built-in versions of points and matrices (pymel is built in to maya 2011+). The math types are in pymel.datatatypes; here's an example of transforming a point by a matrix in pymel:
import pymel.core as pm
pt = pm.datatypes.Point(0,0,0)
mt = pm.datatypes.Matrix(1,0,0,0, 0,1,0,0, 0,0,1,0, 5,5,5,1 )
moved = pt * mt
print moved
# [5,5,5]
Pymel points and matrices will let you do your algorithm. The math is going to be done in the API but the Python <> C++ conversions may still make it feel pretty slow for big data.
It sounds like you're basically re-creating 'freeze transforms' followed by 'zero pivots'. Maybe you should try that as an alternative to doing this in python math...

How can I take a list of points that create a line and extend them into polygons inward of a certain length?

I have gps coordinates. They are just points that create an outline in a few different places, . I want to be able to have a script convert these points into polygons that extend X distance inwards, and also- a way for them to extend both inwards and outwards.
So if I had something like this:
(dots are points)
00000000000000000
00000000000000000
00............000
00.0000000000.000
00.0000000000.000
00.0000000000.000
00.0000000000.000
00.0000000000.000
00............000
00000000000000000
00000000000000000
I could run this program with a distance of 1 and "inwards", and I would end up with a polygon of # shape:
00000000000000000
00000000000000000
00&&&&&&&&&&&&000
00&&&&&&&&&&&&000
00&&00000000&&000
00&&00000000&&000
00&&00000000&&000
00&&&&&&&&&&&&000
00&&&&&&&&&&&&000
00000000000000000
00000000000000000
So far I have tried using circles and then reducing them but it seems wrong / not really feasible. This isn't being performed on a grid, actually it used floats for coordinates.
Any libraries that could do this as well are appreciated.
GDAL/OGR is another option. Ultimately what you want to do is a buffer. To expand your polygons shape outward use a buffer with a positive buffer distance, inwards it would be negative buffer distance. The following is a simple example using a shapefile. Not sure what format your data is in, but I would be surprised if GDAL/OGR can't read it.
import osgeo.ogr
# using ESRI Shape file in this example but there are a number
# of different files this lib supports: http://www.gdal.org/ogr/ogr_formats.html
driver = osgeo.ogr.GetDriverByName('ESRI Shapefile')
osgeo.ogr.UseExceptions()
# Create a data source using the driver...
dataSource = driver.Open("/home/user1/data.shp")
# Get the layer
lyr = dataSource.GetLayer()
# Select the feature in this case using an attribute query
lyr.SetAttributeFilter("column = 'value'")
# verify that you have a feature selected
print 'features in layer:', lyr.GetFeatureCount()
# get the firest feature from the layer
feature = lyr.GetNextFeature()
# get the geometry from the feature
geom = feature.GetGeometryRef()
# perform a 100 unit buffer, not sure what units the coordinates of the
# the data you have are in.
bufferGeom = geom.buffer(100)
# bufferGeom is a geometry object, which is described here:
# <http://cosmicproject.org/OGR/ogr_classes.html#Geometry>
The following is a fantastic resource for getting started with working with spatial data using GDAL/ORG: http://www.gis.usu.edu/~chrisg/python/2009/
Api docs: http://cosmicproject.org/OGR/ogr_classes.html
finally here is the link to the GDAL/OGR page. http://www.gdal.org/
https://pypi.python.org/pypi/Shapely
Shapely is a very good 2d computational geometry library; the way I understand it, it reduces your problem to a single line of code.

Categories