summaryrefslogtreecommitdiff
path: root/import_tools/surf.py
blob: 105044aaadb489d94132f3c807aebc0c6d1874c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
#!/usr/bin/python
#############################
#put insightful overview here
#############################
from copy import copy, deepcopy
import unittest
import os

#in case i do something stupid and it hangs..
from os import getpid
print "*** pid = ", getpid()

#let's not reinvent the wheel
from skdb import Point, Shape, Face, Wire

#stuff that actually matters
import OCC.TopoDS as TopoDS
from OCC.TopoDS import TopoDS_Vertex
from OCC.TColgp import TColgp_Array1OfPnt, TColgp_Array2OfPnt
from OCC.GeomAPI import GeomAPI_PointsToBSplineSurface, GeomAPI_PointsToBSpline
from OCC.GeomAdaptor import GeomAdaptor_HCurve
from OCC.GeomFill import GeomFill_SimpleBound
from OCC.Geom import Handle_Geom_BSplineSurface, Geom_BSplineSurface
from OCC.GeomPlate import GeomPlate_BuildPlateSurface, GeomPlate_PointConstraint, GeomPlate_MakeApprox
from OCC.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_MakePolygon, BRepBuilderAPI_MakeWire
from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCC.BRepAdaptor import BRepAdaptor_HCurve
from OCC.BRepFill import BRepFill_CurveConstraint
from OCC.Utils.Topology import WireExplorer #what about BRepTools_WireExplorer?
from OCC.TopExp import TopExp_Explorer
from OCC.TopAbs import TopAbs_VERTEX, TopAbs_FACE, TopAbs_WIRE, TopAbs_EDGE
from OCC.Utils.DataExchange.STL import STLImporter
from OCC.BRep import BRep_Tool

#for wire exploring
from OCC.BRepTools import BRepTools_WireExplorer

#gui stuff
from skdb.gui import *

#points = TColgp_Array2OfPnt()
#surface = GeomAPI.GeomAPI_PointsToBSplineSurface(points) #surface is of type GeomAPI_PointsToBSplineSurface
#surface1 = surface.Surface() #surface1 is of type Handle_Geom_BSplineSurface
####surface2 = surface1.GetObject() #surface2 is of type Geom_BSplineSurface
#surface2 = Handle_Geom_Surface(surface1)
#face = BRepBuilderAPI_MakeFace(surface2)
#my_shape = face.Shape()
#OCC.Display.wxSamplesGui.display.DisplayShape(my_shape)

def point_list_to_TColgp_Array1OfPnt(li):
    pts = TColgp_Array1OfPnt(0, len(li)-1)
    for n,i in enumerate(li):
        pts.SetValue(n,i)
    return pts
#not that anyone is going to remember that..
point_list_1 = point_list_to_TColgp_Array1OfPnt

#this doesnt work
def point_list_to_TColgp_Array2OfPnt(li):
    assert (len(li))%2 == 0, "point_list_to_TColgp_Array2OfPnt: the length of the list must be divisible by two so it can be split into two."
    pts = TColgp_Array2OfPnt(0, (len(li)-1)/2, 0, (len(li)-1)/2)
    print "row_length: ", pts.RowLength()
    print "column_length: ", pts.ColLength()
    first_half = li[:len(li)/2]
    second_half = li[len(li)/2:]
    for n,i in enumerate(first_half):
        print "n, i = (%s, %s)" % (n, i)
        pts.SetValue(0,n,i) #row, column, value
    for n,i in enumerate(second_half):
        pts.SetValue(1,n,i)
    print "done"
    return pts
#synonym
point_list_2 = point_list_to_TColgp_Array2OfPnt

