Getting (t, c, k) values from OpenCascade surfaces - python

I've created a library for creating and using b-spline surfaces in Python, utilizing parallel scipy.interpolate.RectBivariateSpline() instances to hold the knot vectors, (X, Y, Z) control point mesh, and degrees in u and v (the (t, c, k) tuple against which surface evaluation is performed). I also wrote a STEP parser to read surface data exported from CAD packages; I take the (t, c, k) values from the b_spline_surface_with_knots entities in the file and stuff them into my own objects. The surface library works pretty well for me, but the STEP parser is a pain and fails in various ways almost every time I use it. So I've tried using a 'real' STEP parser, like this:
from OCC.STEPControl import STEPControl_Reader
from OCC.IFSelect import IFSelect_RetDone, IFSelect_ItemsByEntity
step_reader = STEPControl_Reader()
status = step_reader.ReadFile('c:/LPT/nomdata/lpt3.stp')
if status == IFSelect_RetDone: # check status
failsonly = False
step_reader.PrintCheckLoad(failsonly, IFSelect_ItemsByEntity)
step_reader.PrintCheckTransfer(failsonly, IFSelect_ItemsByEntity)
ok = step_reader.TransferRoot(1)
_nbs = step_reader.NbShapes()
aResShape = step_reader.Shape(1)
else:
print("Error: can't read file.")
sys.exit(0)
Now I have this aResShape object, but no amount of poking and prodding it in IPython (nor googling) reveals how to get at the (t, c, k) values that define the surface.
Can someone please point me to the method that will reveal these values? Or is there possibly another Python-based STEP parser that's a little less opaque?

The question is a bit old, but just in case anybody else hits here with a similar problem...
The result of step_reader.Shape() is a TopoDS_Shape, which is a topological entity which can be divided into the following component topologies:
Vertex – a zero-dimensional shape corresponding to a point in geometry;
Edge – a shape corresponding to a curve, and bound by a vertex at each extremity;
Wire – a sequence of edges connected by their vertices;
Face – part of a plane (in 2D geometry) or a surface (in 3D geometry) bounded by a closed wire;
Shell – a collection of faces connected by some edges of their wire boundaries;
Solid – a part of 3D space bound by a shell;
Compound solid – a collection of solids.
Tipically, you'd query it with the method TopoDS_Shape::ShapeType() in order know what is that shape (vertex? edge?, ...).
If the model is formed by a single b-spline surface, the shape then should be a TopoDS_Face, that you can get by calling:
face = aResShape.Face();
Once you have the TopoDS_Face at hand, you can get the underlying geometry (Geom_Surface) like this:
surface = BRepAdaptor_Surface(face).Surface().BSpline();
Now that you have had access to the underlying geometry, you can call this object's methods and they will provide you with the information you need.
They are documented here:
https://www.opencascade.com/doc/occt-7.1.0/refman/html/class_geom___b_spline_surface.html
OpenCASCADE documentation may seem confusing, but I think you might be interested on this topic:
https://www.opencascade.com/doc/occt-7.0.0/overview/html/occt_user_guides__modeling_data.html#occt_modat_3
Hope it helps.

Related

What are the ximgproc_DisparityWLSFilter.filter() Arguments?

I get a ximgproc_DisparityWLSFilter from cv2.ximgproc.createDisparityWLSFilter(left_matcher),
but I cannot get ximgproc_DisparityWLSFilter.filter() to work.
The error I get is
OpenCV Error: Assertion failed (!disparity_map_right.empty() && (disparity_map_right.depth() == CV_16S) && (disparity_map_right.channels() == 1)) in cv::ximgproc::DisparityWLSFilterImpl::filter, file ......\opencv_contrib\modules\ximgproc\src\disparity_filters.cpp, line 262
In general, how do I figure out how to use this, when there isn't a single google result for "ximgproc_DisparityWLSFilter"?
I had this issue too, what you need to do is create the filter first. Then you can filter... hopefully that makes sense. Here is a code snippet of what I used tested on Python 3.6 opencv3.4.2
wls = cv2.ximgproc.createDisparityWLSFilter(left_Matcher)
filteredDisp = wls.filter(leftStereoComputeOutput, leftOriginalImage, disparity_map_right=rightStereoComputeOutput)
In order to figure out how this bit worked, I had to look at the documentation and at what other people had implemented on Github, then connect the pieces. Lots of Trial and Error.
Arguments for the filter are:
Python:
filtered_disparity_map = cv.ximgproc_DisparityFilter.filter( disparity_map_left, left_view[, filtered_disparity_map[, disparity_map_right[, ROI[, right_view]]]] )
Parameters:
disparity_map_left disparity map of the left view, 1 channel, CV_16S type. Implicitly assumes that disparity values are scaled by 16 (one-pixel disparity corresponds to the value of 16 in the disparity map). Disparity map can have any resolution, it will be automatically resized to fit left_view resolution.
left_view left view of the original stereo-pair to guide the filtering process, 8-bit single-channel or three-channel image.
filtered_disparity_map output disparity map.
disparity_map_right optional argument, some implementations might also use the disparity map of the right view to compute confidence maps, for instance.
ROI region of the disparity map to filter. Optional, usually it should be set automatically.
right_view optional argument, some implementations might also use the right view of the original stereo-pair.
The above Parameters were found at https://docs.opencv.org/3.4/db/d72/classcv_1_1ximgproc_1_1DisparityFilter.html
Unlike c++, Python doesn't work well with pointers. So the arguments are
Filtered_disp = ximgproc_DisparityWLSFilter.filter(left_disp,left, None,right_disp)
Note that it's no longer a void function in Python!
I figured this out through trial and error though.

