1
- from typing import Tuple , Union , Any , Callable , List , Optional
1
+ from typing import Tuple , Union , Any , Callable , List , Optional , Dict
2
2
from nptyping import NDArray as Array
3
3
4
4
from numpy import array , eye , zeros , pi
5
- from scipy . optimize import minimize
5
+ import nlopt
6
6
7
- from OCP .gp import gp_Vec , gp_Pln , gp_Lin , gp_Dir , gp_Pnt , gp_Trsf , gp_Quaternion
7
+ from OCP .gp import gp_Vec , gp_Pln , gp_Dir , gp_Pnt , gp_Trsf , gp_Quaternion
8
8
9
9
from .geom import Location
10
10
15
15
]
16
16
17
17
NDOF = 6
18
- DIR_SCALING = 1e4
19
- DIFF_EPS = 1e-9
18
+ DIR_SCALING = 1e2
19
+ DIFF_EPS = 1e-10
20
+ TOL = 1e-12
21
+ MAXITER = 2000
20
22
21
23
22
24
class ConstraintSolver (object ):
@@ -99,9 +101,7 @@ def pt_cost(
99
101
100
102
val = 0 if val is None else val
101
103
102
- return (
103
- val - (m1 .Transformed (t1 ).XYZ () - m2 .Transformed (t2 ).XYZ ()).Modulus ()
104
- ) ** 2
104
+ return val - (m1 .Transformed (t1 ).XYZ () - m2 .Transformed (t2 ).XYZ ()).Modulus ()
105
105
106
106
def dir_cost (
107
107
m1 : gp_Dir ,
@@ -113,9 +113,7 @@ def dir_cost(
113
113
114
114
val = pi if val is None else val
115
115
116
- return (
117
- DIR_SCALING * (val - m1 .Transformed (t1 ).Angle (m2 .Transformed (t2 ))) ** 2
118
- )
116
+ return DIR_SCALING * (val - m1 .Transformed (t1 ).Angle (m2 .Transformed (t2 )))
119
117
120
118
def pnt_pln_cost (
121
119
m1 : gp_Pnt ,
@@ -130,7 +128,7 @@ def pnt_pln_cost(
130
128
m2_located = m2 .Transformed (t2 )
131
129
# offset in the plane's normal direction by val:
132
130
m2_located .Translate (gp_Vec (m2_located .Axis ().Direction ()).Multiplied (val ))
133
- return m2_located .SquareDistance (m1 .Transformed (t1 ))
131
+ return m2_located .Distance (m1 .Transformed (t1 ))
134
132
135
133
def f (x ):
136
134
"""
@@ -152,11 +150,11 @@ def f(x):
152
150
153
151
for m1 , m2 in zip (ms1 , ms2 ):
154
152
if isinstance (m1 , gp_Pnt ) and isinstance (m2 , gp_Pnt ):
155
- rv += pt_cost (m1 , m2 , t1 , t2 , d )
153
+ rv += pt_cost (m1 , m2 , t1 , t2 , d ) ** 2
156
154
elif isinstance (m1 , gp_Dir ):
157
- rv += dir_cost (m1 , m2 , t1 , t2 , d )
155
+ rv += dir_cost (m1 , m2 , t1 , t2 , d ) ** 2
158
156
elif isinstance (m1 , gp_Pnt ) and isinstance (m2 , gp_Pln ):
159
- rv += pnt_pln_cost (m1 , m2 , t1 , t2 , d )
157
+ rv += pnt_pln_cost (m1 , m2 , t1 , t2 , d ) ** 2
160
158
else :
161
159
raise NotImplementedError (f"{ m1 ,m2 } " )
162
160
@@ -196,11 +194,11 @@ def jac(x):
196
194
197
195
if k1 not in self .locked :
198
196
tmp1 = pt_cost (m1 , m2 , t1j , t2 , d )
199
- rv [k1 * NDOF + j ] += (tmp1 - tmp ) / DIFF_EPS
197
+ rv [k1 * NDOF + j ] += 2 * tmp * (tmp1 - tmp ) / DIFF_EPS
200
198
201
199
if k2 not in self .locked :
202
200
tmp2 = pt_cost (m1 , m2 , t1 , t2j , d )
203
- rv [k2 * NDOF + j ] += (tmp2 - tmp ) / DIFF_EPS
201
+ rv [k2 * NDOF + j ] += 2 * tmp * (tmp2 - tmp ) / DIFF_EPS
204
202
205
203
elif isinstance (m1 , gp_Dir ):
206
204
tmp = dir_cost (m1 , m2 , t1 , t2 , d )
@@ -212,11 +210,11 @@ def jac(x):
212
210
213
211
if k1 not in self .locked :
214
212
tmp1 = dir_cost (m1 , m2 , t1j , t2 , d )
215
- rv [k1 * NDOF + j ] += (tmp1 - tmp ) / DIFF_EPS
213
+ rv [k1 * NDOF + j ] += 2 * tmp * (tmp1 - tmp ) / DIFF_EPS
216
214
217
215
if k2 not in self .locked :
218
216
tmp2 = dir_cost (m1 , m2 , t1 , t2j , d )
219
- rv [k2 * NDOF + j ] += (tmp2 - tmp ) / DIFF_EPS
217
+ rv [k2 * NDOF + j ] += 2 * tmp * (tmp2 - tmp ) / DIFF_EPS
220
218
221
219
elif isinstance (m1 , gp_Pnt ) and isinstance (m2 , gp_Pln ):
222
220
tmp = pnt_pln_cost (m1 , m2 , t1 , t2 , d )
@@ -228,34 +226,48 @@ def jac(x):
228
226
229
227
if k1 not in self .locked :
230
228
tmp1 = pnt_pln_cost (m1 , m2 , t1j , t2 , d )
231
- rv [k1 * NDOF + j ] += (tmp1 - tmp ) / DIFF_EPS
229
+ rv [k1 * NDOF + j ] += 2 * tmp * (tmp1 - tmp ) / DIFF_EPS
232
230
233
231
if k2 not in self .locked :
234
232
tmp2 = pnt_pln_cost (m1 , m2 , t1 , t2j , d )
235
- rv [k2 * NDOF + j ] += (tmp2 - tmp ) / DIFF_EPS
233
+ rv [k2 * NDOF + j ] += 2 * tmp * (tmp2 - tmp ) / DIFF_EPS
236
234
else :
237
235
raise NotImplementedError (f"{ m1 ,m2 } " )
238
236
239
237
return rv
240
238
241
239
return f , jac
242
240
243
- def solve (self ) -> List [Location ]:
241
+ def solve (self ) -> Tuple [ List [Location ], Dict [ str , Any ] ]:
244
242
245
243
x0 = array ([el for el in self .entities ]).ravel ()
246
244
f , jac = self ._cost ()
247
245
248
- res = minimize (
249
- f ,
250
- x0 ,
251
- jac = jac ,
252
- method = "BFGS" ,
253
- options = dict (disp = True , gtol = 1e-12 , maxiter = 1000 ),
254
- )
246
+ def func (x , grad ):
247
+
248
+ if grad .size > 0 :
249
+ grad [:] = jac (x )
250
+
251
+ return f (x )
252
+
253
+ opt = nlopt .opt (nlopt .LD_CCSAQ , len (x0 ))
254
+ opt .set_min_objective (func )
255
+
256
+ opt .set_ftol_abs (0 )
257
+ opt .set_ftol_rel (0 )
258
+ opt .set_xtol_rel (TOL )
259
+ opt .set_xtol_abs (0 )
260
+ opt .set_maxeval (MAXITER )
255
261
256
- x = res .x
262
+ x = opt .optimize (x0 )
263
+ result = {
264
+ "cost" : opt .last_optimum_value (),
265
+ "iters" : opt .get_numevals (),
266
+ "status" : opt .last_optimize_result (),
267
+ }
257
268
258
- return [
269
+ locs = [
259
270
Location (self ._build_transform (* x [NDOF * i : NDOF * (i + 1 )]))
260
271
for i in range (self .ne )
261
272
]
273
+ return locs , result
0 commit comments