Coverage for geometry/vertices.py: 74%

78 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 22:49 +1300

1""" 

2Geometry Primitive Vertices 

3=========================== 

4 

5Define methods for generating vertices of fundamental geometric primitives 

6used in colour science visualisations and computations. 

7 

8- :func:`colour.geometry.primitive_vertices_quad_mpl` 

9- :func:`colour.geometry.primitive_vertices_grid_mpl` 

10- :func:`colour.geometry.primitive_vertices_cube_mpl` 

11- :func:`colour.geometry.primitive_vertices_sphere` 

12- :func:`colour.PRIMITIVE_VERTICES_METHODS` 

13- :func:`colour.primitive_vertices` 

14""" 

15 

16from __future__ import annotations 

17 

18import typing 

19 

20import numpy as np 

21 

22from colour.algebra import spherical_to_cartesian 

23from colour.geometry import MAPPING_PLANE_TO_AXIS 

24 

25if typing.TYPE_CHECKING: 

26 from colour.hints import Any, ArrayLike, Literal, NDArrayFloat, Sequence 

27 

28from colour.utilities import ( 

29 CanonicalMapping, 

30 as_float_array, 

31 filter_kwargs, 

32 full, 

33 ones, 

34 tsplit, 

35 tstack, 

36 validate_method, 

37 zeros, 

38) 

39 

40__author__ = "Colour Developers" 

41__copyright__ = "Copyright 2013 Colour Developers" 

42__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

43__maintainer__ = "Colour Developers" 

44__email__ = "colour-developers@colour-science.org" 

45__status__ = "Production" 

46 

47__all__ = [ 

48 "primitive_vertices_quad_mpl", 

49 "primitive_vertices_grid_mpl", 

50 "primitive_vertices_cube_mpl", 

51 "primitive_vertices_sphere", 

52 "PRIMITIVE_VERTICES_METHODS", 

53 "primitive_vertices", 

54] 

55 

56 

57def primitive_vertices_quad_mpl( 

58 width: float = 1, 

59 height: float = 1, 

60 depth: float = 0, 

61 origin: ArrayLike = (0, 0), 

62 axis: Literal["+z", "+x", "+y", "yz", "xz", "xy"] | str = "+z", 

63) -> NDArrayFloat: 

64 """ 

65 Generate vertices of a quad primitive for use with *Matplotlib* 

66 :class:`mpl_toolkits.mplot3d.art3d.Poly3DCollection` class. 

67 

68 Parameters 

69 ---------- 

70 width 

71 Width of the primitive. 

72 height 

73 Height of the primitive. 

74 depth 

75 Depth of the primitive. 

76 origin 

77 Origin of the primitive on the construction plane. 

78 axis 

79 Axis to which the primitive will be normal, or plane with which the 

80 primitive will be co-planar. 

81 

82 Returns 

83 ------- 

84 :class:`numpy.ndarray` 

85 Quad primitive vertices. 

86 

87 Examples 

88 -------- 

89 >>> primitive_vertices_quad_mpl() 

90 array([[ 0., 0., 0.], 

91 [ 1., 0., 0.], 

92 [ 1., 1., 0.], 

93 [ 0., 1., 0.]]) 

94 """ 

95 

96 axis = MAPPING_PLANE_TO_AXIS.get(axis, axis).lower() 

97 axis = validate_method( 

98 axis, ("+x", "+y", "+z"), '"{0}" axis invalid, it must be one of {1}!' 

99 ) 

100 

101 u, v = tsplit(origin) 

102 

103 if axis == "+z": 

104 vertices = [ 

105 (u, v, depth), 

106 (u + width, v, depth), 

107 (u + width, v + height, depth), 

108 (u, v + height, depth), 

109 ] 

110 elif axis == "+y": 

111 vertices = [ 

112 (u, depth, v), 

113 (u + width, depth, v), 

114 (u + width, depth, v + height), 

115 (u, depth, v + height), 

116 ] 

117 elif axis == "+x": 

118 vertices = [ 

119 (depth, u, v), 

120 (depth, u + width, v), 

121 (depth, u + width, v + height), 

122 (depth, u, v + height), 

123 ] 

124 

125 return as_float_array(vertices) 

126 

127 

