|
@@ -0,0 +1,193 @@
|
|
|
+
|
|
|
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
+ *
|
|
|
+ * @author Ludovic Autin <autin@scripps.edu>
|
|
|
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
|
+ */
|
|
|
+
|
|
|
+import { Vec3, Quat, Mat4 } from '../../../../mol-math/linear-algebra';
|
|
|
+import { NumberArray } from '../../../../mol-util/type-helpers';
|
|
|
+
|
|
|
+interface Frame {
|
|
|
+ t: Vec3,
|
|
|
+ r: Vec3,
|
|
|
+ s: Vec3,
|
|
|
+}
|
|
|
+
|
|
|
+function CubicInterpolate(y0: Vec3, y1: Vec3, y2: Vec3, y3: Vec3, mu: number): Vec3 {
|
|
|
+ const out = Vec3.zero()
|
|
|
+ const mu2 = mu * mu;
|
|
|
+ const a0 = Vec3()
|
|
|
+ const a1 = Vec3()
|
|
|
+ const a2 = Vec3()
|
|
|
+ const a3 = Vec3()
|
|
|
+ Vec3.sub(a0, y3, y2)
|
|
|
+ Vec3.sub(a0, a0, y0)
|
|
|
+ Vec3.add(a0, a0, y1)
|
|
|
+
|
|
|
+ Vec3.sub(a1, y0, y1)
|
|
|
+ Vec3.sub(a1, a1, a0)
|
|
|
+
|
|
|
+ Vec3.sub(a2, y2, y0)
|
|
|
+
|
|
|
+ Vec3.copy(a3, y1)
|
|
|
+
|
|
|
+ out[0] = a0[0] * mu * mu2 + a1[0] * mu2 + a2[0] * mu + a3[0]
|
|
|
+ out[1] = a0[1] * mu * mu2 + a1[1] * mu2 + a2[1] * mu + a3[1]
|
|
|
+ out[2] = a0[2] * mu * mu2 + a1[2] * mu2 + a2[2] * mu + a3[2]
|
|
|
+
|
|
|
+ return out
|
|
|
+}
|
|
|
+
|
|
|
+function ResampleControlPoints(points: NumberArray, segmentLength: number) {
|
|
|
+ const nP = points.length / 3
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let resampledControlPoints: Vec3[] = []
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let idx = 1
|
|
|
+ let currentPosition = Vec3.create(points[idx * 3], points[idx * 3 + 1], points[idx * 3 + 2])
|
|
|
+
|
|
|
+ let lerpValue = 0.0
|
|
|
+
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ if (idx + 2 >= nP) break
|
|
|
+ const cp0 = Vec3.create(points[(idx-1)*3], points[(idx-1)*3+1], points[(idx-1)*3+2])
|
|
|
+ const cp1 = Vec3.create(points[idx*3], points[idx*3+1], points[idx*3+2])
|
|
|
+ const cp2 = Vec3.create(points[(idx+1)*3], points[(idx+1)*3+1], points[(idx+1)*3+2])
|
|
|
+ const cp3 = Vec3.create(points[(idx+2)*3], points[(idx+2)*3+1], points[(idx+2)*3+2]);
|
|
|
+ let found = false
|
|
|
+ for (; lerpValue <= 1; lerpValue += 0.01) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ const candidate = CubicInterpolate(cp0, cp1, cp2, cp3, lerpValue)
|
|
|
+ const d = Vec3.distance(currentPosition, candidate);
|
|
|
+ if (d > segmentLength) {
|
|
|
+ resampledControlPoints.push(candidate)
|
|
|
+ Vec3.copy(currentPosition, candidate)
|
|
|
+ found = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!found) {
|
|
|
+ lerpValue = 0
|
|
|
+ idx += 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return resampledControlPoints
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function GetSmoothNormals(points: Vec3[]) {
|
|
|
+ const nP: number = points.length;
|
|
|
+ const smoothNormals: Vec3[] = []
|
|
|
+ if (points.length < 3) {
|
|
|
+ for (let i = 0; i < points.length; ++i)
|
|
|
+ smoothNormals.push(Vec3.normalize(Vec3(), points[i]))
|
|
|
+ return smoothNormals;
|
|
|
+ }
|
|
|
+ let p0 = Vec3.copy(Vec3(), points[0])
|
|
|
+ let p1 = Vec3.copy(Vec3(), points[1])
|
|
|
+ let p2 = Vec3.copy(Vec3(), points[2])
|
|
|
+ const p21 = Vec3.sub(Vec3(), p2, p1)
|
|
|
+ const p01 = Vec3.sub(Vec3(), p0, p1)
|
|
|
+ const p0121 = Vec3.cross(Vec3(), p01, p21)
|
|
|
+ let last = Vec3.normalize(Vec3(), p0121)
|
|
|
+ smoothNormals.push(last)
|
|
|
+ for (let i = 1; i < points.length - 1; ++i) {
|
|
|
+ p0 = points[i - 1]
|
|
|
+ p1 = points[i]
|
|
|
+ p2 = points[i + 1]
|
|
|
+ const t = Vec3.normalize(Vec3(), Vec3.sub(Vec3(), p2 , p0))
|
|
|
+ const b = Vec3.normalize(Vec3(), Vec3.cross(Vec3(), t, last))
|
|
|
+ let n = Vec3.normalize(Vec3(), Vec3.cross(Vec3(), t, b))
|
|
|
+ n = Vec3.scale(n, n, -1.0)
|
|
|
+ last = Vec3.copy(last, n)
|
|
|
+ smoothNormals.push(n)
|
|
|
+ }
|
|
|
+ last = Vec3.normalize(Vec3(), Vec3.cross(Vec3(), Vec3.sub(Vec3(), points[nP - 3], points[nP-2]), Vec3.sub(Vec3(), points[nP-2] , points[nP-1])))
|
|
|
+ smoothNormals.push(last)
|
|
|
+ return smoothNormals;
|
|
|
+}
|
|
|
+
|
|
|
+function getFrame(reference: Vec3, tangent: Vec3) {
|
|
|
+ const t: Vec3 = Vec3.normalize(Vec3(), tangent);
|
|
|
+
|
|
|
+ const proj_r_to_t: Vec3 = Vec3.scale(
|
|
|
+ Vec3(), tangent, Vec3.dot(reference, tangent) / Vec3.dot(tangent, tangent)
|
|
|
+ )
|
|
|
+ const r: Vec3 = Vec3.normalize(Vec3(), Vec3.sub(Vec3(), reference , proj_r_to_t))
|
|
|
+
|
|
|
+ const s: Vec3 = Vec3.normalize(Vec3(), Vec3.cross(Vec3(), t, r))
|
|
|
+ return { t, r, s }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
|
|
|
+ const frames: Frame[] = [];
|
|
|
+ const t0: Vec3 = Vec3.normalize(Vec3(), Vec3.sub(Vec3(), points[1], points[0]))
|
|
|
+ frames.push(getFrame(normals[0], t0))
|
|
|
+
|
|
|
+ for (let i = 0; i< points.length-2; ++i) {
|
|
|
+ const t2 = Vec3.normalize(Vec3(), Vec3.sub(Vec3(), points[i+2], points[i+1]))
|
|
|
+ const v1: Vec3 = Vec3.sub(Vec3(), points[i + 1], points[i])
|
|
|
+ const c1 = Vec3.dot(v1, v1)
|
|
|
+
|
|
|
+ const v1r = Vec3.scale(Vec3(), v1, (2.0/c1)*Vec3.dot(v1, frames[i].r))
|
|
|
+ const ref_L_i: Vec3 = Vec3.sub(Vec3(), frames[i].r, v1r)
|
|
|
+
|
|
|
+ const v1t = Vec3.scale(Vec3(), v1, (2.0/c1) * Vec3.dot(v1, frames[i].t))
|
|
|
+ const tan_L_i: Vec3 = Vec3.sub(Vec3(), frames[i].t, v1t)
|
|
|
+
|
|
|
+ const v2: Vec3 = Vec3.sub(Vec3(), t2 , tan_L_i)
|
|
|
+ const c2 = Vec3.dot(v2, v2)
|
|
|
+
|
|
|
+ const v2l = Vec3.scale(Vec3(), v1, (2.0/c2) * Vec3.dot(v2, ref_L_i))
|
|
|
+ const ref_next = Vec3.sub(Vec3(), ref_L_i, v2l)
|
|
|
+ frames.push(getFrame(ref_next, t2))
|
|
|
+ }
|
|
|
+ return frames;
|
|
|
+}
|
|
|
+
|
|
|
+export function getMatFromResamplePoints(points: NumberArray) {
|
|
|
+ let segmentLength = 3.4
|
|
|
+ let new_points: Vec3[] = ResampleControlPoints(points, 3.4)
|
|
|
+ const npoints = new_points.length
|
|
|
+ let new_normal: Vec3[] = GetSmoothNormals(new_points)
|
|
|
+ let frames: Frame[] = GetMiniFrame(new_points, new_normal)
|
|
|
+ const limit = npoints
|
|
|
+ let transforms: Mat4[] = []
|
|
|
+ let pti: Vec3 = Vec3.copy(Vec3(), new_points[0]);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ for (let i = 0; i<npoints-2; ++i) {
|
|
|
+ const pti1: Vec3 = new_points[i+1]
|
|
|
+ const d = Vec3.distance(pti, pti1)
|
|
|
+ if (d >= segmentLength) {
|
|
|
+
|
|
|
+ const quat: Quat = Quat.rotationTo(Quat.zero(), Vec3.create(0, 0, 1), frames[i].t)
|
|
|
+ const rq: Quat = Quat.setAxisAngle(Quat.zero(), frames[i].t, Math.random()*3.60 )
|
|
|
+ let m: Mat4 = Mat4.fromQuat(Mat4.zero(), Quat.multiply(Quat.zero(), rq, quat))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ Mat4.setTranslation(m, pti1)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ transforms.push(m)
|
|
|
+ pti = Vec3.copy(pti, pti1)
|
|
|
+ }
|
|
|
+ if (transforms.length >= limit) break
|
|
|
+ }
|
|
|
+ return transforms
|
|
|
+}
|