|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +from typing import List |
| 4 | + |
| 5 | +import numpy as np |
| 6 | +from mmcore.geom.nurbs import NURBSSurface, NURBSCurve, find_span, basis_functions |
| 7 | + |
| 8 | + |
| 9 | +def extract_surface_boundaries(surface: NURBSSurface) -> List[NURBSCurve]: |
| 10 | + """ |
| 11 | + Extract the four boundary curves of a NURBS surface. |
| 12 | +
|
| 13 | + Args: |
| 14 | + surface (NURBSSurface): The input NURBS surface |
| 15 | +
|
| 16 | + Returns: |
| 17 | + List[NURBSCurve]: List of four boundary curves in order: |
| 18 | + [u=0 curve, u=1 curve, v=0 curve, v=1 curve] |
| 19 | + """ |
| 20 | + (u_min, u_max), (v_min, v_max) = surface.interval() |
| 21 | + |
| 22 | + # Extract iso-curves at the boundaries |
| 23 | + |
| 24 | + |
| 25 | + u0_curve = extract_isocurve(surface, u_min, 'u') # v-direction curve at u=0 |
| 26 | + u1_curve = extract_isocurve(surface, u_max, 'u') # v-direction curve at u=1 |
| 27 | + v0_curve = extract_isocurve(surface, v_min, 'v') # u-direction curve at v=0 |
| 28 | + v1_curve = extract_isocurve(surface, v_max, 'v') # u-direction curve at v=1 |
| 29 | + |
| 30 | + return [u0_curve, u1_curve, v0_curve, v1_curve] |
| 31 | + |
| 32 | + |
| 33 | +def extract_isocurve( |
| 34 | + surface: NURBSSurface, param: float, direction: str = "u" |
| 35 | +) -> NURBSCurve: |
| 36 | + """ |
| 37 | + Extract an isocurve from a NURBS surface at a given parameter in the u or v direction. |
| 38 | + Args: |
| 39 | + surface (NURBSSurface): The input NURBS surface. |
| 40 | + param (float): The parameter value at which to extract the isocurve. |
| 41 | + direction (str): The direction of the isocurve, either 'u' or 'v'. Default is 'u'. |
| 42 | + Returns: |
| 43 | + NURBSCurve: The extracted isocurve as a NURBS curve. |
| 44 | + Raises: |
| 45 | + ValueError: If the direction is not 'u' or 'v', or if the param is out of range. |
| 46 | + """ |
| 47 | + if direction not in ["u", "v"]: |
| 48 | + raise ValueError("Direction must be either 'u' or 'v'.") |
| 49 | + interval = surface.interval() |
| 50 | + #print('ij', surface.knots_u, surface.knots_v, surface.interval()) |
| 51 | + if direction == "u": |
| 52 | + # For u-direction: we fix u and vary v |
| 53 | + # First check if the u parameter is in range |
| 54 | + param_range = interval[0] # u range |
| 55 | + if param < param_range[0] or param > param_range[1]: |
| 56 | + raise ValueError(f"Parameter {param} is out of range {param_range}") |
| 57 | + |
| 58 | + # Find the span and basis functions in u direction (the direction we're fixing) |
| 59 | + n_u = surface.shape[0] - 1 # number of control points in u direction - 1 |
| 60 | + degree_u = surface.degree[0] |
| 61 | + span = find_span(n_u, degree_u, param, surface.knots_u, 0) |
| 62 | + basis = basis_functions(span, param, degree_u, surface.knots_u) |
| 63 | + |
| 64 | + # The resulting curve will have as many control points as the surface has in v direction |
| 65 | + m = surface.shape[1] |
| 66 | + control_points = np.zeros((m, 4)) |
| 67 | + |
| 68 | + # Compute control points for the extracted curve |
| 69 | + for i in range(m): # iterate over v direction |
| 70 | + for j in range(degree_u + 1): # combine with basis functions |
| 71 | + control_points[i] += basis[j] * surface.control_points_w[span - degree_u + j, i] |
| 72 | + |
| 73 | + # Return curve with v-direction degree and knots since we're varying in v |
| 74 | + |
| 75 | + cc=NURBSCurve(control_points, degree=surface.degree[1],knots=surface.knots_v) |
| 76 | + #cc.knots=surface.knots_v |
| 77 | + |
| 78 | + #print('j', cc.knots,cc.interval()) |
| 79 | + return cc |
| 80 | + |
| 81 | + else: # direction == 'v' |
| 82 | + # For v-direction: we fix v and vary u |
| 83 | + # First check if the v parameter is in range |
| 84 | + param_range = interval[1] # v range |
| 85 | + if param < param_range[0] or param > param_range[1]: |
| 86 | + raise ValueError(f"Parameter {param} is out of range {param_range}") |
| 87 | + |
| 88 | + # Find the span and basis functions in v direction (the direction we're fixing) |
| 89 | + n_v = surface.shape[1] - 1 # number of control points in v direction - 1 |
| 90 | + degree_v = surface.degree[1] |
| 91 | + span = find_span(n_v, degree_v, param, surface.knots_v, 0) |
| 92 | + basis = basis_functions(span, param, degree_v, surface.knots_v) |
| 93 | + |
| 94 | + # The resulting curve will have as many control points as the surface has in u direction |
| 95 | + m = surface.shape[0] |
| 96 | + control_points = np.zeros((m, 4)) |
| 97 | + |
| 98 | + # Compute control points for the extracted curve |
| 99 | + for i in range(m): # iterate over u direction |
| 100 | + for j in range(degree_v + 1): # combine with basis functions |
| 101 | + control_points[i] += basis[j] * surface.control_points_w[i, span - degree_v + j] |
| 102 | + cc = NURBSCurve(control_points, surface.degree[0],surface.knots_u) |
| 103 | + #print('i',cc.knots,cc.interval()) |
| 104 | + #cc.knots = surface.knots_u |
| 105 | + # Return curve with u-direction degree and knots since we're varying in u |
| 106 | + return cc |
0 commit comments