128def primitive_vertices_grid_mpl( 

129 width: float = 1, 

130 height: float = 1, 

131 depth: float = 0, 

132 width_segments: int = 1, 

133 height_segments: int = 1, 

134 origin: ArrayLike = (0, 0), 

135 axis: Literal["+z", "+x", "+y", "yz", "xz", "xy"] | str = "+z", 

136) -> NDArrayFloat: 

137 """ 

138 Generate vertices for a grid primitive composed of quadrilateral 

139 primitives for use with *Matplotlib* 

140 :class:`mpl_toolkits.mplot3d.art3d.Poly3DCollection` class. 

141 

142 Parameters 

143 ---------- 

144 width 

145 Width of the primitive. 

146 height 

147 Height of the primitive. 

148 depth 

149 Depth of the primitive. 

150 width_segments: 

151 Number of width segments defining quad primitive counts along 

152 the width axis. 

153 height_segments: 

154 Number of height segments defining quad primitive counts along 

155 the height axis. 

156 origin 

157 Origin of the primitive on the construction plane. 

158 axis 

159 Axis to which the primitive will be normal, or plane with which the 

160 primitive will be co-planar. 

161 

162 Returns 

163 ------- 

164 :class:`numpy.ndarray` 

165 Grid primitive vertices. 

166 

167 Examples 

168 -------- 

169 >>> primitive_vertices_grid_mpl(width_segments=2, height_segments=2) 

170 array([[[ 0. , 0. , 0. ], 

171 [ 0.5, 0. , 0. ], 

172 [ 0.5, 0.5, 0. ], 

173 [ 0. , 0.5, 0. ]], 

174 <BLANKLINE> 

175 [[ 0. , 0.5, 0. ], 

176 [ 0.5, 0.5, 0. ], 

177 [ 0.5, 1. , 0. ], 

178 [ 0. , 1. , 0. ]], 

179 <BLANKLINE> 

180 [[ 0.5, 0. , 0. ], 

181 [ 1. , 0. , 0. ], 

182 [ 1. , 0.5, 0. ], 

183 [ 0.5, 0.5, 0. ]], 

184 <BLANKLINE> 

185 [[ 0.5, 0.5, 0. ], 

186 [ 1. , 0.5, 0. ], 

187 [ 1. , 1. , 0. ], 

188 [ 0.5, 1. , 0. ]]]) 

189 """ 

190 

191 u, v = tsplit(origin) 

192 

193 w_x, h_y = width / width_segments, height / height_segments 

194 

195 quads = [] 

196 for i in range(width_segments): 

197 for j in range(height_segments): 

198 quads.append( # noqa: PERF401 

199 primitive_vertices_quad_mpl( 

200 w_x, h_y, depth, (i * w_x + u, j * h_y + v), axis 

201 ) 

202 ) 

203 

204 return as_float_array(quads) 

205 

206 

207def primitive_vertices_cube_mpl( 

208 width: float = 1, 

209 height: float = 1, 

210 depth: float = 1, 

211 width_segments: int = 1, 

212 height_segments: int = 1, 

213 depth_segments: int = 1, 

214 origin: ArrayLike = (0, 0, 0), 

215 planes: ( 

216 Sequence[ 

217 Literal[ 

218 "-x", 

219 "+x", 

220 "-y", 

221 "+y", 

222 "-z", 

223 "+z", 

224 "xy", 

225 "xz", 

226 "yz", 

227 "yx", 

228 "zx", 

229 "zy", 

230 ] 

231 ] 

232 | None 

233 ) = None, 

234) -> NDArrayFloat: 