Elegant Solution to an OOP Issue involving type-changing in python

I run into an OOP problem when coding something in python that I don't know how to address in an elegant solution. I have a class that represents the equation of a line (y = mx + b) based on the m and b parameters, called Line. Vertical lines have infinite slope, and have equation x = c, so there is another class VerticalLine which only requires a c parameter. Note that I am unable to have a Line class that is represented by two points in the xy-plane, if this were a solution I would indeed use it.
I want to be able to rotate the lines. Rotating a horizontal line by pi/2 + k*pi (k an integer) results in a vertical line, and vice versa. So a normal Line would have to somehow be converted to a VerticalLine in-place, which is impossible in python (well, not impossible but incredibly wonky). How can I better structure my program to account for this problem?
Note that other geometric objects in the program have a rotation method that is in-place, and they are already used frequently, so if I could I would like the line rotation methods to also be in place. Indeed, this would be a trivial problem if the line rotation methods could return a new rotated Line or VerticalLine object as seen fit.
Geometric objects that have a fixed boundary/end-points can be translated and rotated in place. But for a line, unless you talk about a line from point A to point B with a fixed length, you are looking at both end-points either being at infinity or -infinity (y = mx + c). Division using infinity or -infinity is not simple math and hence I believe complicates the rotation and translation algorithms

OOP Programming style decision

I just have a quick question about an OOP programming I've been having difficulty deciding. The premise is that I'm making a set of very simple geometric classes such as vertex and angle and vector objects, but one of the classes, the line class to be specific, is a little different. It's basically just a collection of methods that I use one time only, I never actually save a line object for later use or recollection of data anywhere else in the program. An example usage to demonstrate my point would be this:
class Line:
def __init__(self, vertex1, vertex2):
self.start = vertex1
self.end = vertex2
def to_the_left(self, vertex):
"""Check to see if a vertex is to the left of the line segment."""
#code stuff
data = Line(Vertex(0, 0), Vertex(10, 0)).to_the_left(Vertex(5, 5))
I only ever instantiate Line(Vertex(0, 0), Vertex(10, 0)) once to retrieve the data. So I was thinking that I might as well just have a bunch of functions available instead of packing it all into a class, but then I was skeptical about doing that since there are a ton of methods that would have to be converted to functions.
Another thing I was thinking of doing was to make a Line class and then convert all it's methods into normal functions like so:
#continuing from the code above
def to_the_left(line_start, line_end, vertex):
return Line(line_start, line_end).to_the_left(vertex)
data = to_the_left(Vertex(0, 0), Vertex(10, 0), Vertex(5, 5))
Which method do you think I should use?
I would opt for using an object as you might need to do multiple operations on Line.
For example you might compute the length, if it's to the left, and some other operation. You might need to pass the Line around who knows.
One thing you might want to consider is instead of using Line and Vertex, use Vector which acts as both. If your vertex has x,y you can make a Vector that has x,y,w.
In this scheme w=1 for vertices and w=0 for Lines - it would simplify a lot of code.
Look up Homogenous coordinates to learn more

Force matrix_world to be recalculated in Blender