def get_simple_bound(rndPts0):    
    _spl1 = GeomAPI_PointsToBSpline(rndPts0) #not a GeomAPI_PointsToBSplineSurface ??
    _spl1.thisown = False
    spl1  = _spl1.Curve()
    
    spl1_adap = GeomAdaptor_HCurve(spl1)
    spl1_adap.thisown = False
    spl1_adap_h = spl1_adap.GetHandle()
    bound1 = GeomFill_SimpleBound(spl1_adap_h, 0.001, 0.001)
    bound1.thisown = False
    bound1_h = bound1.GetHandle()
    return spl1, bound1_h

def build_plate(polygon, points):
    ''' 
    build a surface from a constraining polygon(s) and point(s)
    @param polygon:     list of polygons (TopoDS_Shape)
    @param points:      list of points (gp_Pnt) 
    '''
    # plate surface
    bpSrf = GeomPlate_BuildPlateSurface(3,15,2)
    
    # add curve constraints
    for poly in polygon:
        for edg in WireExplorer(poly.Wire()).ordered_edges():
            c = BRepAdaptor_HCurve()
            c.ChangeCurve().Initialize(edg)
            constraint = BRepFill_CurveConstraint(c.GetHandle(), 0)
            bpSrf.Add(constraint.GetHandle())
     
    # add point constraint
    for pt in points:
        bpSrf.Add(GeomPlate_PointConstraint(pt, 0).GetHandle())
        bpSrf.Perform()
    
    maxSeg, maxDeg, critOrder = 9,8,0
    tol  = 1e-4
    dmax = max([tol,10*bpSrf.G0Error()])
    
    srf = bpSrf.Surface()
    plate = GeomPlate_MakeApprox(srf, tol, maxSeg, maxDeg, dmax, critOrder)
    
    uMin, uMax, vMin, vMax = srf.GetObject().Bounds()
    
    face = BRepBuilderAPI_MakeFace(plate.Surface(), uMin, uMax, vMin, vMax)
    face.Build()
    return face

#this doesnt work, see build_plate instead
def approximate_surface(points=[]):
    #just to be safe, let's convert that list to Point objects
    if points is not []:
        new_points = []
        for point in points:
            if not isinstance(point, Point):
                new_points.append(gp_Pnt(Point(point)._CSFDB_Getgp_Pntcoord()))
            else: new_points.append(point)
        points = new_points
    points = point_list_2(points)
    print "approximate_surface: about to call GeomAPI_PointsToBSplineSurface"
    surface = GeomAPI_PointsToBSplineSurface(points)
    print "approximate_surface: about to call surface.Surface"
    surface1 = surface.Surface()
    surface2 = Handle_Geom_Surface(surface1)
    print "approximate_surface: about to call BRepBuilderAPI_MakeFace"
    face = BRepBuilderAPI_MakeFace(surface2)
    #to get a TopoDS_Face, do: face.Face()
    my_shape = face.Shape()
    print "approximate_surface: returning"
    return my_shape

class Cloud:
    '''a cloud of points in 3D space'''
    def __init__(self, points=[]):
        self._points = points
        pass
    def __add__(self, other):
        '''implements what is known as "registration" (actually not yet)'''
        if isinstance(other, Point):
            #add the point to the cloud
            #FIXME: need to express this point in the common coordinate system ("registration")
            temp = copy(self)
            temp._points.append(other)
            return temp
        elif isinstance(other, Cloud):
            #merge these two clouds
            #FIXME: align the two coordinate systems
            points = copy(self._points)
            points.append(copy(other._points))
            temp = Cloud(points=points)
            return temp