235 """ 

236 Generate vertices of a cube primitive made of grid primitives for 

237 use with *Matplotlib* :class:`mpl_toolkits.mplot3d.art3d.Poly3DCollection` 

238 class. 

239 

240 Parameters 

241 ---------- 

242 width 

243 Width of the primitive. 

244 height 

245 Height of the primitive. 

246 depth 

247 Depth of the primitive. 

248 width_segments 

249 Number of width segments defining quad primitive counts along 

250 the width axis. 

251 height_segments 

252 Number of height segments defining quad primitive counts along 

253 the height axis. 

254 depth_segments 

255 Number of depth segments defining quad primitive counts along 

256 the depth axis. 

257 origin 

258 Origin of the primitive. 

259 planes 

260 Grid primitives to include in the cube construction. 

261 

262 Returns 

263 ------- 

264 :class:`numpy.ndarray` 

265 Cube primitive vertices. 

266 

267 Examples 

268 -------- 

269 >>> primitive_vertices_cube_mpl() 

270 array([[[ 0., 0., 0.], 

271 [ 1., 0., 0.], 

272 [ 1., 1., 0.], 

273 [ 0., 1., 0.]], 

274 <BLANKLINE> 

275 [[ 0., 0., 1.], 

276 [ 1., 0., 1.], 

277 [ 1., 1., 1.], 

278 [ 0., 1., 1.]], 

279 <BLANKLINE> 

280 [[ 0., 0., 0.], 

281 [ 1., 0., 0.], 

282 [ 1., 0., 1.], 

283 [ 0., 0., 1.]], 

284 <BLANKLINE> 

285 [[ 0., 1., 0.], 

286 [ 1., 1., 0.], 

287 [ 1., 1., 1.], 

288 [ 0., 1., 1.]], 

289 <BLANKLINE> 

290 [[ 0., 0., 0.], 

291 [ 0., 1., 0.], 

292 [ 0., 1., 1.], 

293 [ 0., 0., 1.]], 

294 <BLANKLINE> 

295 [[ 1., 0., 0.], 

296 [ 1., 1., 0.], 

297 [ 1., 1., 1.], 

298 [ 1., 0., 1.]]]) 

299 """ 

300 

301 axis = ( 

302 sorted(MAPPING_PLANE_TO_AXIS.values()) 

303 if planes is None 

304 else [MAPPING_PLANE_TO_AXIS.get(plane, plane).lower() for plane in planes] 

305 ) 

306 

307 u, v, w = tsplit(origin) 

308 

309 w_s, h_s, d_s = width_segments, height_segments, depth_segments 

310 

311 grids: list = [] 

312 if "-z" in axis: 

313 grids.extend( 

314 primitive_vertices_grid_mpl(width, depth, v, w_s, d_s, (u, w), "+z") 

315 ) 

316 if "+z" in axis: 

317 grids.extend( 

318 primitive_vertices_grid_mpl( 

319 width, depth, v + height, w_s, d_s, (u, w), "+z" 

320 ) 

321 ) 

322 

323 if "-y" in axis: 

324 grids.extend( 

325 primitive_vertices_grid_mpl(width, height, w, w_s, h_s, (u, v), "+y") 

326 ) 

327 if "+y" in axis: 

328 grids.extend( 

329 primitive_vertices_grid_mpl( 

330 width, height, w + depth, w_s, h_s, (u, v), "+y" 

331 ) 

332 ) 

333 

334 if "-x" in axis: 

335 grids.extend( 

336 primitive_vertices_grid_mpl(depth, height, u, d_s, h_s, (w, v), "+x") 

337 ) 

338 if "+x" in axis: 

339 grids.extend( 

340 primitive_vertices_grid_mpl( 

341 depth, height, u + width, d_s, h_s, (w, v), "+x" 

342 ) 

343 ) 

344 

345 return as_float_array(grids) 

346 

347 

348def primitive_vertices_sphere( 

349 radius: float = 0.5, 

350 segments: int = 8, 

351 intermediate: bool = False, 

352 origin: ArrayLike = (0, 0, 0), 

353 axis: Literal["+z", "+x", "+y", "yz", "xz", "xy"] | str = "+z", 

354) -> NDArrayFloat: 

