def bezier_surface_to_chebyshev(bsurf): spline = bsurf.data.splines[0] mb = spline.point_count_u #order_u nb = spline.point_count_v #order_v if max(mb, nb) > 4: return [] B = numpy.zeros((mb, nb, 3)) for l, p in enumerate(spline.points): i = l % mb j = int((l - i) / mb) for k in range(3): B[i, j, k] = p.co[k] M = cheb.B2Cmatrix(mb) N = cheb.B2Cmatrix(nb) C = numpy.zeros((mb, nb, 3)) for dim in range(3): """ for i in range(mb): for j in range(nb): for k in range(nb): MB = 0.0 for l in range(mb): MB += M[i,l]*B[l,k,dim] C[i,j,dim] += MB*N[j,k] """ C[:, :, dim] = matmul(matmul(M, B[:, :, dim]), N.T) return C
def minimum_area_OBB(xy): """ returns (center, ranges, axes) """ # get convex hull hull = quickhull2d(xy) nh = len(hull) # handle special cases if nh < 1: return (numpy.zeros(2), numpy.zeros(2), numpy.eye(2)) elif nh == 1: return (xy[hull[0]], numpy.zeros(2), numpy.eye(2)) elif nh == 2: center = 0.5 * numpy.sum(xy[hull], axis=0) vec = xy[hull[1]] - xy[hull[0]] ranges = numpy.array([0.5 * numpy.hypot(vec[0], vec[1]), 0]) axes = rotation_matrix2d(-numpy.arctan2(vec[1], vec[0])) return (center, ranges, axes) xyh = xy[hull] area = 1e20 for i in range(nh): # i-th edge of the convex hull vec = xyh[(i + 1) % nh] - xyh[i] # apply rotation that makes that edge parallel to the x-axis rot = rotation_matrix2d(numpy.arctan2(vec[1], vec[0])) xyrot = matmul(rot, xyh.T).T # xy ranges of the rotated convex hull mn = numpy.amin(xyrot, axis=0) mx = numpy.amax(xyrot, axis=0) ranges_tmp = mx - mn area_tmp = ranges_tmp[0] * ranges_tmp[1] if area_tmp < area: area = area_tmp # inverse rotation rot = rot.T center = matvecprod(rot, 0.5 * (mn + mx)) if ranges_tmp[1] > ranges_tmp[0]: ranges = 0.5 * ranges_tmp[[1, 0]] axes = numpy.zeros((2, 2)) axes[:, 0] = rot[:, 1] axes[:, 1] = -rot[:, 0] else: ranges = 0.5 * ranges_tmp axes = rot return (center, ranges, axes)
def minimal_OBB(xy, critere='area', tol=0.0): """ returns (center, ranges, axes) """ # get convex hull hull = quickhull2d(xy) nh = len(hull) # handle special cases if nh < 1: return (numpy.zeros(2), numpy.zeros(2), numpy.eye(2)) elif nh == 1: return (xy[hull[0]], numpy.zeros(2), numpy.eye(2)) elif nh == 2: center = 0.5 * numpy.sum(xy[hull], axis=0) vec = xy[hull[1]] - xy[hull[0]] ranges = numpy.array([0.5 * numpy.hypot(vec[0], vec[1]), 0]) axes = rotation_matrix2d(-numpy.arctan2(vec[1], vec[0])) return (center, ranges, axes) xyh = xy[hull] val = 1e20 frac = 1.0 + tol rot = numpy.zeros((2, 2)) for i in range(nh): # i-th edge of the convex hull vec = xyh[(i + 1) % nh] - xyh[i] # apply rotation that makes that edge parallel to the x-axis if True: rot = rotation_matrix2d(numpy.arctan2(vec[1], vec[0])) else: rot[:, 0] = vec rot[:, 1] = [vec[1], -vec[0]] rot = rot / numpy.hypot(vec[0], vec[1]) xyrot = matmul(rot, xyh.T).T # xy ranges of the rotated convex hull mn = numpy.amin(xyrot, axis=0) mx = numpy.amax(xyrot, axis=0) ranges_tmp = mx - mn if critere == 'area': val_tmp = ranges_tmp[0] * ranges_tmp[1] elif critere == 'width': val_tmp = min(ranges_tmp) print('VAL_TMP =', val_tmp, ', VAL_TMP - VAL =', val_tmp - val) if val_tmp < val * frac: val = val_tmp # inverse rotation rot = rot.T center = matvecprod(rot, 0.5 * (mn + mx)) if ranges_tmp[1] > ranges_tmp[0]: ranges = 0.5 * ranges_tmp[[1, 0]] axes = numpy.zeros((2, 2)) axes[:, 0] = rot[:, 1] axes[:, 1] = -rot[:, 0] else: ranges = 0.5 * ranges_tmp axes = rot return (center, ranges, axes)
def minimum_width_OBB(xy): """ returns (center, ranges, axes) inspired by https://www.mathworks.com/matlabcentral/fileexchange/7844-geom2d, function 'orientedBox', by David Legland """ # get convex hull hull = quickhull2d(xy) nh = len(hull) # handle special cases if nh < 1: return (numpy.zeros(2), numpy.zeros(2), numpy.eye(2)) elif nh == 1: return (xy[hull[0]], numpy.zeros(2), numpy.eye(2)) elif nh == 2: center = 0.5 * numpy.sum(xy[hull], axis=0) vec = xy[hull[1]] - xy[hull[0]] ranges = numpy.array([0.5 * numpy.hypot(vec[0], vec[1]), 0]) axes = rotation_matrix2d(-numpy.arctan2(vec[1], vec[0])) return (center, ranges, axes) xyh = xy[hull] minWidth = 1e20 rotatedAngle = 0 minAngle = 0 # indices of vertices in extreme y directions indA = numpy.argmin(xyh[:, 1]) indB = numpy.argmax(xyh[:, 1]) caliperA = numpy.array([1, 0]) # Caliper A points along the positive x-axis caliperB = numpy.array([-1, 0]) # Caliper B points along the negative x-axis # Find the direction with minimum width (rotating caliper algorithm) while rotatedAngle < numpy.pi: # compute the direction vectors corresponding to each edge indA2 = (indA + 1) % nh vectorA = xyh[indA2] - xyh[indA] indB2 = (indB + 1) % nh vectorB = xyh[indB2] - xyh[indB] # Determine the angle between each caliper and the next adjacent edge in the convex hull angleA = angle_between_vectors_2d(caliperA, vectorA) angleB = angle_between_vectors_2d(caliperB, vectorB) if angleA < 0: angleA += 2 * numpy.pi if angleB < 0: angleB += 2 * numpy.pi # Determine the smallest of these angles angleIncrement = min(angleA, angleB) # Rotate the calipers by the smallest angle caliperA = rotate_2d_vector(caliperA, angleIncrement) caliperB = rotate_2d_vector(caliperB, angleIncrement) rotatedAngle += angleIncrement # compute current width, and update opposite vertex if angleA < angleB: width = abs(distance_point_line(xyh[indB], xyh[indA], xyh[indA2])) indA = (indA + 1) % nh else: width = abs(distance_point_line(xyh[indA], xyh[indB], xyh[indB2])) indB = (indB + 1) % nh # update minimum width and corresponding angle if needed if width < minWidth: minWidth = width minAngle = rotatedAngle # OBB axes axes = rotation_matrix2d(minAngle) # OBB extents and center xyr = matmul(axes, xyh.T).T mn = numpy.amin(xyr, axis=0) mx = numpy.amax(xyr, axis=0) center = matvecprod(axes, 0.5 * (mn + mx)) ranges = 0.5 * (mx - mn) return (center, ranges, axes)
def LL_patch_from_corners(arcs, center, mrg=0, construction_steps=False, critere='area'): m = len(arcs) # normalize corners onto unit sphere s = numpy.array([arc[0] - center for arc in arcs]) r = numpy.sqrt(numpy.sum(s**2, axis=1)) ravg = numpy.sum(r) / float(m) s = s / numpy.tile(r, (3, 1)).T # orthonormal basis R = complete_orthonormal_matrix(numpy.sum(s, axis=0), i=0).T # central projection onto plane tangent to unit sphere at point r1 = R[:,0] s_dot_r1 = s[:, 0] * R[0, 0] + s[:, 1] * R[1, 0] + s[:, 2] * R[2, 0] s_dot_r1 = numpy.sign(s_dot_r1) * numpy.maximum(1e-6, numpy.absolute(s_dot_r1)) inv_s_dot_r1 = 1. / s_dot_r1 p = s * numpy.tile(inv_s_dot_r1, (3, 1)).T # coordinates in local frame (r2, r3) ab = lib_linalg.matmul(p, R[:, 1:3]) if construction_steps: abverts = [(a, b, 0) for a, b in ab] obj = lbu.pydata_to_mesh(verts=abverts, faces=[], edges=[(i, (i + 1) % m) for i in range(m)], name='ab_points') obj.layers[3] = True obj.layers[0] = False # mimimum-area OBB ctr_ab, rng_ab, axes_ab = minimal_OBB(ab) if construction_steps: OBBverts = [((-1)**(i + 1), (-1)**(j + 1), 0) for j in range(2) for i in range(2)] OBBedges = [(0, 1), (1, 3), (3, 2), (2, 0)] obj = lbu.pydata_to_mesh(verts=OBBverts, faces=[], edges=OBBedges, name='ab_OBB') obj.layers[3] = True obj.layers[0] = False obj.scale = (rng_ab[0], rng_ab[1], 1) obj.location = (ctr_ab[0], ctr_ab[1], 0) obj.rotation_euler[2] = numpy.arctan2(axes_ab[1, 0], axes_ab[0, 0]) R[:, 1:3] = lib_linalg.matmul(R[:, 1:3], axes_ab) # xyz-coords in rotated frame s = numpy.empty((0, 3), dtype=float) for arc in arcs: s = numpy.vstack([ s, lib_linalg.matmul((arc - numpy.tile(center, (len(arc), 1))) / ravg, R) ]) n = len(s) if construction_steps: obj = lbu.pydata_to_mesh(verts=s, faces=[], edges=[(i, (i + 1) % n) for i in range(n)], name='s_points') obj.layers[2] = True obj.layers[0] = False obj.location = center obj.scale = ravg * numpy.ones(3) obj.rotation_euler = Matrix(R).to_euler() # spherical coords: longitude t(heta), latitude l(ambda) tl = numpy.zeros((n, 2)) tl[:, 0] = numpy.arctan2(s[:, 1], s[:, 0]) tl[:, 1] = numpy.arcsin(s[:, 2]) if construction_steps: tlverts = [(t / numpy.pi, l / numpy.pi, 0) for t, l in tl] obj = lbu.pydata_to_mesh(verts=tlverts, faces=[], edges=[(i, (i + 1) % n) for i in range(n)], name='tl_points') obj.layers[4] = True obj.layers[0] = False min_tl = numpy.amin(tl, axis=0) max_tl = numpy.amax(tl, axis=0) ctr_tl = 0.5 * (min_tl + max_tl) print('lambda_0/pi = ', ctr_tl[1] / numpy.pi) rng_tl = (1 + mrg) * 0.5 * (max_tl - min_tl) print('max |lambda|/pi = ', max(abs(ctr_tl[1] - rng_tl[1]), ctr_tl[1] + rng_tl[1]) / numpy.pi) if construction_steps: obj = lbu.pydata_to_mesh(verts=OBBverts, faces=[], edges=OBBedges, name='tl_OBB') obj.layers[4] = True obj.layers[0] = False obj.scale = (rng_tl[0] / numpy.pi, rng_tl[1] / numpy.pi, 1) obj.location = (ctr_tl[0] / numpy.pi, ctr_tl[1] / numpy.pi, 0) # uv-coords uv = (tl - numpy.tile(ctr_tl, (n, 1))) / numpy.tile(rng_tl, (n, 1)) return (ravg, R, ctr_tl, rng_tl, uv)
def LL_patch_from_arcs(planes, corners, center, nsample=10): m = len(planes) # normalize corners onto unit sphere if False: xyz_sample = corners else: xyz_sample = [] for i, (tng, occ) in enumerate(planes): ci = corners[i] cj = corners[(i - 1) % m] # r1 = cj - occ r2 = ci - occ r190d = numpy.cross(r1, tng) a = numpy.arctan2(r2.dot(r190d), r2.dot(r1)) % (2 * numpy.pi) mij = occ + r1 * numpy.cos(0.5 * a) + r190d * numpy.sin(0.5 * a) xyz_sample.append(cj) xyz_sample.append(mij) s = numpy.array([xyz - center for xyz in xyz_sample]) r = numpy.sqrt(numpy.sum(s**2, axis=1)) ravg = numpy.sum(r) / float(len(s)) s = s / numpy.tile(r, (3, 1)).T print('s = ') print(s) # orthonormal basis B = complete_orthonormal_matrix(numpy.sum(s, axis=0), i=0).T print('Btmp =') print(B) # central projection onto plane tangent to unit sphere at point r1 = B[:,0] s_dot_r1 = s[:, 0] * B[0, 0] + s[:, 1] * B[1, 0] + s[:, 2] * B[2, 0] s_dot_r1 = numpy.sign(s_dot_r1) * numpy.maximum(1e-6, numpy.absolute(s_dot_r1)) inv_s_dot_r1 = 1. / s_dot_r1 p = s * numpy.tile(inv_s_dot_r1, (3, 1)).T print('p =') print(p) # coordinates in local frame (r2, r3) ab = lib_linalg.matmul(p, B[:, 1:3]) print('p_2d =') print(ab) # mimimum-area OBB if True: ctr_ab, rng_ab, axes_ab = minimal_OBB(ab) else: ctr_ab, rng_ab, axes_ab = covariance_ellipse(ab) print('ctr_ab =') print(ctr_ab) print('rng_ab =') print(rng_ab) print('area =') print(rng_ab[0] * rng_ab[1]) print('axes_ab =') print(axes_ab) # final rotation matrix B[:, 1:3] = lib_linalg.matmul(B[:, 1:3], axes_ab) numpy.savetxt(pth_test + 'rotation_matrix.dat', B) # longitude-latitude extrema min_lon = numpy.pi max_lon = -numpy.pi min_lat = 0.5 * numpy.pi max_lat = -0.5 * numpy.pi for i, (tng, occ) in enumerate(planes): ci = corners[i] cj = corners[(i - 1) % m] # fid = open(pth_test + 'arc_' + str(i) + '.dat', 'w') for vec in [center, occ, tng, cj, ci]: fid.write('%s %s %s\n' % (vec[0], vec[1], vec[2])) fid.close() # r1 = cj - occ r2 = ci - occ r190d = numpy.cross(r1, tng) a = numpy.arctan2(numpy.dot(r2, r190d), numpy.dot(r2, r1)) if a < 0: a += 2 * numpy.pi # latitude min_lati, max_lati = extrema_latitude(occ - center, tng, r1, r190d, a, B[:, 2]) min_lat = min(min_lat, min_lati) max_lat = max(max_lat, max_lati) # longitude min_loni, max_loni = extrema_longitude(occ - center, tng, r1, r190d, a, B) min_lon = min(min_lon, min_loni) max_lon = max(max_lon, max_loni) print('%s < lon/PI < %s' % (min_lon / numpy.pi, max_lon / numpy.pi)) print('%s < lat/PI < %s' % (min_lat / numpy.pi, max_lat / numpy.pi)) ctr_tl = 0.5 * numpy.array([max_lon + min_lon, max_lat + min_lat]) rng_tl = 0.5 * numpy.array([max_lon - min_lon, max_lat - min_lat]) return (ravg, B, ctr_tl, rng_tl)