class Surface:
    def __init__(self):
        self.bottom_left, self.bottom_right, self.top_left, self.top_right = Point(0,0,0), Point(0,0,0), Point(0,0,0), Point(0,0,0)
        self.points = [] #points that we want to approximate
        self.shape = Shape()
        self.polygons = [BRepBuilderAPI_MakePolygon()]
    def make_bounding_box(self):
        '''figures out a bounding box from a set of points.
        this is just to set bottom_left, top_right, bottom_right, top_left
        see Surface.approximate for the real magic.'''
        x_list, y_list, z_list = [], [], []
        #really we just need to figure out two points
        for point in self.points:
            if hasattr(point, "__iter__"):
                #it's a list
                for pnt in point:
                    pnt = make_point(pnt)
                    x,y,z = pnt.Coord() #Point(BRep_Tool().Pnt(pnt)).Coord()
                    x_list.append(x); y_list.append(y); z_list.append(z)
                break
            point = make_point(point)
            if hasattr(point, "Coord"):
                x, y, z = point.Coord()
                x_list.append(x)
                y_list.append(y)
                z_list.append(z)
        x_min, y_min, z_min = min(x_list), min(y_list), min(z_list)
        x_max, y_max, z_max = max(x_list), max(y_list), max(z_list)
        #we're assuming z=0 for this plane that we're working on.
        #check to make sure the points line up in the z=0 plane
        self.bottom_left = Point(x_min, y_min, 0)
        self.top_right = Point(x_max, y_max, 0)
        self.bottom_right = Point(x_max, y_min, 0)
        self.top_left = Point(x_min, y_max, 0)
    def approximate(self):
        '''make a boundary representation of a surface that approximates the points'''
        self.make_bounding_box()
        #make the bounding box into a polygon
        poly = BRepBuilderAPI_MakePolygon()
        map(poly.Add, [self.bottom_left, self.bottom_right, self.top_left, self.top_right])
        poly.Build()
        self.polygons = [poly]
        my_surf = build_plate(self.polygons, self.points)
        self.shape = my_surf.Shape()
        display.DisplayShape(self.shape)

app = Surface()

def make_point(vertex):
    #print "make_point: vertex=", vertex
    if isinstance(vertex, TopoDS_Vertex):
        #first check to see if it points anywhere
        location = vertex.Location()
        if location.IsDifferent(location.__class__()) == 1:
            print "making a gp_Pnt"
            pnt = BRep_Tool().Pnt(vertex)
            print "making a point"
            return Point(pnt)
        else:
            print "vertex wasn't anything special"
            return Point(0,0,0) #but it might not actually be 0,0,0
    elif isinstance(vertex, Point):
        return vertex
    elif isinstance(vertex, gp_Pnt):
        return Point(vertex)
    else: return vertex

def load_stl(filename):
    '''load_stl(filename) -> TopoDS_Shape'''
    importer = STLImporter(filename)
    importer.ReadFile()
    shape = importer.GetShape()
    return shape

def extract_shape_vertices(shape):
    '''dumps a list of points that define the shape, not including edges'''
    wires = []
    points = []
    wires.extend(process_face(shape))
    for wire in wires:
        points.extend(process_wire(wire))
    return points

def shape_vertices(shape):
    '''OO way of figuring out shape vertices'''
    shape = Shape(shape)
    return shape.points
    #wires, points = [], []
    #wires.extend(shape.wires)
    #for wire in wires:
    #    points.extend(wire.points)
    #return points

def process_shape(shape):
    '''extracts faces from a shape
    note: if this doesnt work you should try process_face(shape)
    returns a list of faces'''
    faces = []
    explorer = TopExp_Explorer(shape, TopAbs_FACE)
    while explorer.More():
        face = TopoDS().Face(explorer.Current())
        faces.append(face)
        explorer.Next()
    return faces

def process_face(face):
    '''traverses a face for wires
    returns a list of wires'''
    wires = []
    explorer = TopExp_Explorer(face, TopAbs_EDGE)
    while explorer.More():
        edge = TopoDS().Edge(explorer.Current())
        wire = BRepBuilderAPI_MakeWire(edge).Wire()
        wires.append(wire)
        explorer.Next()
    return wires

def process_edge(edge):
    '''returns a list of points on the edge'''
    wire = BRepBuilderAPI_MakeWire(edge).Wire()
    return process_wire(wire)