355 """ 

356 Generate vertices of a latitude-longitude sphere primitive. 

357 

358 Parameters 

359 ---------- 

360 radius 

361 Radius of the sphere. 

362 segments 

363 Number of latitude-longitude segments. If the ``intermediate`` 

364 argument is *True*, the sphere will have one fewer segment 

365 along its longitude. 

366 intermediate 

367 Whether to generate sphere vertices at the centres of the faces 

368 outlined by the segments of a regular sphere generated without 

369 the ``intermediate`` argument set to *True*. The resulting 

370 sphere is inscribed within the regular sphere faces while 

371 maintaining identical poles. 

372 origin 

373 Origin of the primitive on the construction plane. 

374 axis 

375 Axis to which the primitive will be normal, or plane with which the 

376 primitive will be co-planar. 

377 

378 Returns 

379 ------- 

380 :class:`numpy.ndarray` 

381 Sphere primitive vertices. 

382 

383 Notes 

384 ----- 

385 - The sphere poles have latitude segments count - 1 co-located vertices. 

386 

387 Examples 

388 -------- 

389 >>> primitive_vertices_sphere(segments=4) # doctest: +ELLIPSIS 

390 array([[[ 0.0000000...e+00, 0.0000000...e+00, 5.0000000...e-01], 

391 [ -3.5355339...e-01, -4.3297802...e-17, 3.5355339...e-01], 

392 [ -5.0000000...e-01, -6.1232340...e-17, 3.0616170...e-17], 

393 [ -3.5355339...e-01, -4.3297802...e-17, -3.5355339...e-01], 

394 [ -6.1232340...e-17, -7.4987989...e-33, -5.0000000...e-01]], 

395 <BLANKLINE> 

396 [[ 0.0000000...e+00, 0.0000000...e+00, 5.0000000...e-01], 

397 [ 2.1648901...e-17, -3.5355339...e-01, 3.5355339...e-01], 

398 [ 3.0616170...e-17, -5.0000000...e-01, 3.0616170...e-17], 

399 [ 2.1648901...e-17, -3.5355339...e-01, -3.5355339...e-01], 

400 [ 3.7493994...e-33, -6.1232340...e-17, -5.0000000...e-01]], 

401 <BLANKLINE> 

402 [[ 0.0000000...e+00, 0.0000000...e+00, 5.0000000...e-01], 

403 [ 3.5355339...e-01, 0.0000000...e+00, 3.5355339...e-01], 

404 [ 5.0000000...e-01, 0.0000000...e+00, 3.0616170...e-17], 

405 [ 3.5355339...e-01, 0.0000000...e+00, -3.5355339...e-01], 

406 [ 6.1232340...e-17, 0.0000000...e+00, -5.0000000...e-01]], 

407 <BLANKLINE> 

408 [[ 0.0000000...e+00, 0.0000000...e+00, 5.0000000...e-01], 

409 [ 2.1648901...e-17, 3.5355339...e-01, 3.5355339...e-01], 

410 [ 3.0616170...e-17, 5.0000000...e-01, 3.0616170...e-17], 

411 [ 2.1648901...e-17, 3.5355339...e-01, -3.5355339...e-01], 

412 [ 3.7493994...e-33, 6.1232340...e-17, -5.0000000...e-01]]]) 

413 """ 

414 

415 origin = as_float_array(origin) 

416 

417 axis = MAPPING_PLANE_TO_AXIS.get(axis, axis).lower() 

418 axis = validate_method( 

419 axis, ("+x", "+y", "+z"), '"{0}" axis invalid, it must be one of {1}!' 

420 ) 

421 

422 if not intermediate: 

423 theta = np.tile( 

424 np.radians(np.linspace(0, 180, segments + 1)), 

425 (int(segments) + 1, 1), 

426 ) 

427 phi = np.transpose( 

428 np.tile( 

429 np.radians(np.linspace(-180, 180, segments + 1)), 

430 (int(segments) + 1, 1), 

431 ) 

432 ) 

433 else: 

434 theta = np.tile( 

435 np.radians(np.linspace(0, 180, segments * 2 + 1)[1::2][1:-1]), 

436 (int(segments) + 1, 1), 

437 ) 

438 theta = np.hstack( 

439 [ 

440 zeros((segments + 1, 1)), 

441 theta, 

442 full((segments + 1, 1), np.pi), 

443 ] 

444 ) 

445 phi = np.transpose( 

446 np.tile( 

447 np.radians(np.linspace(-180, 180, segments + 1)) 

448 + np.radians(360 / segments / 2), 

449 (int(segments), 1), 

450 ) 

451 ) 

452 

453 rho = ones(phi.shape) * radius 

454 rho_theta_phi = tstack([rho, theta, phi]) 

455 

456 vertices = spherical_to_cartesian(rho_theta_phi) 

457 

458 # Removing extra longitude vertices. 

459 vertices = vertices[:-1, :, :] 

460 

461 if axis == "+z": 

462 pass 

463 elif axis == "+y": 

464 vertices = np.roll(vertices, 2, -1) 

465 elif axis == "+x": 