I'm designing an add-on for blender, that changes the location of certain vertices of an object. Every object in blender has a matrix_world atribute, that holds a matrix that transposes the coordinates of the vertices from the object to the world frame.
print(object.matrix_world) # unit matrix (as expected)
object.location += mathutils.Vector((5,0,0))
object.rotation_quaternion *= mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(45))
print(object.matrix_world) # Also unit matrix!?!
The above snippet shows that after the translation, you still have the same matrix_world. How can I force blender to recalculate the matrix_world?
You should call Scene.update after changing those values, otherwise Blender won't recalculate matrix_world until it's needed [somewhere else]. The reason, according to the "Gotcha's" section in the API docs, is that this re-calc is an expensive operation, so it's not done right away:
Sometimes you want to modify values from python and immediately access the updated values, eg:
Once changing the objects bpy.types.Object.location you may want to access its transformation right after from bpy.types.Object.matrix_world, but this doesn’t work as you might expect.
Consider the calculations that might go into working out the objects final transformation, this includes:
animation function curves.
drivers and their pythons expressions.
constraints
parent objects and all of their f-curves, constraints etc.
To avoid expensive recalculations every time a property is modified, Blender defers making the actual calculations until they are needed.
However, while the script runs you may want to access the updated values.
This can be done by calling bpy.types.Scene.update after modifying values which recalculates all data that is tagged to be updated.
Calls to bpy.context.scene.update() can become expensive when called within a loop.
If your objects have no complex constraints (e.g. plain or parented), the following can be used to recompute the world matrix after changing object's .location, .rotation_euler\quaternion, or .scale.
def update_matrices(obj):
if obj.parent is None:
obj.matrix_world = obj.matrix_basis
else:
obj.matrix_world = obj.parent.matrix_world * \
obj.matrix_parent_inverse * \
obj.matrix_basis
Some notes:
Immediately after setting object location/rotation/scale the object's matrix_basis is updated
But matrix_local (when parented) and matrix_world are only updated during scene.update()
When matrix_world is manually recomputed (using the code above), matrix_local is recomputed as well
If the object is parented, then its world matrix depends on the parent's world matrix as well as the parent's inverse matrix at the time of creation of the parenting relationship.
I needed to do this too but needed this value to be updated whilst I imported a large scene with tens of thousands of objects.
Calling 'scene.update()' became exponentially slower, so I needed to find a way to do this without calling that function.
This is what I came up with:
def BuildScaleMatrix(s):
return Matrix.Scale(s[0],4,(1,0,0)) * Matrix.Scale(s[1],4,(0,1,0)) * Matrix.Scale(s[2],4,(0,0,1))
def BuildRotationMatrixXYZ(r):
return Matrix.Rotation(r[2],4,'Z') * Matrix.Rotation(r[1],4,'Y') * Matrix.Rotation(r[0],4,'X')
def BuildMatrix(t,r,s):
return Matrix.Translation(t) * BuildRotationMatrixXYZ(r) * BuildScaleMatrix(s)
def UpdateObjectTransform(ob):
ob.matrix_world = BuildMatrix(ob.location, ob.rotation_euler, ob.scale)
This isn't most efficient way to build a matrix (if you know of a better way in blender, please add) and this only works for XYZ order transforms but this avoids exponential slow downs when dealing with large data sets.
Accessing Object.matrix_world is causing it to "freeze" even though you don't do anything to it, eg:
m = C.active_object.matrix_world
causes the matrix to be stuck. Whenever you want to access the matrix use
Object.matrix_world.copy()
only if you want to write the matrix, use
C.active_object.matrix_world = m

Python - basics - how to make a 'class('?) function for a simple set of maths based variables

I'm trying to write a basic drawing widget using the Tkinter library.
The very basic code I am using for now is:
from Tkinter import *
master = Tk()
w = Canvas(master, width=1200, height=800)
w_centre = 600
h_centre = 400
w.pack()
w.create_oval(w_centre-50, h_centre-50, w_centre+50, h_centre+50)
mainloop()
What actually want to do is start with 3 variables, x,y (centre of circle) and size. From there, I can use simple maths to work out the (x0, y0, x1, y1) set required to make the circle (http://docs.huihoo.com/tkinter/tkinter-reference-a-gui-for-python/create_oval.html)
I want to do this programatically, by feeding in the size as a value from a dataset, and x,y as dependant value (if I need 1 circle, it would I would use x1,y1 if I need two circles they would be x2,y2 & x3,y3 etc). The purpose being to try and build a basic visualiser for a dataset I have. I figure I can write an array of the x,y coords that I can look up as required, and as the size value will be pulled from a list - so it would be better to write a function that would take the size, lookup the x,y as required and feed the create_circle call the appropriate values.
I know I need to call the create_oval function with the x0,y0,x1,y1 values, and I wonder if there was a way I could call another function that would allow me to make these values every time by handing it the x,y (centre of circle) and size (radius) value, and for it give me back the relevant x0,y0,x1,y1 values.
As this is a reusable piece of maths, I think I need to make a class, but I can't find a tutorial that helps me to understand how to define the class function, and then to call it every time I need it.
I appreciate I've probably not worded this very well, I'm trying to learn rudimentary python on my own (with no CS background) so please forgive me if I've named something wrong, or missed something important.
Could someone one throw me a hint or a pointer towards a decent resouce?
Python allows you to return any kind of object from a function; in particular, you can return the tuple (x0,y0,x,1,y1) that you need for create_oval:
def enclosing_box(x, y, radius):
"""Given the coordinates of the circle center and its radius, return the top-left and bottom-right coordinates of the enclosing box."""
return (x-radius, y-radius, x+radius, y+radius)
Then you can use the *args syntax to call a function with a set of arguments taken from a sequence (a list, a tuple, etc.). You can use it to call create_oval this way:
coords = enclosing_box(x,y,radius)
w.create_oval(*coords)

Categories