def process_wire(wire):
    '''takes a wire and extracts points
    returns a list of points'''
    vertices = []
    explorer = BRepTools_WireExplorer(wire)
    while explorer.More():
        vertex = TopoDS().Vertex(explorer.CurrentVertex())
        xyz = Point(BRep_Tool().Pnt(vertex))
        vertices.append(xyz)
        explorer.Next()
    return vertices

def demo(event=None):
    global app
    app.approximate()

def clear(event=None):
    global app
    app = Surface() #start over again
    display.EraseAll()

def ask_user(event=None):
    global app
    x = raw_input("x?")
    y = raw_input("y?")
    z = raw_input("z?")
    app.points.append(Point(x,y,z))

def exit_app(event=None):
    sys.exit()

add_key("c", clear)
add_key("a", ask_user)
add_key("d", demo)

pts = [Point(0,0,0), Point(0,0,1), Point(0,1,0), Point(0,1,1), Point(1,0,0), Point(1,1,0), Point(1,0,1), Point(1,1,1)]
class TestApproximation(unittest.TestCase):
    def test_point_list(self):
        tcolgp_array = point_list_1(pts)
        self.assertTrue(isinstance(tcolgp_array, TColgp_Array1OfPnt))
        tcolgp_array = point_list_2(pts)
        self.assertTrue(isinstance(tcolgp_array, TColgp_Array2OfPnt))
    def test_approximate(self):
        #my_surface = approximate_surface(pts)
        p1,p2,p3,p4,p5 = Point(0,0,0),Point(0,10,0),Point(0,10,10),Point(0,0,10),Point(5,5,5)
        poly = BRepBuilderAPI_MakePolygon()
        map(poly.Add, [p1,p2,p3,p4,p1])
        poly.Build()
        
        my_surf = build_plate([poly], [Point(-1,-1,-1)])
        sh = my_surf.Shape()
        display.DisplayShape(sh)
    @staticmethod
    def test_stl(self, event=None):
        #you probably dont have these
        #occ_shape = load_stl("~/local/pythonocc-0.3/pythonOCC/src/samples/Level2/DataExchange/sample.stl")
        #occ_shape = load_stl("~/local/legos/diver.stl") #http://adl.serveftp.org/lab/legos/diver.stl
        #occ_shape = load_stl(os.path.join(os.path.curdir, "models/cube.stl"))
        
        #make a box
        occ_shape = BRepPrimAPI_MakeBox(Point(0,0,0), Point(1,1,1)).Shape()
        display.DisplayShape(occ_shape)
        
        shape = Face(occ_shape)
        temp_points = list(set(shape.points))
        #self.assertTrue(len(temp_points)>0)

        #TODO: cluster points and make surfaces. but how do you compute the first parameter to build_plate?
        surf = Surface()
        surf.shape = occ_shape
        surf.points = temp_points
        #surf.approximate() #should be more like test_extract_shape_vertices
        curve1 = GeomAPI_PointsToBSpline(point_list_1(surf.points)).Curve()
        curve1.GetObject()
        e1 = Edge(curve1.GetHandle())
        display.DisplayShape(e1)
        #face1 = make_face(surf1.GetHandle())
        #display.DisplayShape(face1)
    def test_extract_shape_vertices(self):
        clear()
        occ_shape = BRepPrimAPI_MakeBox(Point(0,0,0), Point(1,1,1)).Shape()
        display.DisplayShape(occ_shape)
        temp_points = extract_shape_vertices(occ_shape)
        self.assertTrue(len(temp_points)>0)
        surf = Surface()
        surf.shape = occ_shape
        surf.points = temp_points
        surf.approximate()

if __name__ == "__main__":
    #unittest.main()
    #exit()
    from OCC.Display.wxSamplesGui import add_function_to_menu, add_menu, start_display
    add_menu("demo")
    add_function_to_menu("demo", TestApproximation.test_stl)
    add_function_to_menu("demo", demo)
    add_function_to_menu("demo", clear)
    add_function_to_menu("demo", ask_user)
    add_function_to_menu("demo", exit_app)
    init_display()
    start_display()