466 vertices = np.roll(vertices, 1, -1) 

467 

468 vertices += origin 

469 

470 return vertices 

471 

472 

473PRIMITIVE_VERTICES_METHODS: CanonicalMapping = CanonicalMapping( 

474 { 

475 "Quad MPL": primitive_vertices_quad_mpl, 

476 "Grid MPL": primitive_vertices_grid_mpl, 

477 "Cube MPL": primitive_vertices_cube_mpl, 

478 "Sphere": primitive_vertices_sphere, 

479 } 

480) 

481PRIMITIVE_VERTICES_METHODS.__doc__ = """ 

482Supported methods for generating vertices of geometry primitives. 

483""" 

484 

485 

486def primitive_vertices( 

487 method: (Literal["Cube MPL", "Quad MPL", "Grid MPL", "Sphere"] | str) = "Cube MPL", 

488 **kwargs: Any, 

489) -> NDArrayFloat: 

490 """ 

491 Generate vertices of a geometry primitive. 

492 

493 Parameters 

494 ---------- 

495 method 

496 Method for generating primitive vertices. 

497 

498 Other Parameters 

499 ---------------- 

500 axis 

501 {:func:`colour.geometry.primitive_vertices_quad_mpl`, 

502 :func:`colour.geometry.primitive_vertices_grid_mpl`, 

503 :func:`colour.geometry.primitive_vertices_sphere`}, 

504 **{'+z', '+x', '+y', 'yz', 'xz', 'xy'}**, 

505 Axis to which the primitive will be normal, or plane with which 

506 the primitive will be co-planar. 

507 depth 

508 {:func:`colour.geometry.primitive_vertices_quad_mpl`, 

509 :func:`colour.geometry.primitive_vertices_grid_mpl`, 

510 :func:`colour.geometry.primitive_vertices_cube_mpl`}, 

511 Depth of the primitive. 

512 depth_segments 

513 {:func:`colour.geometry.primitive_vertices_cube_mpl`}, 

514 Number of depth segments defining quad primitive counts along 

515 the depth axis. 

516 height 

517 {:func:`colour.geometry.primitive_vertices_quad_mpl`, 

518 :func:`colour.geometry.primitive_vertices_grid_mpl`, 

519 :func:`colour.geometry.primitive_vertices_cube_mpl`}, 

520 Height of the primitive. 

521 height_segments 

522 {:func:`colour.geometry.primitive_vertices_grid_mpl`, 

523 :func:`colour.geometry.primitive_vertices_cube_mpl`}, 

524 Number of height segments defining quad primitive counts along 

525 the height axis. 

526 intermediate 

527 {:func:`colour.geometry.primitive_vertices_sphere`}, 

528 Whether to generate sphere vertices at the centres of the faces 

529 outlined by the segments of a regular sphere generated without 

530 the ``intermediate`` argument set to *True*. The resulting 

531 sphere is inscribed within the regular sphere faces while 

532 maintaining identical poles. 

533 origin 

534 {:func:`colour.geometry.primitive_vertices_quad_mpl`, 

535 :func:`colour.geometry.primitive_vertices_grid_mpl`, 

536 :func:`colour.geometry.primitive_vertices_cube_mpl`, 

537 :func:`colour.geometry.primitive_vertices_sphere`}, 

538 Origin of the primitive on the construction plane. 

539 planes 

540 {:func:`colour.geometry.primitive_vertices_cube_mpl`}, 

541 **{'-x', '+x', '-y', '+y', '-z', '+z', 

542 'xy', 'xz', 'yz', 'yx', 'zx', 'zy'}**, 

543 Grid primitives to include in the cube construction. 

544 radius 

545 {:func:`colour.geometry.primitive_vertices_sphere`}, 

546 Radius of the sphere. 

547 segments 

548 {:func:`colour.geometry.primitive_vertices_sphere`}, 

549 Number of latitude-longitude segments. If the ``intermediate`` 

550 argument is *True*, the sphere will have one fewer segment 

551 along its longitude. 

552 width 

553 {:func:`colour.geometry.primitive_vertices_quad_mpl`, 

554 :func:`colour.geometry.primitive_vertices_grid_mpl`, 

555 :func:`colour.geometry.primitive_vertices_cube_mpl`}, 

556 Width of the primitive. 

557 width_segments 

558 {:func:`colour.geometry.primitive_vertices_grid_mpl`, 

559 :func:`colour.geometry.primitive_vertices_cube_mpl`}, 

560 Number of width segments defining quad primitive counts along 

561 the width axis. 

562 

563 Returns 

564 ------- 

565 :class:`numpy.ndarray` 

566 Vertices of the primitive. 

567 

568 Examples 

569 -------- 

570 >>> primitive_vertices() 

571 array([[[ 0., 0., 0.], 

572 [ 1., 0., 0.], 

573 [ 1., 1., 0.], 

574 [ 0., 1., 0.]], 

575 <BLANKLINE> 

576 [[ 0., 0., 1.], 

577 [ 1., 0., 1.], 

578 [ 1., 1., 1.], 

579 [ 0., 1., 1.]], 

580 <BLANKLINE> 

581 [[ 0., 0., 0.], 

582 [ 1., 0., 0.], 

583 [ 1., 0., 1.], 

584 [ 0., 0., 1.]], 

585 <BLANKLINE> 

586 [[ 0., 1., 0.], 

587 [ 1., 1., 0.], 

588 [ 1., 1., 1.], 

589 [ 0., 1., 1.]], 

590 <BLANKLINE> 

591 [[ 0., 0., 0.], 

592 [ 0., 1., 0.], 

593 [ 0., 1., 1.], 

594 [ 0., 0., 1.]], 

595 <BLANKLINE> 

596 [[ 1., 0., 0.], 

597 [ 1., 1., 0.], 

598 [ 1., 1., 1.], 

599 [ 1., 0., 1.]]]) 

600 >>> primitive_vertices("Quad MPL") 

601 array([[ 0., 0., 0.], 

602 [ 1., 0., 0.], 

603 [ 1., 1., 0.], 

604 [ 0., 1., 0.]]) 

605 >>> primitive_vertices("Sphere", segments=4) # doctest: +ELLIPSIS 

606 array([[[ 0.0000000...e+00, 0.0000000...e+00, 5.0000000...e-01], 

607 [ -3.5355339...e-01, -4.3297802...e-17, 3.5355339...e-01], 

608 [ -5.0000000...e-01, -6.1232340...e-17, 3.0616170...e-17], 

609 [ -3.5355339...e-01, -4.3297802...e-17, -3.5355339...e-01], 

610 [ -6.1232340...e-17, -7.4987989...e-33, -5.0000000...e-01]], 

611 <BLANKLINE> 

612 [[ 0.0000000...e+00, 0.0000000...e+00, 5.0000000...e-01], 

613 [ 2.1648901...e-17, -3.5355339...e-01, 3.5355339...e-01], 

614 [ 3.0616170...e-17, -5.0000000...e-01, 3.0616170...e-17], 

615 [ 2.1648901...e-17, -3.5355339...e-01, -3.5355339...e-01], 

616 [ 3.7493994...e-33, -6.1232340...e-17, -5.0000000...e-01]], 

617 <BLANKLINE> 

618 [[ 0.0000000...e+00, 0.0000000...e+00, 5.0000000...e-01], 

619 [ 3.5355339...e-01, 0.0000000...e+00, 3.5355339...e-01], 

620 [ 5.0000000...e-01, 0.0000000...e+00, 3.0616170...e-17], 

621 [ 3.5355339...e-01, 0.0000000...e+00, -3.5355339...e-01], 

622 [ 6.1232340...e-17, 0.0000000...e+00, -5.0000000...e-01]], 

623 <BLANKLINE> 

624 [[ 0.0000000...e+00, 0.0000000...e+00, 5.0000000...e-01], 

625 [ 2.1648901...e-17, 3.5355339...e-01, 3.5355339...e-01], 

626 [ 3.0616170...e-17, 5.0000000...e-01, 3.0616170...e-17], 

627 [ 2.1648901...e-17, 3.5355339...e-01, -3.5355339...e-01], 

628 [ 3.7493994...e-33, 6.1232340...e-17, -5.0000000...e-01]]]) 

629 """ 

630 

631 method = validate_method(method, tuple(PRIMITIVE_VERTICES_METHODS)) 

632 

633 function = PRIMITIVE_VERTICES_METHODS[method] 

634 

635 return function(**filter_kwargs(function, **kwargs))