diff --git a/01_Code/physical_computing_interface/assembly/InverseKinematic.js b/01_Code/physical_computing_interface/assembly/InverseKinematic.js index 77e099f57c21f61a4516b9954dc2418f4b06c56f..6a417ddade8d1abcd7b4042f88a7b2711f41387b 100644 --- a/01_Code/physical_computing_interface/assembly/InverseKinematic.js +++ b/01_Code/physical_computing_interface/assembly/InverseKinematic.js @@ -1,465 +1,452 @@ -// define((require, exports, module) => { - var debug=false; + - const Serial = { - println(text) { - // // console.log(text) - }, - } +function InverseKinematic(geometry){ + this.debug=false; + this.OK = 0 + this.OUT_OF_RANGE = 1 + this.OUT_OF_BOUNDS = 2 - const arrows = {} + this.V1_length_x_z = Math.sqrt(Math.pow(geometry[1][0], 2) + Math.pow(geometry[1][2], 2)) + this.V4_length_x_y_z = Math.sqrt(Math.pow(geometry[4][0], 2) + Math.pow(geometry[4][2], 2) + Math.pow(-geometry[4][1], 2)) - function addArrow(name, from, to, color = 0xffff00, length = 10) { - if (arrows.hasOwnProperty(name)) { - window.debug.scene.remove(arrows[name]) - } - if (!debug) return - const toPoint = new THREE.Vector3(to[0], to[1], to[2]) - const origin = new THREE.Vector3(from[0], from[1], from[2]) - // length = length || toPoint.sub(origin).length() - // toPoint.normalize() - arrows[name] = new THREE.ArrowHelper(toPoint.sub(origin).normalize(), origin, length, color, 2, 1) - window.debug.scene.add(arrows[name]) - } - function addVectorArrow(name, from, vector, color, length) { - addArrow(name, from, [from[0] + vector[0], from[1] + vector[1], from[2] + vector[2]], color, length) + this.J_initial_absolute = [] + const tmpPos = [0, 0, 0] + for (let i = 0; i < geometry.length; i++) { + this.J_initial_absolute.push([tmpPos[0], tmpPos[1], tmpPos[2]]) + tmpPos[0] += geometry[i][0] + tmpPos[1] += geometry[i][1] + tmpPos[2] += geometry[i][2] } - const spheres = {} - - function addSphere(name, position, color = 0xffff00, diameter = 1) { - if (spheres.hasOwnProperty(name)) { - window.debug.scene.remove(spheres[name]) - } - // if (!debug) return// Amira changed - if (!debug) return - - const geometry = new THREE.SphereGeometry(diameter, 32, 32) - const material = new THREE.MeshBasicMaterial({ - color, - }) - - spheres[name] = new THREE.Mesh(geometry, material) - spheres[name].position.set(position[0], position[1], position[2]) - window.debug.scene.add(spheres[name]) - } - - class InverseKinematic { - constructor(geometry) { - this.OK = 0 - this.OUT_OF_RANGE = 1 - this.OUT_OF_BOUNDS = 2 - - this.V1_length_x_z = Math.sqrt(Math.pow(geometry[1][0], 2) + Math.pow(geometry[1][2], 2)) - this.V4_length_x_y_z = Math.sqrt(Math.pow(geometry[4][0], 2) + Math.pow(geometry[4][2], 2) + Math.pow(-geometry[4][1], 2)) - - - this.J_initial_absolute = [] - const tmpPos = [0, 0, 0] - for (let i = 0; i < geometry.length; i++) { - this.J_initial_absolute.push([tmpPos[0], tmpPos[1], tmpPos[2]]) - tmpPos[0] += geometry[i][0] - tmpPos[1] += geometry[i][1] - tmpPos[2] += geometry[i][2] - } - - this.R_corrected = [0, 0, 0, 0, 0, 0] - - this.R_corrected[1] += Math.PI / 2 - this.R_corrected[1] -= Math.atan2(geometry[1][0], geometry[1][2]) // correct offset bone - - this.R_corrected[2] += Math.PI / 2 - this.R_corrected[2] += Math.atan2((geometry[2][2] + geometry[3][2]), (geometry[2][0] + geometry[3][0])) // correct offset bone V2,V3 - this.R_corrected[2] += Math.atan2(geometry[1][0], geometry[1][2]) // correct bone offset of V1 - - this.R_corrected[4] += Math.PI - - this.geometry = geometry - } - - calculateAngles(x, y, z, a, b, c, angles, config = [false, false, false]) { - const cc = Math.cos(c) - const sc = Math.sin(c) - const cb = Math.cos(b) - const sb = Math.sin(b) - const ca = Math.cos(a) - const sa = Math.sin(a) - - const targetVectorZ = [ - sb, -sa * cb, - ca * cb, - ] - - const R = [ - this.R_corrected[0], - this.R_corrected[1], - this.R_corrected[2], - this.R_corrected[3], - this.R_corrected[4], - this.R_corrected[5], - ] - - const J = [ - [0, 0, 0], - [0, 0, 0], - [0, 0, 0], - [0, 0, 0], - [0, 0, 0], - [0, 0, 0], - ] - - - // ---- J5 ---- - - J[5][0] = x - J[5][1] = y - J[5][2] = z - addSphere('J5', J[5]) + this.R_corrected = [0, 0, 0, 0, 0, 0] - // ---- J4 ---- - // vector + this.R_corrected[1] += Math.PI / 2 + this.R_corrected[1] -= Math.atan2(geometry[1][0], geometry[1][2]) // correct offset bone - J[4][0] = x - this.V4_length_x_y_z * targetVectorZ[0] - J[4][1] = y - this.V4_length_x_y_z * targetVectorZ[1] - J[4][2] = z - this.V4_length_x_y_z * targetVectorZ[2] - addSphere('J4', J[4]) + this.R_corrected[2] += Math.PI / 2 + this.R_corrected[2] += Math.atan2((geometry[2][2] + geometry[3][2]), (geometry[2][0] + geometry[3][0])) // correct offset bone V2,V3 + this.R_corrected[2] += Math.atan2(geometry[1][0], geometry[1][2]) // correct bone offset of V1 + this.R_corrected[4] += Math.PI - // ---- R0 ---- - // # J4 + this.geometry = geometry + this.spheres = {} + this.arrows = {} - const alphaR0 = Math.asin(this.J_initial_absolute[4][1] / this.length2(J[4][1], J[4][0])) - R[0] += Math.atan2(J[4][1], J[4][0]) - R[0] += -alphaR0 +} - if (config[0]) { - R[0] += 2 * alphaR0 - Math.PI - } +InverseKinematic.prototype.calculateAngles=function (x, y, z, a, b, c, angles, config = [false, false, false]) { + const cc = Math.cos(c) + const sc = Math.sin(c) + const cb = Math.cos(b) + const sb = Math.sin(b) + const ca = Math.cos(a) + const sa = Math.sin(a) - if (-this.J_initial_absolute[4][1] > this.length2(J[4][2], J[4][0])) { - Serial.println('out of reach') - } + const targetVectorZ = [ + sb, -sa * cb, + ca * cb, + ] + const R = [ + this.R_corrected[0], + this.R_corrected[1], + this.R_corrected[2], + this.R_corrected[3], + this.R_corrected[4], + this.R_corrected[5], + ] - // ---- J1 ---- - // # R0 + const J = [ + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + ] - J[1][0] = Math.cos(R[0]) * this.geometry[0][0] + Math.sin(R[0]) * -this.geometry[0][1] - J[1][1] = Math.sin(R[0]) * this.geometry[0][0] + Math.cos(R[0]) * this.geometry[0][1] - J[1][2] = this.geometry[0][2] - addSphere('J1', J[1], 0x00ff00) + // ---- J5 ---- - // ---- rotate J4 into x,z plane ---- - // # J4 R0 + J[5][0] = x + J[5][1] = y + J[5][2] = z + this.addSphere('J5', J[5]) - const J4_x_z = [] - J4_x_z[0] = Math.cos(R[0]) * J[4][0] + Math.sin(R[0]) * J[4][1] - J4_x_z[1] = Math.sin(R[0]) * J[4][0] + Math.cos(R[0]) * -J[4][1] // 0 - J4_x_z[2] = J[4][2] - addSphere('J4_x_z', J4_x_z, 0xff0000) + // ---- J4 ---- + // vector + J[4][0] = x - this.V4_length_x_y_z * targetVectorZ[0] + J[4][1] = y - this.V4_length_x_y_z * targetVectorZ[1] + J[4][2] = z - this.V4_length_x_y_z * targetVectorZ[2] + this.addSphere('J4', J[4]) - // ---- J1J4_projected_length_square ---- - // # J4 R0 - const J1J4_projected_length_square = Math.pow(J4_x_z[0] - this.J_initial_absolute[1][0], 2) + Math.pow(J4_x_z[2] - this.J_initial_absolute[1][2], 2) // not using Math.sqrt + // ---- R0 ---- + // # J4 + const alphaR0 = Math.asin(this.J_initial_absolute[4][1] / this.length2(J[4][1], J[4][0])) + R[0] += Math.atan2(J[4][1], J[4][0]) + R[0] += -alphaR0 - // ---- R2 ---- - // # J4 R0 - - const J2J4_length_x_z = this.length2(this.geometry[2][0] + this.geometry[3][0], this.geometry[2][2] + this.geometry[3][2]) - R[2] += ((config[1] ? !config[0] : config[0]) ? 1.0 : -1.0) * Math.acos((-J1J4_projected_length_square + Math.pow(J2J4_length_x_z, 2) + Math.pow(this.V1_length_x_z, 2)) / (2.0 * (J2J4_length_x_z) * this.V1_length_x_z)) - R[2] -= 2 * Math.PI - - R[2] = ((R[2] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI // clamp -180/180 degree - - - // ---- R1 ---- - // # J4 R0 - - const J1J4_projected_length = Math.sqrt(J1J4_projected_length_square) - R[1] -= Math.atan2((J4_x_z[2] - this.J_initial_absolute[1][2]), (J4_x_z[0] - this.J_initial_absolute[1][0])) // a'' - R[1] += ((config[1] ? !config[0] : config[0]) ? 1.0 : -1.0) * Math.acos((J1J4_projected_length_square - Math.pow(J2J4_length_x_z, 2) + Math.pow(this.V1_length_x_z, 2)) / (2.0 * J1J4_projected_length * this.V1_length_x_z)) // a - - R[1] = ((R[1] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI + if (config[0]) { + R[0] += 2 * alphaR0 - Math.PI + } + if (-this.J_initial_absolute[4][1] > this.length2(J[4][2], J[4][0])) { + Serial.println('out of reach') + } - // ---- J2 ---- - // # R1 R0 - const ta = Math.cos(R[0]) - const tb = Math.sin(R[0]) - const tc = this.geometry[0][0] - const d = this.geometry[0][2] - const e = -this.geometry[0][1] - const f = Math.cos(R[1]) - const g = Math.sin(R[1]) - const h = this.geometry[1][0] - const i = this.geometry[1][2] - const j = -this.geometry[1][1] - const k = Math.cos(R[2]) - const l = Math.sin(R[2]) - const m = this.geometry[2][0] - const n = this.geometry[2][2] - const o = -this.geometry[2][1] + // ---- J1 ---- + // # R0 - J[2][0] = ta * tc + tb * e + ta * f * h - ta * -g * i + tb * j - J[2][1] = -(-tb * tc + ta * e - tb * f * h + tb * -g * i + ta * j) - J[2][2] = d + -g * h + f * i - addSphere('J2', J[2], 0x0000ff) + J[1][0] = Math.cos(R[0]) * this.geometry[0][0] + Math.sin(R[0]) * -this.geometry[0][1] + J[1][1] = Math.sin(R[0]) * this.geometry[0][0] + Math.cos(R[0]) * this.geometry[0][1] + J[1][2] = this.geometry[0][2] + this.addSphere('J1', J[1], 0x00ff00) - J[3][0] = J[2][0] + ta * f * k * m - ta * -g * -l * m - ta * -g * k * n - ta * f * -l * n + tb * o - J[3][1] = J[2][1] - (-tb * f * k * m + tb * -g * -l * m + tb * -g * k * n + tb * f * -l * n + ta * o) - J[3][2] = J[2][2] + -g * k * m + f * -l * m + f * k * n + g * -l * n - addSphere('J3', J[3], 0x0000ff) + // ---- rotate J4 into x,z plane ---- + // # J4 R0 - // ---- J4J3 J4J5 ---- - // # J3 J4 J5 + const J4_x_z = [] - const J4J5_vector = [J[5][0] - J[4][0], J[5][1] - J[4][1], J[5][2] - J[4][2]] - const J4J3_vector = [J[3][0] - J[4][0], J[3][1] - J[4][1], J[3][2] - J[4][2]] + J4_x_z[0] = Math.cos(R[0]) * J[4][0] + Math.sin(R[0]) * J[4][1] + J4_x_z[1] = Math.sin(R[0]) * J[4][0] + Math.cos(R[0]) * -J[4][1] // 0 + J4_x_z[2] = J[4][2] + this.addSphere('J4_x_z', J4_x_z, 0xff0000) - // ---- R3 ---- - // # J3 J4 J5 + // ---- J1J4_projected_length_square ---- + // # J4 R0 - const J4J5_J4J3_normal_vector = this.cross(J4J5_vector, J4J3_vector) + const J1J4_projected_length_square = Math.pow(J4_x_z[0] - this.J_initial_absolute[1][0], 2) + Math.pow(J4_x_z[2] - this.J_initial_absolute[1][2], 2) // not using Math.sqrt - addVectorArrow('normal J4', J[4], J4J5_J4J3_normal_vector, 0xbada55, 8) - const ZY_parallel_aligned_vector = [ - 10 * -Math.sin(R[0]), - 10 * Math.cos(R[0]), - 0, - ] + // ---- R2 ---- + // # J4 R0 - const ZY_aligned_J4J3_normal_vector = this.cross(ZY_parallel_aligned_vector, J4J3_vector) + const J2J4_length_x_z = this.length2(this.geometry[2][0] + this.geometry[3][0], this.geometry[2][2] + this.geometry[3][2]) + R[2] += ((config[1] ? !config[0] : config[0]) ? 1.0 : -1.0) * Math.acos((-J1J4_projected_length_square + Math.pow(J2J4_length_x_z, 2) + Math.pow(this.V1_length_x_z, 2)) / (2.0 * (J2J4_length_x_z) * this.V1_length_x_z)) + R[2] -= 2 * Math.PI - addVectorArrow('normal J4 Y_vectors', J[4], ZY_aligned_J4J3_normal_vector, 0xff00ff) - addVectorArrow('XZ_parallel_aligned_vector', J[4], ZY_parallel_aligned_vector, 0x11ff11) + R[2] = ((R[2] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI // clamp -180/180 degree - R[3] = this.angleBetween(J4J5_J4J3_normal_vector, ZY_parallel_aligned_vector, ZY_aligned_J4J3_normal_vector) - if (config[2]) // rotate 180 and clamp -180,180 - { - R[3] += Math.PI - } - R[3] = ((R[3] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI + // ---- R1 ---- + // # J4 R0 + const J1J4_projected_length = Math.sqrt(J1J4_projected_length_square) + R[1] -= Math.atan2((J4_x_z[2] - this.J_initial_absolute[1][2]), (J4_x_z[0] - this.J_initial_absolute[1][0])) // a'' + R[1] += ((config[1] ? !config[0] : config[0]) ? 1.0 : -1.0) * Math.acos((J1J4_projected_length_square - Math.pow(J2J4_length_x_z, 2) + Math.pow(this.V1_length_x_z, 2)) / (2.0 * J1J4_projected_length * this.V1_length_x_z)) // a - // ---- R4 ---- - // # J4 J3 J5 R3 + R[1] = ((R[1] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI - R[4] += ((config[2]) ? 1 : -1) * this.angleBetween2(J4J5_vector, J4J3_vector) - // clamp -180,180 - R[4] = ((R[4] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI + // ---- J2 ---- + // # R1 R0 + const ta = Math.cos(R[0]) + const tb = Math.sin(R[0]) + const tc = this.geometry[0][0] + const d = this.geometry[0][2] + const e = -this.geometry[0][1] + const f = Math.cos(R[1]) + const g = Math.sin(R[1]) + const h = this.geometry[1][0] + const i = this.geometry[1][2] + const j = -this.geometry[1][1] + const k = Math.cos(R[2]) + const l = Math.sin(R[2]) + const m = this.geometry[2][0] + const n = this.geometry[2][2] + const o = -this.geometry[2][1] - // ---- R5 ---- - // # J4 J5 J3 + J[2][0] = ta * tc + tb * e + ta * f * h - ta * -g * i + tb * j + J[2][1] = -(-tb * tc + ta * e - tb * f * h + tb * -g * i + ta * j) + J[2][2] = d + -g * h + f * i + this.addSphere('J2', J[2], 0x0000ff) - const targetVectorY = [-cb * sc, - ca * cc - sa * sb * sc, - sa * cc + ca * sb * sc, - ] + J[3][0] = J[2][0] + ta * f * k * m - ta * -g * -l * m - ta * -g * k * n - ta * f * -l * n + tb * o + J[3][1] = J[2][1] - (-tb * f * k * m + tb * -g * -l * m + tb * -g * k * n + tb * f * -l * n + ta * o) + J[3][2] = J[2][2] + -g * k * m + f * -l * m + f * k * n + g * -l * n + this.addSphere('J3', J[3], 0x0000ff) - R[5] -= this.angleBetween(J4J5_J4J3_normal_vector, targetVectorY, this.cross(targetVectorZ, targetVectorY)) - if (config[2]) R[5] += Math.PI + // ---- J4J3 J4J5 ---- + // # J3 J4 J5 - R[5] = ((R[5] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI - // ---- Error handling ---- + const J4J5_vector = [J[5][0] - J[4][0], J[5][1] - J[4][1], J[5][2] - J[4][2]] + const J4J3_vector = [J[3][0] - J[4][0], J[3][1] - J[4][1], J[3][2] - J[4][2]] - const error = false - const outOfBounds = [false, false, false, false, false, false] + // ---- R3 ---- + // # J3 J4 J5 - angles[0] = R[0] - angles[1] = R[1] - angles[2] = R[2] - angles[3] = R[3] - angles[4] = R[4] - angles[5] = R[5] - } + const J4J5_J4J3_normal_vector = this.cross(J4J5_vector, J4J3_vector) - calculateTCP(R0, R1, R2, R3, R4, R5, jointsResult) { - const joints = [ - [], - [], - [], - [], - [], - [], - ] - this.calculateCoordinates(R0, R1, R2, R3, R4, R5, joints) - jointsResult[0] = joints[5][0] - jointsResult[1] = joints[5][1] - jointsResult[2] = joints[5][2] - jointsResult[3] = joints[5][3] - jointsResult[4] = joints[5][4] - jointsResult[5] = joints[5][5] - } + this.addVectorArrow('normal J4', J[4], J4J5_J4J3_normal_vector, 0xbada55, 8) - calculateCoordinates(R0, R1, R2, R3, R4, R5, jointsResult, config = [false, false, false]) { - // todo detect config - - const a = Math.cos(R0) - const b = Math.sin(R0) - const c = this.geometry[0][0] - const d = this.geometry[0][1] - const e = this.geometry[0][2] - const f = Math.cos(R1) - const g = Math.sin(R1) - const h = this.geometry[1][0] - const i = this.geometry[1][1] - const j = this.geometry[1][2] - const k = Math.cos(R2) - const l = Math.sin(R2) - const m = this.geometry[2][0] - const n = this.geometry[2][1] - const o = this.geometry[2][2] - const p = Math.cos(R3) - const q = Math.sin(R3) - const r = this.geometry[3][0] - const s = this.geometry[3][1] - const t = this.geometry[3][2] - const u = Math.cos(R4) - const v = Math.sin(R4) - const w = this.geometry[4][0] - const x = this.geometry[4][1] - const y = this.geometry[4][2] - const A = Math.cos(R5) - const B = Math.sin(R5) - - jointsResult[0][0] = 0 - jointsResult[0][1] = 0 - jointsResult[0][2] = 0 - - jointsResult[1][0] = jointsResult[0][0] + a * c - b * d - jointsResult[1][1] = jointsResult[0][1] + b * c + a * d - jointsResult[1][2] = jointsResult[0][2] + e - - jointsResult[2][0] = jointsResult[1][0] + a * f * h - b * i + a * g * j - jointsResult[2][1] = jointsResult[1][1] + b * f * h + a * i + b * g * j - jointsResult[2][2] = jointsResult[1][2] + -g * h + f * j - - jointsResult[3][0] = jointsResult[2][0] + a * f * k * m - a * g * l * m - b * n + a * g * k * o + a * f * l * o - jointsResult[3][1] = jointsResult[2][1] + b * f * k * m - b * g * l * m + a * n + b * g * k * o + b * f * l * o - jointsResult[3][2] = jointsResult[2][2] - g * k * m - f * l * m + f * k * o - g * l * o - - jointsResult[4][0] = jointsResult[3][0] + a * f * k * r - a * g * l * r - b * p * s + a * g * k * q * s + a * f * l * q * s + a * g * k * p * t + a * f * l * p * t + b * q * t - jointsResult[4][1] = jointsResult[3][1] + b * f * k * r - b * g * l * r + a * p * s + b * g * k * q * s + b * f * l * q * s + b * g * k * p * t + b * f * l * p * t - a * q * t - jointsResult[4][2] = jointsResult[3][2] - g * k * r - f * l * r + f * k * q * s - g * l * q * s + f * k * p * t - g * l * p * t - - jointsResult[5][0] = jointsResult[4][0] + a * f * k * u * w - a * g * l * u * w - a * g * k * p * v * w - a * f * l * p * v * w - b * q * v * w - b * p * x + a * g * k * q * x + a * f * l * q * x + a * g * k * p * u * y + a * f * l * p * u * y + b * q * u * y + a * f * k * v * y - a * g * l * v * y - jointsResult[5][1] = jointsResult[4][1] + b * f * k * u * w - b * g * l * u * w - b * g * k * p * v * w - b * f * l * p * v * w + a * q * v * w + a * p * x + b * g * k * q * x + b * f * l * q * x + b * g * k * p * u * y + b * f * l * p * u * y - a * q * u * y + b * f * k * v * y - b * g * l * v * y - jointsResult[5][2] = jointsResult[4][2] - g * k * u * w - f * l * u * w - f * k * p * v * w + g * l * p * v * w + f * k * q * x - g * l * q * x + f * k * p * u * y - g * l * p * u * y - g * k * v * y - f * l * v * y - - const M = [ - [-B * b * p + B * a * g * k * q + B * a * f * l * q - A * a * g * k * p * u - A * a * f * l * p * u - A * b * q * u - A * a * f * k * v + A * a * g * l * v, -A * b * p + A * a * g * k * q + A * a * f * l * q + B * a * g * k * p * u + B * a * f * l * p * u + B * b * q * u + B * a * f * k * v - B * a * g * l * v, a * f * k * u - a * g * l * u - a * g * k * p * v - a * f * l * p * v - b * q * v], - [B * a * p + B * b * g * k * q + B * b * f * l * q - A * b * g * k * p * u - A * b * f * l * p * u + A * a * q * u - A * b * f * k * v + A * b * g * l * v, A * a * p + A * b * g * k * q + A * b * f * l * q + B * b * g * k * p * u + B * b * f * l * p * u - B * a * q * u + B * b * f * k * v - B * b * g * l * v, b * f * k * u - b * g * l * u - b * g * k * p * v - b * f * l * p * v + a * q * v], - [B * f * k * q - B * g * l * q - A * f * k * p * u + A * g * l * p * u + A * g * k * v + A * f * l * v, A * f * k * q - A * g * l * q + B * f * k * p * u - B * g * l * p * u - B * g * k * v - B * f * l * v, -g * k * u - f * l * u - f * k * p * v + g * l * p * v], - ] - - // https://www.geometrictools.com/Documentation/EulerAngles.pdf - let thetaY, - thetaX, - thetaZ - if (M[0][2] < 1) { - if (M[0][2] > -1) { - thetaY = Math.asin(M[0][2]) - thetaX = Math.atan2(-M[1][2], M[2][2]) - thetaZ = Math.atan2(-M[0][1], M[0][0]) - } else { - thetaY = -Math.PI / 2 - thetaX = -Math.atan2(M[1][0], M[1][1]) - thetaZ = 0 - } - } else { - thetaY = +Math.PI / 2 - thetaX = Math.atan2(M[1][0], M[1][1]) - thetaZ = 0 - } - - - jointsResult[5][3] = thetaX - jointsResult[5][4] = thetaY - jointsResult[5][5] = thetaZ - } + const ZY_parallel_aligned_vector = [ + 10 * -Math.sin(R[0]), + 10 * Math.cos(R[0]), + 0, + ] - cross(vectorA, vectorB, result = []) { - result[0] = vectorA[1] * vectorB[2] - vectorA[2] * vectorB[1] - result[1] = vectorA[2] * vectorB[0] - vectorA[0] * vectorB[2] - result[2] = vectorA[0] * vectorB[1] - vectorA[1] * vectorB[0] - return result - } + const ZY_aligned_J4J3_normal_vector = this.cross(ZY_parallel_aligned_vector, J4J3_vector) - dot(vectorA, vectorB) { - return vectorA[0] * vectorB[0] + vectorA[1] * vectorB[1] + vectorA[2] * vectorB[2] - } + this.addVectorArrow('normal J4 Y_vectors', J[4], ZY_aligned_J4J3_normal_vector, 0xff00ff) + this.addVectorArrow('XZ_parallel_aligned_vector', J[4], ZY_parallel_aligned_vector, 0x11ff11) - /** - * @param {array} vectorA angle from - * @param {array} vectorB angle to - * @param {array} referenceVector angle to set 0 degree from. coplanar with vecA and vecB - * @return {number} description - * @example angleBetween([1,0,0],[0,1,0],[0,0,1]) // PI/2 - */ - angleBetween(vectorA, vectorB, referenceVector) { - // angle = atan2(norm(cross(a, b)), dot(a, b)) + R[3] = this.angleBetween(J4J5_J4J3_normal_vector, ZY_parallel_aligned_vector, ZY_aligned_J4J3_normal_vector) - const norm = this.length3(this.cross(vectorA, vectorB)) - - const angle = Math.atan2(norm, (vectorB[0] * vectorA[0] + vectorB[1] * vectorA[1] + vectorB[2] * vectorA[2])) - - const tmp = referenceVector[0] * vectorA[0] + referenceVector[1] * vectorA[1] + referenceVector[2] * vectorA[2] - - const sign = (tmp > 0.0001) ? 1.0 : -1.0 - - return angle * sign - } - - length3(vector) { - return Math.sqrt(Math.pow(vector[0], 2) + Math.pow(vector[1], 2) + Math.pow(vector[2], 2)) + if (config[2]) // rotate 180 and clamp -180,180 + { + R[3] += Math.PI + } + R[3] = ((R[3] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI + + + // ---- R4 ---- + // # J4 J3 J5 R3 + + R[4] += ((config[2]) ? 1 : -1) * this.angleBetween2(J4J5_vector, J4J3_vector) + + // clamp -180,180 + R[4] = ((R[4] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI + + + // ---- R5 ---- + // # J4 J5 J3 + + const targetVectorY = [-cb * sc, + ca * cc - sa * sb * sc, + sa * cc + ca * sb * sc, + ] + + R[5] -= this.angleBetween(J4J5_J4J3_normal_vector, targetVectorY, this.cross(targetVectorZ, targetVectorY)) + + if (config[2]) R[5] += Math.PI + + R[5] = ((R[5] + 3 * Math.PI) % (2 * Math.PI)) - Math.PI + // ---- Error handling ---- + + const error = false + const outOfBounds = [false, false, false, false, false, false] + + + angles[0] = R[0] + angles[1] = R[1] + angles[2] = R[2] + angles[3] = R[3] + angles[4] = R[4] + angles[5] = R[5] +} + +InverseKinematic.prototype.calculateTCP=function(R0, R1, R2, R3, R4, R5, jointsResult) { + const joints = [ + [], + [], + [], + [], + [], + [], + ] + this.calculateCoordinates(R0, R1, R2, R3, R4, R5, joints) + jointsResult[0] = joints[5][0] + jointsResult[1] = joints[5][1] + jointsResult[2] = joints[5][2] + jointsResult[3] = joints[5][3] + jointsResult[4] = joints[5][4] + jointsResult[5] = joints[5][5] +} + +InverseKinematic.prototype.calculateCoordinates=function(R0, R1, R2, R3, R4, R5, jointsResult, config = [false, false, false]) { + // todo detect config + + const a = Math.cos(R0) + const b = Math.sin(R0) + const c = this.geometry[0][0] + const d = this.geometry[0][1] + const e = this.geometry[0][2] + const f = Math.cos(R1) + const g = Math.sin(R1) + const h = this.geometry[1][0] + const i = this.geometry[1][1] + const j = this.geometry[1][2] + const k = Math.cos(R2) + const l = Math.sin(R2) + const m = this.geometry[2][0] + const n = this.geometry[2][1] + const o = this.geometry[2][2] + const p = Math.cos(R3) + const q = Math.sin(R3) + const r = this.geometry[3][0] + const s = this.geometry[3][1] + const t = this.geometry[3][2] + const u = Math.cos(R4) + const v = Math.sin(R4) + const w = this.geometry[4][0] + const x = this.geometry[4][1] + const y = this.geometry[4][2] + const A = Math.cos(R5) + const B = Math.sin(R5) + + jointsResult[0][0] = 0 + jointsResult[0][1] = 0 + jointsResult[0][2] = 0 + + jointsResult[1][0] = jointsResult[0][0] + a * c - b * d + jointsResult[1][1] = jointsResult[0][1] + b * c + a * d + jointsResult[1][2] = jointsResult[0][2] + e + + jointsResult[2][0] = jointsResult[1][0] + a * f * h - b * i + a * g * j + jointsResult[2][1] = jointsResult[1][1] + b * f * h + a * i + b * g * j + jointsResult[2][2] = jointsResult[1][2] + -g * h + f * j + + jointsResult[3][0] = jointsResult[2][0] + a * f * k * m - a * g * l * m - b * n + a * g * k * o + a * f * l * o + jointsResult[3][1] = jointsResult[2][1] + b * f * k * m - b * g * l * m + a * n + b * g * k * o + b * f * l * o + jointsResult[3][2] = jointsResult[2][2] - g * k * m - f * l * m + f * k * o - g * l * o + + jointsResult[4][0] = jointsResult[3][0] + a * f * k * r - a * g * l * r - b * p * s + a * g * k * q * s + a * f * l * q * s + a * g * k * p * t + a * f * l * p * t + b * q * t + jointsResult[4][1] = jointsResult[3][1] + b * f * k * r - b * g * l * r + a * p * s + b * g * k * q * s + b * f * l * q * s + b * g * k * p * t + b * f * l * p * t - a * q * t + jointsResult[4][2] = jointsResult[3][2] - g * k * r - f * l * r + f * k * q * s - g * l * q * s + f * k * p * t - g * l * p * t + + jointsResult[5][0] = jointsResult[4][0] + a * f * k * u * w - a * g * l * u * w - a * g * k * p * v * w - a * f * l * p * v * w - b * q * v * w - b * p * x + a * g * k * q * x + a * f * l * q * x + a * g * k * p * u * y + a * f * l * p * u * y + b * q * u * y + a * f * k * v * y - a * g * l * v * y + jointsResult[5][1] = jointsResult[4][1] + b * f * k * u * w - b * g * l * u * w - b * g * k * p * v * w - b * f * l * p * v * w + a * q * v * w + a * p * x + b * g * k * q * x + b * f * l * q * x + b * g * k * p * u * y + b * f * l * p * u * y - a * q * u * y + b * f * k * v * y - b * g * l * v * y + jointsResult[5][2] = jointsResult[4][2] - g * k * u * w - f * l * u * w - f * k * p * v * w + g * l * p * v * w + f * k * q * x - g * l * q * x + f * k * p * u * y - g * l * p * u * y - g * k * v * y - f * l * v * y + + const M = [ + [-B * b * p + B * a * g * k * q + B * a * f * l * q - A * a * g * k * p * u - A * a * f * l * p * u - A * b * q * u - A * a * f * k * v + A * a * g * l * v, -A * b * p + A * a * g * k * q + A * a * f * l * q + B * a * g * k * p * u + B * a * f * l * p * u + B * b * q * u + B * a * f * k * v - B * a * g * l * v, a * f * k * u - a * g * l * u - a * g * k * p * v - a * f * l * p * v - b * q * v], + [B * a * p + B * b * g * k * q + B * b * f * l * q - A * b * g * k * p * u - A * b * f * l * p * u + A * a * q * u - A * b * f * k * v + A * b * g * l * v, A * a * p + A * b * g * k * q + A * b * f * l * q + B * b * g * k * p * u + B * b * f * l * p * u - B * a * q * u + B * b * f * k * v - B * b * g * l * v, b * f * k * u - b * g * l * u - b * g * k * p * v - b * f * l * p * v + a * q * v], + [B * f * k * q - B * g * l * q - A * f * k * p * u + A * g * l * p * u + A * g * k * v + A * f * l * v, A * f * k * q - A * g * l * q + B * f * k * p * u - B * g * l * p * u - B * g * k * v - B * f * l * v, -g * k * u - f * l * u - f * k * p * v + g * l * p * v], + ] + + // https://www.geometrictools.com/Documentation/EulerAngles.pdf + let thetaY, + thetaX, + thetaZ + if (M[0][2] < 1) { + if (M[0][2] > -1) { + thetaY = Math.asin(M[0][2]) + thetaX = Math.atan2(-M[1][2], M[2][2]) + thetaZ = Math.atan2(-M[0][1], M[0][0]) + } else { + thetaY = -Math.PI / 2 + thetaX = -Math.atan2(M[1][0], M[1][1]) + thetaZ = 0 } + } else { + thetaY = +Math.PI / 2 + thetaX = Math.atan2(M[1][0], M[1][1]) + thetaZ = 0 + } - length2(a, b) { - return Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)) - } - angleBetween2(v1, v2) { - let angle - // turn vectors into unit vectors - // this.normalize(v1,v1) - // this.normalize(v2,v2) - // - // var angle = Math.acos(this.dot(v1, v2)) - // // if no noticable rotation is available return zero rotation - // // this way we avoid Cross product artifacts - // if (Math.abs(angle) < 0.0001) return 0 - // // in this case there are 2 lines on the same axis - // // // angle = atan2(norm(cross(a, b)), dot(a, b)) - const cross = this.cross(v1, v2) - - return Math.atan2(this.length3(cross), this.dot(v1, v2)) - } - normalize(vector, result) { - const length = Math.sqrt((vector[0] * vector[0]) + (vector[1] * vector[1]) + (vector[2] * vector[2])) - result[0] = vector[0] / length - result[1] = vector[1] / length - result[2] = vector[2] / length - } + jointsResult[5][3] = thetaX + jointsResult[5][4] = thetaY + jointsResult[5][5] = thetaZ +} + +InverseKinematic.prototype.cross=function(vectorA, vectorB, result = []) { + result[0] = vectorA[1] * vectorB[2] - vectorA[2] * vectorB[1] + result[1] = vectorA[2] * vectorB[0] - vectorA[0] * vectorB[2] + result[2] = vectorA[0] * vectorB[1] - vectorA[1] * vectorB[0] + return result +} + +InverseKinematic.prototype.dot=function(vectorA, vectorB) { + return vectorA[0] * vectorB[0] + vectorA[1] * vectorB[1] + vectorA[2] * vectorB[2] +} + +/** + * @param {array} vectorA angle from + * @param {array} vectorB angle to + * @param {array} referenceVector angle to set 0 degree from. coplanar with vecA and vecB + * @return {number} description + * @example angleBetween([1,0,0],[0,1,0],[0,0,1]) // PI/2 + */ +InverseKinematic.prototype.angleBetween=function(vectorA, vectorB, referenceVector) { + // angle = atan2(norm(cross(a, b)), dot(a, b)) + + const norm = this.length3(this.cross(vectorA, vectorB)) + + const angle = Math.atan2(norm, (vectorB[0] * vectorA[0] + vectorB[1] * vectorA[1] + vectorB[2] * vectorA[2])) + + const tmp = referenceVector[0] * vectorA[0] + referenceVector[1] * vectorA[1] + referenceVector[2] * vectorA[2] + + const sign = (tmp > 0.0001) ? 1.0 : -1.0 + + return angle * sign +} + +InverseKinematic.prototype.length3=function(vector) { + return Math.sqrt(Math.pow(vector[0], 2) + Math.pow(vector[1], 2) + Math.pow(vector[2], 2)) +} + +InverseKinematic.prototype.length2=function(a, b) { + return Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)) +} + +InverseKinematic.prototype.angleBetween2=function(v1, v2) { + let angle + // turn vectors into unit vectors + // this.normalize(v1,v1) + // this.normalize(v2,v2) + // + // var angle = Math.acos(this.dot(v1, v2)) + // // if no noticable rotation is available return zero rotation + // // this way we avoid Cross product artifacts + // if (Math.abs(angle) < 0.0001) return 0 + // // in this case there are 2 lines on the same axis + // // // angle = atan2(norm(cross(a, b)), dot(a, b)) + const cross = this.cross(v1, v2) + + return Math.atan2(this.length3(cross), this.dot(v1, v2)) +} + +InverseKinematic.prototype.normalize=function(vector, result) { + const length = Math.sqrt((vector[0] * vector[0]) + (vector[1] * vector[1]) + (vector[2] * vector[2])) + result[0] = vector[0] / length + result[1] = vector[1] / length + result[2] = vector[2] / length +} + +InverseKinematic.prototype.addSphere=function(name, position, color = 0xffff00, diameter = 1) { + if (this.spheres.hasOwnProperty(name)) { + window.debug.scene.remove(this.spheres[name]) } - - function kinematic() { - return InverseKinematic + // if (!debug) return// Amira changed + if (!this.debug) return + + const geometry = new THREE.SphereGeometry(diameter, 32, 32) + const material = new THREE.MeshBasicMaterial({ + color, + }) + + this.spheres[name] = new THREE.Mesh(geometry, material) + this.spheres[name].position.set(position[0], position[1], position[2]) + window.debug.scene.add(this.spheres[name]) +} + +InverseKinematic.prototype.addArrow=function(name, from, to, color = 0xffff00, length = 10) { + if (this.arrows.hasOwnProperty(name)) { + window.debug.scene.remove(this.arrows[name]) } + if (!this.debug) return + const toPoint = new THREE.Vector3(to[0], to[1], to[2]) + const origin = new THREE.Vector3(from[0], from[1], from[2]) + // length = length || toPoint.sub(origin).length() + // toPoint.normalize() + this.arrows[name] = new THREE.ArrowHelper(toPoint.sub(origin).normalize(), origin, length, color, 2, 1) + window.debug.scene.add(this.arrows[name]) +} + +InverseKinematic.prototype.addVectorArrow=function(name, from, vector, color, length) { + this.addArrow(name, from, [from[0] + vector[0], from[1] + vector[1], from[2] + vector[2]], color, length) +} -// module.exports = InverseKinematic -// }) diff --git a/01_Code/physical_computing_interface/assembly/main.js b/01_Code/physical_computing_interface/assembly/assembly.js similarity index 100% rename from 01_Code/physical_computing_interface/assembly/main.js rename to 01_Code/physical_computing_interface/assembly/assembly.js diff --git a/01_Code/physical_computing_interface/assembly/index.html b/01_Code/physical_computing_interface/assembly/index.html index fedf4cec6f1ddb15eb5ff91b317cfaf3872e8d22..1fdfe7c4b6a6e3db8bee9fffce54cc17bb1881ea 100644 --- a/01_Code/physical_computing_interface/assembly/index.html +++ b/01_Code/physical_computing_interface/assembly/index.html @@ -18,7 +18,7 @@ <script src="./InverseKinematic.js"></script> <script src="./voxel.js"></script> - <script src="./main.js"></script> + <script src="./standAloneAssembly.js"></script> diff --git a/01_Code/physical_computing_interface/assembly/standAloneAssembly.js b/01_Code/physical_computing_interface/assembly/standAloneAssembly.js new file mode 100644 index 0000000000000000000000000000000000000000..d9be7bb364d95e46feb2547fba9b38897a01ba96 --- /dev/null +++ b/01_Code/physical_computing_interface/assembly/standAloneAssembly.js @@ -0,0 +1,2231 @@ +// Amira Abdel-Rahman +// (c) Massachusetts Institute of Technology 2018 + +///////////////////////////globals/////////////////////////////// + + +///////////////////////////THREE/////////////////////////////// +var container, camera, scene, renderer; + +var guiControls =[]; +var geometryGui,targetGui,jointsGui, jointsParams, targetParams, geometryParams,end, currentStep; + +var DEG_TO_RAD = Math.PI / 180; +var RAD_TO_DEG = 180 / Math.PI; + +///////////////////////////voxels/////////////////////////////// +var voxel; +var voxelSpacing=1.0; +var voxelLocations=[]; +var voxelSlices=[]; +var voxelSlicesCount=[]; + +var currentVoxelCount=0; +var build=[]; + +var globalZ=0; +var globalRank=0; + +var voxelNum=0; +var gridSize=6+4;//change 10 to changecube size + +var stepsCap=100; //change with very big simulations + +var grid=[]; + + +///////////////////////////robot/////////////////////////////// +var numberOfRobots=1; //change number of robots +var speed=20; //change to speed up the simulation +var THREE1=[]; +var robotBones = []; +var joints = []; +var angles = []; +var IK=[]; + +var robotState=[]; + +var THREERobot; +var VisualRobot= []; +var THREESimulationRobot= []; +var geo=[]; +var defaultRobotState= []; +var target = []; +var control= []; +var leg= []; + +var voxelNormal= []; +var normalAdjustmentVector= []; + +var startLocations= []; + +var THREE1dir= []; +var THREE1Pos= []; + +//path +var targetPositionMesh= []; +var carriedVoxel= []; + +var goHome= []; + +////////////////////// + +var steps=[]; +var path= []; + +var totalNumberofSteps=[]; + +var locations=[] + + +///////////////////////function calls/////////////////////////////// +declareGlobals(); +init(); +// frepVoxel(10,10,10,"");//later change string for frep + + +for( i=0;i<numberOfRobots;i++) +{ + setupGUI(i); + THREERobotCall(i); + defaultRobot(i); + buildHelperMeshes(i); + assemble(i); + +} + +///////////////////////////////scene/////////////////////////////// +function declareGlobals(){ + for( i=0;i<numberOfRobots;i++) + { + guiControls.push(null); + + THREE1.push(new THREE.Vector3(0,0,0) ); + robotBones.push([]); + joints.push([]); + angles.push([0, 0, 0, 0, 0, 0]); + robotState.push({ + leg1Pos:new THREE.Vector3(1,0,0), + leg2Pos:new THREE.Vector3(0,0,0), + up:new THREE.Vector3(0,0,1), + forward:new THREE.Vector3(1,0,0), + Z:0, + rank:0 + }); + + // THREERobot.push(null); + VisualRobot.push(null); + THREESimulationRobot.push(null); + geo.push(null); + defaultRobotState.push(null); + target.push(null); + control.push(null); + leg.push(1); + voxelNormal.push(180); + normalAdjustmentVector.push(new THREE.Vector3( 0, 0, 0)); + startLocations.push(new THREE.Vector3(0*voxelSpacing,10*voxelSpacing,0) ); + THREE1dir.push(new THREE.Vector3(1,0,0) ); + THREE1Pos.push(new THREE.Vector3(0,0,0) ); + + //path + targetPositionMesh.push(startLocations[i]); + carriedVoxel.push(null); + + goHome.push(false); + totalNumberofSteps.push(0); + + ////////////////////// + + steps.push([]); + path.push({ + curve: null, + currentPoint: 0, + points:[], + number:20, + delay:1000/speed, + timeout:0, + cHeight:5.0*voxelSpacing, + showPath:true, + normals:[], + changeLegs:[], + changeRotation:[], + normalAdjustments:[] + }); + IK.push([]); + + } + //starting positions and pickup stations + if(numberOfRobots>1){ + robotState[1].leg1Pos=new THREE.Vector3((gridSize-1),(gridSize-2),0); + robotState[1].leg2Pos=new THREE.Vector3((gridSize-1),(gridSize-1),0); + startLocations[1]=new THREE.Vector3((gridSize-1)*voxelSpacing,(gridSize-1)*voxelSpacing,0*voxelSpacing); + + } + if(numberOfRobots>2){ + robotState[2].leg1Pos=new THREE.Vector3((gridSize-1),1,0); + robotState[2].leg2Pos=new THREE.Vector3((gridSize-1),0,0); + startLocations[2]=new THREE.Vector3((gridSize-1)*voxelSpacing,0*voxelSpacing,0*voxelSpacing); + + } + if(numberOfRobots>3){ + robotState[3].leg1Pos=new THREE.Vector3(1,gridSize-1,0); + robotState[3].leg2Pos=new THREE.Vector3(0,gridSize-1,0); + startLocations[3]=new THREE.Vector3(0*voxelSpacing,(gridSize-1)*voxelSpacing,0*voxelSpacing); + } + +} + +function init() { + container = document.getElementById( 'webgl' ); + + renderer = new THREE.WebGLRenderer({ + antialias: true, // to get smoother output + preserveDrawingBuffer: false, // no screenshot -> faster? + }); + renderer.setClearColor(0x333333); + + renderer.setSize(window.innerWidth, window.innerHeight) + container.appendChild( renderer.domElement ); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 10000) + + camera.up.set(0, 0, 0.5); + camera.position.set(75, 75, 75); + scene.add(camera); + + // lights + var light = new THREE.AmbientLight(0xaaaaaa); + scene.add(light); + var light2 = new THREE.DirectionalLight(0xaaaaaa); + light2.position.set(1, 1.3, 1).normalize(); + scene.add(light2); + + cameraControls = new THREE.OrbitControls(camera, renderer.domElement); + cameraControls.addEventListener('change', () => renderer.render(scene, camera)); + + function onWindowResize() + { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.render(scene, camera); + } + + window.addEventListener('resize', onWindowResize, false); + + var size = 20; + var step = 20; + + var gridHelper = new THREE.GridHelper(size, step); + gridHelper.rotation.x = Math.PI / 2; + scene.add(gridHelper); + + var axisHelper = new THREE.AxesHelper(5); + var colors = axisHelper.geometry.attributes.color.array; + + colors.set( [ + 0, 1, 0, 0, 1, 0, // x-axis rgb at origin; rgb at end of segment + 1, 0, 0, 1, 0, 0, // y-axis + 0, 0, 1, 0, 0, 1 // z-axis + ] ); + + scene.add(axisHelper); + animate(); + + ////////////////////load voxel///////////////////////// + loadVoxel(); +} + +function animate() { + requestAnimationFrame( animate ); + render(); +} + +function render() { + renderer.render(scene, camera); + +} + +///////////////////////////geometry/////////////////////////////// +//implemented based on https://github.com/glumb/robot-gui +function THREERobotCall(robotIndex) { + THREERobot= function (robotIndex,V_initial, limits, scene) { + + THREE1[robotIndex] = new THREE.Group(); + + var colors = [ + 0xaaaaaa, + 0xbbbbbb, + 0xbcbcbc, + 0xcbcbcb, + 0xcccccc, + 0x0, + ]; + + let parentObject = THREE1[robotIndex]; + robotBones[robotIndex] = []; + joints[robotIndex] = []; + + function createCube(x, y, z, w, h, d, min, max, jointNumber) { + var thicken = 1; + + var w_thickened = Math.abs(w) + thicken; + var h_thickened = Math.abs(h) + thicken; + var d_thickened = Math.abs(d) + thicken; + + var material = new THREE.MeshLambertMaterial({ color: colors[jointNumber],}); + var geometry = new THREE.CubeGeometry(w_thickened, h_thickened, d_thickened); + var mesh = new THREE.Mesh(geometry, material); + + + + mesh.position.set(w / 2, h / 2, d / 2); + var group = new THREE.Object3D(); + group.position.set(x, y, z); + group.add(mesh); + + // min = min / 180 * Math.PI + // max = max / 180 * Math.PI + + var jointGeo1 = new THREE.CylinderGeometry(0.8, 0.8, 0.8 * 2, 32, 32, false, -min, 2 * Math.PI - max + min) + var jointGeoMax = new THREE.CylinderGeometry(0.8, 0.8, 0.8 * 2, 32, 32, false, -max, max) + var jointGeoMin = new THREE.CylinderGeometry(0.8, 0.8, 0.8 * 2, 32, 32, false, 0, -min) + var jointMesh1 = new THREE.Mesh(jointGeo1, new THREE.MeshBasicMaterial({ + color: 0x000000,//0xffbb00, + })); + var jointMeshMax = new THREE.Mesh(jointGeoMax, new THREE.MeshBasicMaterial({ + color: 0x000000,//0x009900, + })); + var jointMeshMin = new THREE.Mesh(jointGeoMin, new THREE.MeshBasicMaterial({ + color: 0x000000,//0xdd2200, + })); + + var joint = new THREE.Group(); + joint.add(jointMeshMax, jointMeshMin, jointMesh1); + + joints[robotIndex].push(joint); + + switch (jointNumber) { + // case 0: + // joint.rotation.x = Math.PI / 2 + // break + // case 1: + // joint.rotation.z = Math.PI / 2 //changed + // // joint.rotation.y = Math.PI / 2 //changed + // break + // case 2: + // joint.rotation.z = Math.PI / 2 //changed + // // joint.rotation.y = Math.PI / 2 //changed + // // joint.rotation.x = Math.PI / 2 + // break + // case 3: + // joint.rotation.z = Math.PI / 2 //changed + // // joint.rotation.y = Math.PI + // break + // case 4: + // joint.rotation.x = Math.PI / 2 //changed + // // joint.rotation.x = Math.PI / 2 + // // joint.rotation.y = Math.PI / 2 + // break + // case 5: + // joint.rotation.x = Math.PI / 2 + // group.rotation.y = Math.PI / 2 + // break; + case 0: + joint.rotation.x = Math.PI / 2 + break + case 1: + // joint.rotation.x = Math.PI / 2 + break + case 2: + // joint.rotation.x = Math.PI / 2 + break + case 3: + joint.rotation.z = Math.PI / 2 + // joint.rotation.y = Math.PI + break + case 4: + // joint.rotation.x = Math.PI / 2 + joint.rotation.y = Math.PI / 2 + break + case 5: + joint.rotation.x = Math.PI / 2 + group.rotation.y = Math.PI / 2 + break; + } + + group.add(joint); + return group; + } + + let x = 0, + y = 0, + z = 0; + + V_initial.push([0, 0, 0]) // add a 6th pseudo link for 6 axis + + for (let i = 0; i < V_initial.length; i++) { + var link = V_initial[i]; + + + + var linkGeo = createCube(x, y, z, link[0], link[1], link[2], limits[i][0], limits[i][1], i); + x = link[0]; + y = link[1]; + z = link[2]; + + if(i==4)//todo change later + { + + carriedVoxel[robotIndex]=voxel.clone(); + carriedVoxel[robotIndex].position.x=x+2.5*voxelSpacing; + carriedVoxel[robotIndex].position.y=y; + carriedVoxel[robotIndex].position.z=z ; + linkGeo.add( carriedVoxel[robotIndex] ); + } + + parentObject.add(linkGeo); + parentObject = linkGeo; + robotBones[robotIndex].push(linkGeo); + } + + scene.add(THREE1[robotIndex]); + }; + + THREERobot.prototype = { + setAngles(robotIndex,angles1) { + // robotBones[robotIndex][0].rotation.z = angles[robotIndex][0]; + // robotBones[robotIndex][1].rotation.y = angles[robotIndex][1]; + // robotBones[robotIndex][2].rotation.y = angles[robotIndex][2]; + // robotBones[robotIndex][3].rotation.y = angles[robotIndex][3]; + // robotBones[robotIndex][4].rotation.z = angles[robotIndex][4]; + // robotBones[robotIndex][5].rotation.y = angles[robotIndex][5]; + + robotBones[robotIndex][0].rotation.z = angles[robotIndex][0]; + robotBones[robotIndex][1].rotation.y = angles[robotIndex][1]; + robotBones[robotIndex][2].rotation.y = angles[robotIndex][2]; + robotBones[robotIndex][3].rotation.x = angles[robotIndex][3]; + robotBones[robotIndex][4].rotation.y = angles[robotIndex][4]; + robotBones[robotIndex][5].rotation.z = angles[robotIndex][5]; + + // robotBones[robotIndex][0].rotation.z =angles[robotIndex][0]; + // robotBones[robotIndex][1].rotation.x =angles[robotIndex][1]; + // robotBones[robotIndex][2].rotation.x =angles[robotIndex][2]; + // robotBones[robotIndex][3].rotation.x =angles[robotIndex][3]; + // robotBones[robotIndex][4].rotation.z =angles[robotIndex][4]; + // robotBones[robotIndex][5].rotation.z =angles[robotIndex][5]; + }, + + setAngle(robotIndex,index, angle) { + angles[robotIndex][index] = angle; + this.setAngles(robotIndex,angles[robotIndex].slice()); + }, + }; +} + +///////////////////////initialization///////////////////////////// +function defaultRobot(robotIndex) { + localState = { + jointOutOfBound: [false, false, false, false, false, false], + }; + var maxAngleVelocity = 90.0 / (180.0 * Math.PI) / 1000.0; + + d1 = 0.1273; + a2 = -0.612; + a3 = -0.5723; + d4 = 0.163941; + d5 = 0.1157; + d6 = 0.0922; + + shoulder_offset = 0.220941; + elbow_offset = -0.1719; + + var scale=0.01; + shoulder_height =scale* d1; + upper_arm_length =scale* -a2; + forearm_length =scale* -a3; + wrist_1_length =scale* d4 - elbow_offset - shoulder_offset; + wrist_2_length =scale* d5; + wrist_3_length =scale* d6; + geo = [ + // [scale*200, 0, scale*50], + // [-scale*100, 0, scale*600], + // [0, 0, scale*600], + // [scale*200, 0,0], + // [0, 0, scale*200], + // [scale*200, 0, 0], + [scale*200, 0, scale*100], + [0, 0, scale*800], + [0, 0,scale*200], + [scale*800, 0, 0], + [scale*200, 0, 0], + [scale*200, 0, 0], + + ]; + defaultRobotState[robotIndex] = { + target: { + position: { + x: startLocations[robotIndex].x, + y: startLocations[robotIndex].y, + z: startLocations[robotIndex].z, + }, + rotation: { + x: Math.PI, + y: 0, + z: 0, + }, + }, + angles: { + A0: 0, + A1: 0, + A2: 0, + A3: 0, + A4: 0, + A5: 0, + }, + jointOutOfBound: [false, false, false, false, false, false], + maxAngleVelocities: { + J0: maxAngleVelocity, + J1: maxAngleVelocity, + J2: maxAngleVelocity, + J3: maxAngleVelocity, + J4: maxAngleVelocity, + J5: maxAngleVelocity, + }, + jointLimits: { + J0: [-190 / 180 * Math.PI, 190 / 180 * Math.PI], + J1: [-90 / 180 * Math.PI, 90 / 180 * Math.PI], + J2: [-135 / 180 * Math.PI, 45 / 180 * Math.PI], + J3: [-90 / 180 * Math.PI, 75 / 180 * Math.PI], + J4: [-139 / 180 * Math.PI, 90 / 180 * Math.PI], + J5: [-188 / 180 * Math.PI, 181 / 180 * Math.PI], + }, + configuration: [false, false, false], + geometry: { + V0: { + x: geo[0][0], + y: geo[0][1], + z: geo[0][2], + }, + V1: { + x: geo[1][0], + y: geo[1][1], + z: geo[1][2], + }, + V2: { + x: geo[2][0], + y: geo[2][1], + z: geo[2][2], + }, + V3: { + x: geo[3][0], + y: geo[3][1], + z: geo[3][2], + }, + V4: { + x: geo[4][0], + y: geo[4][1], + z: geo[4][2], + }, + }, + }; + + THREESimulationRobot[robotIndex] = new THREE.Group(); + scene.add(THREESimulationRobot[robotIndex]); + + //////////////// + + ////////////////// + + updateIK(robotIndex); + + console.log(defaultRobotState[robotIndex].angles); + + var geometry = Object.values(defaultRobotState[robotIndex].geometry).map((val, i, array) => [val.x, val.y, val.z]); + var jointLimits = Object.values(defaultRobotState[robotIndex].jointLimits); + + VisualRobot[robotIndex] = new THREERobot(robotIndex,geometry, jointLimits, THREESimulationRobot[robotIndex]); + // var angles = Object.values(defaultRobotState.angles); + // VisualRobot[robotIndex].setAngles(angles); + var restAngle=60; + // VisualRobot[robotIndex].setAngle(0,restAngle/ 180 * Math.PI); + VisualRobot[robotIndex].setAngle(robotIndex,1,restAngle*RAD_TO_DEG); + VisualRobot[robotIndex].setAngle(robotIndex,2,restAngle*RAD_TO_DEG); + VisualRobot[robotIndex].setAngle(robotIndex,3,restAngle*RAD_TO_DEG); + VisualRobot[robotIndex].setAngle(robotIndex,4,restAngle*RAD_TO_DEG); + VisualRobot[robotIndex].setAngle(robotIndex,5,restAngle*RAD_TO_DEG); + + // var target= new THREE.Vector3(guiControls[robotIndex].x,guiControls[robotIndex].y,guiControls[robotIndex].z); + // updateGUI(); + + targetControl(robotIndex); + THREE1[robotIndex].position.x=0; + THREE1[robotIndex].position.y=0; + THREE1[robotIndex].position.z=0; + updateAngles(robotIndex); + +} + +function updateRobotGeometry1(robotIndex){ + geo = [ + [2.5 + 2.3, 0, 7.3], + [0, 0, 13.0], + [1, 0, 2], + [12.6, 0, 0], + [3.6, 0, 0], + [0, 0, 0], + ]; + + defaultRobotState[robotIndex].geometry ={ + V0: { + x: geo[0][0], + y: geo[0][1], + z: geo[0][2], + }, + V1: { + x: geo[1][0], + y: geo[1][1], + z: geo[1][2], + }, + V2: { + x: geo[2][0], + y: geo[2][1], + z: geo[2][2], + }, + V3: { + x: geo[3][0], + y: geo[3][1], + z: geo[3][2], + }, + V4: { + x: geo[4][0], + y: geo[4][1], + z: geo[4][2], + }, + }; + + updateIK(robotIndex); + + var geometry = Object.values(defaultRobotState[robotIndex].geometry).map((val, i, array) => [val.x, val.y, val.z]); + var jointLimits = Object.values(defaultRobotState[robotIndex].jointLimits); + + while (THREESimulationRobot[robotIndex].children.length) { + THREESimulationRobot[robotIndex].remove(THREESimulationRobot[robotIndex].children[0]); + } + + VisualRobot[robotIndex] = new THREERobot(robotIndex,geometry, jointLimits, THREESimulationRobot[robotIndex]); + + updateAngles(robotIndex); +} + + +////////////////////////////////GUI/////////////////////////////// +function setupGUI(robotIndex){ + //Parameters that can be modified. + guiControls[robotIndex] = new function() { + this.x = startLocations[robotIndex].x; + this.y = startLocations[robotIndex].y; + this.z = startLocations[robotIndex].z; + this.j1 = 0.0; + this.j2 = 0.0; + this.j3 = 0.0; + this.j4 = 0.0; + this.j5 = 0.0; + this.j6 = 0.0; + this.leg1 = 5.0; + this.leg2 = 5.0; + this.offset = 2.0; + this.targetEnd="end 1"; + this.step=0; + }; + + var gui = new dat.GUI(); + + geometryGui = gui.addFolder('Robot Geometry'); + geometryParams=[]; + geometryParams.push(geometryGui.add(guiControls[robotIndex], 'leg1', 1.00, 10.0).step(0.1).listen()); + geometryParams.push(geometryGui.add(guiControls[robotIndex], 'leg2', 1.00, 10.0).step(0.1).listen()); + geometryParams.push(geometryGui.add(guiControls[robotIndex], 'offset', 1.00, 10.0).step(0.1).listen()); + + jointsGui = gui.addFolder('Robot Joints'); + jointsParams=[]; + jointsParams.push(jointsGui.add(guiControls[robotIndex], 'j1', 0.00, 360.0).step(0.5).listen()); + jointsParams.push(jointsGui.add(guiControls[robotIndex], 'j2', 0.00, 360.0).step(0.5).listen()); + jointsParams.push(jointsGui.add(guiControls[robotIndex], 'j3', 0.00, 360.0).step(0.5).listen()); + jointsParams.push(jointsGui.add(guiControls[robotIndex], 'j4', 0.00, 360.0).step(0.5).listen()); + jointsParams.push(jointsGui.add(guiControls[robotIndex], 'j5', 0.00, 360.0).step(0.5).listen()); + + targetGui = gui.addFolder('Target'); + targetParams=[]; + targetParams.push(targetGui.add(guiControls[robotIndex], 'x', -10.0, 10.0).step(0.1).listen()); + targetParams.push(targetGui.add(guiControls[robotIndex], 'y', -10.0, 10.0).step(0.1).listen()); + targetParams.push(targetGui.add(guiControls[robotIndex], 'z', -10.0, 10.0).step(0.1).listen()); + + var endtarget= gui.addFolder('Target End'); + end=endtarget.add(guiControls[robotIndex],'targetEnd',["end 1","end 2"]).listen(); + + currentStep=gui.add(guiControls[robotIndex], 'step', 0, path[robotIndex].number).step(1.0).listen(); + + +} + +function updateGUI(robotIndex){ + for (var i=0;i<targetParams.length;i++) { + targetParams[i].onChange(function(value) { + updateAngles(robotIndex); + control[robotIndex].position.x = guiControls[robotIndex].x; + control[robotIndex].position.y = guiControls[robotIndex].y; + control[robotIndex].position.z = guiControls[robotIndex].z; + target[robotIndex].position.x = guiControls[robotIndex].x; + target[robotIndex].position.y = guiControls[robotIndex].y; + target[robotIndex].position.z = guiControls[robotIndex].z; + }); + } + + for (var i=0;i<jointsParams.length;i++) { + jointsParams[i].onChange(function(value) { + if(leg[robotIndex]==1) + { + VisualRobot[robotIndex].setAngle(robotIndex,0,guiControls[robotIndex].j1*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,1,guiControls[robotIndex].j2*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,2,guiControls[robotIndex].j3*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,3,guiControls[robotIndex].j4*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,4,guiControls[robotIndex].j5*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,5,guiControls[robotIndex].j6*DEG_TO_RAD); + + }else + { + VisualRobot[robotIndex].setAngle(robotIndex,5,guiControls[robotIndex].j1*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,4,guiControls[robotIndex].j2*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,3,guiControls[robotIndex].j3*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,2,guiControls[robotIndex].j4*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,1,guiControls[robotIndex].j5*DEG_TO_RAD); + VisualRobot[robotIndex].setAngle(robotIndex,0,guiControls[robotIndex].j6*DEG_TO_RAD); + + + } + + updateTarget(robotIndex); + }); + } + + for (var i=0 ;i<geometryParams.length ;i++) { + geometryParams[i].onChange(function(value) { + updateRobotGeometry(robotIndex); + }); + } + + currentStep.onChange(function(value) { + step(robotIndex,guiControls[robotIndex].step); + }); + + end.onChange(function(value) { + + changeEnd(robotIndex); + }); + + +} + +///////////////////////Inverse Kinematics//////////////////////// +function updateIK(robotIndex) { + const geo = Object.values(defaultRobotState[robotIndex].geometry).map((val, i, array) => [val.x, val.y, val.z]) + // todo not optimal, since IK is a sideeffect + IK[robotIndex] = new InverseKinematic(geo); +} + +function updateAngles(robotIndex){ + // const calculateAngles = (jointLimits, position, rotation, configuration) => { + const angles = [] + IK[robotIndex].calculateAngles( + guiControls[robotIndex].x, + guiControls[robotIndex].y, + guiControls[robotIndex].z +2.5*voxelSpacing, + defaultRobotState[robotIndex].target.rotation.x, + defaultRobotState[robotIndex].target.rotation.y, + defaultRobotState[robotIndex].target.rotation.z, + angles, + defaultRobotState[robotIndex].target.configuration + ) + outOfBounds = [false, false, false, false, false, false] + // let i = 0 + // for (const index in jointLimits) { + // if (angles[i] < jointLimits[index][0] || angles[i] > jointLimits[index][1]) { + // outOfBounds[i] = true + // } + // i++ + // } + VisualRobot[robotIndex].setAngle(robotIndex,0,angles[0]); + VisualRobot[robotIndex].setAngle(robotIndex,1,angles[1]); + VisualRobot[robotIndex].setAngle(robotIndex,2,angles[2]); + VisualRobot[robotIndex].setAngle(robotIndex,3,angles[3]); + VisualRobot[robotIndex].setAngle(robotIndex,4,angles[4]); + VisualRobot[robotIndex].setAngle(robotIndex,5,angles[5]); + + angles[robotIndex][0]=angles[0]; + angles[robotIndex][1]=angles[1]; + angles[robotIndex][2]=angles[2]; + angles[robotIndex][3]=angles[3]; + angles[robotIndex][4]=angles[4]; + angles[robotIndex][5]=angles[5]; + + guiControls[robotIndex].j1=angles[robotIndex][0]*RAD_TO_DEG; + guiControls[robotIndex].j2=angles[robotIndex][1]*RAD_TO_DEG; + guiControls[robotIndex].j3=angles[robotIndex][2]*RAD_TO_DEG; + guiControls[robotIndex].j4=angles[robotIndex][3]*RAD_TO_DEG; + guiControls[robotIndex].j5=angles[robotIndex][4]*RAD_TO_DEG; + guiControls[robotIndex].j6=angles[robotIndex][5]*RAD_TO_DEG; + return angles; + +} + +function getNormalAdjustment(robotIndex,n,vnormal,forward){//n is normal degree + + var result=new THREE.Vector3(0,0,0); + if(n==180) + { + return result; + } + var theta=Math.abs(180-n); + var base=2*Math.sin(theta/2*DEG_TO_RAD)*guiControls[robotIndex].offset; + var x= Math.sin(((180-theta)/2)*DEG_TO_RAD)*base; + var y= Math.cos(((180-theta)/2)*DEG_TO_RAD)*base; + + result= vnormal.clone().multiplyScalar(-y); + + if(n > 180) + { + var tempV=forward.clone().multiplyScalar(x); + result.add(tempV); + return result; + }else + { + var tempV=forward.clone().multiplyScalar(-x); + result.add(tempV); + return result; + } + +} + +////////////////////////Taget Control//////////////////////////// +function targetControl(robotIndex){ + target[robotIndex] = new THREE.Group(); + scene.add(target[robotIndex]); + + control[robotIndex] = new THREE.TransformControls(camera, renderer.domElement); + target[robotIndex].position.x = guiControls[robotIndex].x; + target[robotIndex].position.y = guiControls[robotIndex].y; + target[robotIndex].position.z = guiControls[robotIndex].z; + + control[robotIndex].size=0.5; + control[robotIndex].space = "local"; + target[robotIndex].rotation.y=180*DEG_TO_RAD; + target[robotIndex].rotation.z=90*DEG_TO_RAD; + // control[robotIndex].setSpace( control[robotIndex].space === "local" ? "world" : "local" ); + control[robotIndex].addEventListener('change', () => { + guiControls[robotIndex].x= target[robotIndex].position.x; + guiControls[robotIndex].y= target[robotIndex].position.y; + guiControls[robotIndex].z= target[robotIndex].position.z; + defaultRobotState[robotIndex].target.position.x=target[robotIndex].position.x; + defaultRobotState[robotIndex].target.position.y=target[robotIndex].position.y; + defaultRobotState[robotIndex].target.position.z=target[robotIndex].position.z; + // defaultRobotState[robotIndex].target.rotation.x + // defaultRobotState[robotIndex].target.rotation.y + // defaultRobotState[robotIndex].target.rotation.z + // console.log("here") + updateAngles(robotIndex); + }); + control[robotIndex].attach(target[robotIndex]); + + scene.add(control[robotIndex]); + // control[robotIndex].visible = false; +} + +function updateTarget(robotIndex){ + console.log("heree") + var tempPosition=new THREE.Vector3(0,0,0); + + parent.updateMatrixWorld(); + + var vector = new THREE.Vector3(); + vector.setFromMatrixPosition( child.matrixWorld ); + + + + var object=THREE1[robotIndex].children[0].children[2].children[2].children[2].children[2]; + object.updateMatrixWorld(); + var vector = object.geometry.vertices[i].clone(); + vector.applyMatrix4( object.matrixWorld ); + + tempPosition.x=THREE1[robotIndex].parent.parent.children[0].position.x; + tempPosition.y=THREE1[robotIndex].parent.parent.children[0].position.y; + tempPosition.z=THREE1[robotIndex].parent.parent.children[0].position.z; + guiControls[robotIndex].x=tempPosition.x; + guiControls[robotIndex].y=tempPosition.y; + guiControls[robotIndex].z=tempPosition.z; + + control[robotIndex].position.x = guiControls[robotIndex].x; + control[robotIndex].position.y = guiControls[robotIndex].y; + control[robotIndex].position.z = guiControls[robotIndex].z; + + target[robotIndex].position.x = guiControls[robotIndex].x; + target[robotIndex].position.y = guiControls[robotIndex].y; + target[robotIndex].position.z = guiControls[robotIndex].z; +} + +///////////////////////assembly///////// +function assemble(robotIndex){ + resetPath(robotIndex); + builtShape(); + generatePoints(robotIndex); + + // steps[robotIndex]=[startLocations[robotIndex],new THREE.Vector3(10*voxelSpacing,0*voxelSpacing,0)]; + // showTargetPosition(robotIndex,minIndex,true); + + for(var i=0;i<steps[robotIndex].length;i++){ + if(i%2!=0){ + moveRobot(robotIndex,true,steps[robotIndex][i]); + }else{ + moveRobot(robotIndex,false,steps[robotIndex][i+1]); + } + + } + +} + +function createPath(robotIndex,start,end){ + var snormal=new THREE.Vector3(0,0,1); + var enormal=new THREE.Vector3(0,0,1); + var robotUp=new THREE.Vector3(0,0,1); + var p1=start.clone(); + p1.add(snormal.clone().multiplyScalar(0)); + var p2=new THREE.Vector3(0,0,0); + var p3=new THREE.Vector3(0,0,0); + var p4=end.clone(); + p4.add(enormal.clone().multiplyScalar(0)); + + var nor = snormal.clone(); + nor.add(enormal); + nor.normalize(); + + var dir=end.clone().sub(start); + + var temp1=new THREE.Vector3(0,0,0); + var temp2=new THREE.Vector3(0,0,0); + nor.multiplyScalar(path[robotIndex].cHeight); + + + temp1.addVectors(start,dir.multiplyScalar(1/3)); + temp2.addVectors(start,dir.multiplyScalar(2)); + + + p2.addVectors(nor,temp1); + p3.addVectors(nor,temp2); + + //create bezier curve + path[robotIndex].curve= new THREE.CubicBezierCurve3 ( + p1, + p2, + p3, + p4 + ); + dividePath(robotIndex,snormal.clone(),enormal.clone()); +} + +function dividePath(robotIndex,snormal,enormal,robotUp,robotLocation){ + + var snormal=new THREE.Vector3(0,0,1); + var enormal=new THREE.Vector3(0,0,1); + var robotUp=new THREE.Vector3(0,0,1); + var robotLocation=new THREE.Vector3(0,0,1); + + //points + var d=1/path[robotIndex].number; + var tempPoints=path[robotIndex].curve.getSpacedPoints(path[robotIndex].number); + + var forward=new THREE.Vector3(0,0,0); + var tempV=tempPoints[0].clone().sub(robotLocation); + if(tempV.x!=0 && robotUp.x==0) + { + forward.x=tempV.x; + }else if(tempV.y!=0 && robotUp.y==0) + { + forward.y=tempV.y; + }else if(tempV.z!=0 && robotUp.z==0) + { + forward.z=tempV.z; + } + forward.normalize(); + + var p1=tempPoints[0]; + var p2=tempPoints[path[robotIndex].number-1]; + var diff=p2.clone().sub(p1); + diff.multiply(snormal); + + //normals + var vnormal1=180; + var vnormal2=180; + if(!snormal.equals(enormal)) + { + + if(diff.x>0||diff.y>0||diff.z>0) + { + if(robotUp.equals(snormal)) + { + vnormal1=180; + vnormal2=90; + + }else + { + vnormal2=180; + vnormal1=90; + + } + + }else if(diff.x<0||diff.y<0||diff.z<0) + { + if(robotUp.equals(snormal)) + { + vnormal1=180; + vnormal2=180+90; + + }else + { + vnormal2=180; + vnormal1=180+90; + + } + } + } + var dn=(vnormal2-vnormal1)/path[robotIndex].number; + + + + for (var i=0;i<=path[robotIndex].number;i++) + { + path[robotIndex].normals.push(vnormal1+i*dn); + path[robotIndex].normalAdjustments.push(getNormalAdjustment(robotIndex,vnormal1+i*dn,robotUp,forward)); + path[robotIndex].points.push(tempPoints[i].clone()); + + + if(path[robotIndex].showPath) + { + var material = new THREE.MeshLambertMaterial({ color:0xff7171,}); + var geometry = new THREE.SphereGeometry(0.05, 0.05, 0.05); + var mesh = new THREE.Mesh(geometry, material); + mesh.position.x=tempPoints[i].x; + mesh.position.y=tempPoints[i].y; + mesh.position.z=tempPoints[i].z; + scene.add(mesh); + } + } + +} + +function moveRobot(robotIndex,leaveVoxel,voxelLoc){ + var ps= path[robotIndex].points.slice(); + if(leaveVoxel){ + setTimeout(function(){ buildVoxelAt(voxelLoc);showTargetPosition(robotIndex,voxelLoc,false) }, path[robotIndex].timeout); + + + }else{ + setTimeout(function(){showTargetPosition(robotIndex,voxelLoc,true) }, path[robotIndex].timeout); + + } + for(var i=0;i<=path[robotIndex].number;i++) + { + setTimeout(function(){ move(robotIndex); }, path[robotIndex].timeout+=path[robotIndex].delay); + } + + + +} + +function move(robotIndex){ + guiControls[robotIndex].x=path[robotIndex].points[path[robotIndex].currentPoint].x; + guiControls[robotIndex].y=path[robotIndex].points[path[robotIndex].currentPoint].y; + guiControls[robotIndex].z=path[robotIndex].points[path[robotIndex].currentPoint].z; + voxelNormal[robotIndex]=path[robotIndex].normals[path[robotIndex].currentPoint]; + normalAdjustmentVector[robotIndex]=path[robotIndex].normalAdjustments[path[robotIndex].currentPoint]; + + updateAngles(robotIndex); + + control[robotIndex].position.x = guiControls[robotIndex].x; + control[robotIndex].position.y = guiControls[robotIndex].y; + control[robotIndex].position.z = guiControls[robotIndex].z; + + target[robotIndex].position.x = guiControls[robotIndex].x; + target[robotIndex].position.y = guiControls[robotIndex].y; + target[robotIndex].position.z = guiControls[robotIndex].z; + + path[robotIndex].currentPoint++; + if(path[robotIndex].currentPoint==path[robotIndex].points.length) + { + // if(goHome[robotIndex]) + // { + + // buildVoxelAt(robotState[robotIndex].leg1Pos.clone().multiplyScalar(voxelSpacing)); + // //go Home + // // console.log("robotIndex:"+robotIndex+" s2s"); + // startMovement(robotIndex,true); + + // }else + // { + // //go to new Voxel + // // console.log("robotIndex:"+robotIndex+" s2s"); + // startMovement(robotIndex,false); + // } + } + +} + +function builtShape(){ + for(var i=0;i<gridSize;i++) + { + var t=[]; + grid.push(t) + for(var j=0;j<gridSize;j++) + { + var tt=[]; + grid[i].push(tt); + for(var k=0;k<gridSize;k++) + { + grid[i][j].push(false); + } + } + } + for(var i=gridSize/2.0;i<gridSize;i++) + { + grid[i][0][0]=true; + locations.push(new THREE.Vector3(i*voxelSpacing,0,0)); //TODO CHANGE LATER BASED ON NUMBER OF ROBOTS + } + +} + +function generatePoints(robotIndex){ + for(var i=0;i<locations.length;i++){ + steps[robotIndex].push(startLocations[robotIndex]); + steps[robotIndex].push(locations[i]); + + } + console.log(steps[robotIndex]) + for(var i=0;i<steps[robotIndex].length-1;i++){ + createPath(robotIndex,steps[robotIndex][i],steps[robotIndex][i+1]); + } + + +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////Change Fixed End//////////////////////////// +function changeEnd(robotIndex){ + //TODO: send fix end and unfix end to code + if(leg[robotIndex]==2) + { + //later output package + // console.log("unfix leg 1, fix leg 2"); + leg[robotIndex]=1; + guiControls[robotIndex].targetEnd="end 1"; + }else + { + //later output package + // console.log("unfix leg 2, fix leg 1"); + leg[robotIndex]=2; + guiControls[robotIndex].targetEnd="end 2"; + } + + var tempPosition=new THREE.Vector3(0,0,0); + tempPosition.x=THREE1[robotIndex].position.x; + tempPosition.y=THREE1[robotIndex].position.y; + tempPosition.z=THREE1[robotIndex].position.z; + + THREE1[robotIndex].position.x=target[robotIndex].position.x; + THREE1[robotIndex].position.y=target[robotIndex].position.y; + THREE1[robotIndex].position.z=target[robotIndex].position.z; + + + guiControls[robotIndex].x=tempPosition.x; + guiControls[robotIndex].y=tempPosition.y; + guiControls[robotIndex].z=tempPosition.z; + + control[robotIndex].position.x = guiControls[robotIndex].x; + control[robotIndex].position.y = guiControls[robotIndex].y; + control[robotIndex].position.z = guiControls[robotIndex].z; + target[robotIndex].position.x = guiControls[robotIndex].x; + target[robotIndex].position.y = guiControls[robotIndex].y; + target[robotIndex].position.z = guiControls[robotIndex].z; + + updateAngles(robotIndex); + +} + +function rotateRobot(robotIndex,dir){ + + var newRotation=getRotation(dir).clone(); + THREE1[robotIndex].rotation.x=newRotation.x; + THREE1[robotIndex].rotation.y=newRotation.y; + THREE1[robotIndex].rotation.z=newRotation.z; + + voxelNormal[robotIndex]=path[robotIndex].normals[path[robotIndex].currentPoint+1]; + normalAdjustmentVector[robotIndex]=path[robotIndex].normalAdjustments[path[robotIndex].currentPoint+1]; + updateAngles(robotIndex); +} + +function getRotation(dir){ + var tempRot=new THREE.Vector3(); + if(dir.equals(new THREE.Vector3(0,0,1)) ) + { + tempRot=new THREE.Vector3(0,0,0); + } + else if (dir.equals( new THREE.Vector3(0,0,-1))) + { + tempRot=new THREE.Vector3(180*DEG_TO_RAD,0,0); + } + else if (dir.equals( new THREE.Vector3(0,-1,0))) + { + tempRot=new THREE.Vector3(90*DEG_TO_RAD,0,0); + } + else if (dir.equals( new THREE.Vector3(0,1,0))) + { + tempRot=new THREE.Vector3(-90*DEG_TO_RAD,0,0); + } + else if (dir.equals(new THREE.Vector3(-1,0,0))) + { + tempRot=new THREE.Vector3(0,-90*DEG_TO_RAD,0); + } + else if (dir.equals( new THREE.Vector3(1,0,0))) + { + tempRot=new THREE.Vector3(0,90*DEG_TO_RAD,0); + } + return tempRot; +} + +////////////////////////////////Path//////////////////////////// + + + +function _createPath(robotIndex,start,end,snormal,enormal,robotUp,robotLocation){ + var p1=start.clone(); + p1.add(snormal.clone().multiplyScalar(1)); + var p2=new THREE.Vector3(0,0,0); + var p3=new THREE.Vector3(0,0,0); + var p4=end.clone(); + p4.add(enormal.clone().multiplyScalar(1)); + + var nor = snormal.clone(); + nor.add(enormal); + nor.normalize(); + + var dir=end.clone().sub(start); + + var temp1=new THREE.Vector3(0,0,0); + var temp2=new THREE.Vector3(0,0,0); + nor.multiplyScalar(path[robotIndex].cHeight); + + + temp1.addVectors(start,dir.multiplyScalar(1/3)); + temp2.addVectors(start,dir.multiplyScalar(2)); + + + p2.addVectors(nor,temp1); + p3.addVectors(nor,temp2); + + //create bezier curve + path[robotIndex].curve= new THREE.CubicBezierCurve3 ( + p1, + p2, + p3, + p4 + ); + _dividePath(robotIndex,snormal.clone(),enormal.clone(),robotUp,robotLocation); +} + +function _dividePath(robotIndex,snormal,enormal,robotUp,robotLocation){ + + //points + var d=1/path[robotIndex].number; + var tempPoints=path[robotIndex].curve.getSpacedPoints(path[robotIndex].number); + + var forward=new THREE.Vector3(0,0,0); + var tempV=tempPoints[0].clone().sub(robotLocation); + if(tempV.x!=0 && robotUp.x==0) + { + forward.x=tempV.x; + }else if(tempV.y!=0 && robotUp.y==0) + { + forward.y=tempV.y; + }else if(tempV.z!=0 && robotUp.z==0) + { + forward.z=tempV.z; + } + forward.normalize(); + + var p1=tempPoints[0]; + var p2=tempPoints[path[robotIndex].number-1]; + var diff=p2.clone().sub(p1); + diff.multiply(snormal); + + //normals + var vnormal1=180; + var vnormal2=180; + if(!snormal.equals(enormal)) + { + + if(diff.x>0||diff.y>0||diff.z>0) + { + if(robotUp.equals(snormal)) + { + vnormal1=180; + vnormal2=90; + + }else + { + vnormal2=180; + vnormal1=90; + + } + + }else if(diff.x<0||diff.y<0||diff.z<0) + { + if(robotUp.equals(snormal)) + { + vnormal1=180; + vnormal2=180+90; + + }else + { + vnormal2=180; + vnormal1=180+90; + + } + } + } + var dn=(vnormal2-vnormal1)/path[robotIndex].number; + + + + for (var i=0;i<=path[robotIndex].number;i++) + { + path[robotIndex].normals.push(vnormal1+i*dn); + path[robotIndex].normalAdjustments.push(getNormalAdjustment(robotIndex,vnormal1+i*dn,robotUp,forward)); + path[robotIndex].points.push(tempPoints[i].clone()); + + + if(path[robotIndex].showPath) + { + var material = new THREE.MeshLambertMaterial({ color:0xff7171,}); + var geometry = new THREE.SphereGeometry(0.05, 0.05, 0.05); + var mesh = new THREE.Mesh(geometry, material); + mesh.position.x=tempPoints[i].x; + mesh.position.y=tempPoints[i].y; + mesh.position.z=tempPoints[i].z; + scene.add(mesh); + } + } + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +function _BilleStep(robotIndex,stepLeg1,stepLeg2,legsNormal){ + var changeLegBefore=false; + var leg1Pos=new THREE.Vector3(0,0,0); + var leg2Pos=new THREE.Vector3(0,0,0); + + var pos1=new THREE.Vector3(0,0,0); + var pos2=new THREE.Vector3(0,0,0); + + if(path[robotIndex].points.length==0) + { + pos1.copy(target[robotIndex].position); + pos2.copy(THREE1[robotIndex].position); + }else{ + pos1.copy(path[robotIndex].points[path[robotIndex].points.length-1-1-path[robotIndex].number]); + pos2.copy(path[robotIndex].points[path[robotIndex].points.length-1]); + + } + + //check starting leg based on distance to target + if(pos2.distanceTo(stepLeg1)<pos1.distanceTo(stepLeg1)) + { + changeLegBefore=true; + var temp=new THREE.Vector3(0,0,0); + temp.copy(pos1); + pos1.copy(pos2); + pos2.copy(temp); + } + + leg1Pos.copy(pos1); + leg2Pos.copy(pos2); + + // var robotUp=THREE1dir[robotIndex].clone(); + var robotUp=THREE1dir[robotIndex].clone(); + var robotLocation=THREE1Pos[robotIndex].clone(); + + _createPath(robotIndex,leg1Pos,stepLeg1,THREE1dir[robotIndex],legsNormal,robotUp,robotLocation); + + robotUp=legsNormal; + robotLocation=stepLeg1; + _createPath(robotIndex,leg2Pos,stepLeg2,THREE1dir[robotIndex],legsNormal,robotUp,robotLocation); + + //update previous locations/pos + THREE1dir[robotIndex] = legsNormal.clone(); + THREE1Pos[robotIndex]=stepLeg2.clone(); + + return [changeLegBefore,legsNormal]; +} + +function moveLeg(robotIndex){ + var ps= path[robotIndex].points.slice(); + + for(var i=0;i<=path[robotIndex].number;i++) + { + setTimeout(function(){ move(robotIndex); }, path[robotIndex].timeout+=path[robotIndex].delay); + } + setTimeout(function(){ changeEnd(robotIndex); }, path[robotIndex].timeout); + +} + +function _move(robotIndex){ + guiControls[robotIndex].x=path[robotIndex].points[path[robotIndex].currentPoint].x; + guiControls[robotIndex].y=path[robotIndex].points[path[robotIndex].currentPoint].y; + guiControls[robotIndex].z=path[robotIndex].points[path[robotIndex].currentPoint].z; + voxelNormal[robotIndex]=path[robotIndex].normals[path[robotIndex].currentPoint]; + normalAdjustmentVector[robotIndex]=path[robotIndex].normalAdjustments[path[robotIndex].currentPoint]; + updateAngles(robotIndex); + + control[robotIndex].position.x = guiControls[robotIndex].x; + control[robotIndex].position.y = guiControls[robotIndex].y; + control[robotIndex].position.z = guiControls[robotIndex].z; + + target[robotIndex].position.x = guiControls[robotIndex].x; + target[robotIndex].position.y = guiControls[robotIndex].y; + target[robotIndex].position.z = guiControls[robotIndex].z; + + path[robotIndex].currentPoint++; + if(path[robotIndex].currentPoint==path[robotIndex].points.length) + { + if(goHome[robotIndex]) + { + + buildVoxelAt(robotState[robotIndex].leg1Pos.clone().multiplyScalar(voxelSpacing)); + //go Home + // console.log("robotIndex:"+robotIndex+" s2s"); + startMovement(robotIndex,true); + + }else + { + //go to new Voxel + // console.log("robotIndex:"+robotIndex+" s2s"); + startMovement(robotIndex,false); + } + } + +} + +function getDirVec(direction){ + var vec = new THREE.Vector3(0,0,0); + + switch(direction) + { + case 0: + vec = new THREE.Vector3(1,0,0); + break; + case 1: + vec = new THREE.Vector3(-1,0,0); + break; + case 2: + vec = new THREE.Vector3(0,1,0); + break; + case 3: + vec = new THREE.Vector3(0,-1,0); + break; + } + return vec; + +} + +function resetPath(robotIndex){ + ////////////////////// + steps[robotIndex]=[]; + path[robotIndex].curve=null; + path[robotIndex].points=[]; + path[robotIndex].normals=[]; + path[robotIndex].changeRotation=[]; + path[robotIndex].normalAdjustments=[]; + path[robotIndex].currentPoint=0; + path[robotIndex].timeout=0; +} + +//////////////////////////commands stack//////////////////////////// +function _generatePoints(robotIndex){ + for (var i=0; i<steps[robotIndex].length ; i++) + { + var s=_BilleStep(robotIndex,steps[robotIndex][i][0],steps[robotIndex][i][1],steps[robotIndex][i][2]); + path[robotIndex].changeLegs.push(s[0]); + path[robotIndex].changeRotation.push(s[1]); + } +} + +function step(robotIndex,i){ + totalNumberofSteps[robotIndex]++; + // console.log("robot "+robotIndex +":"+totalNumberofSteps[robotIndex]); + // console.log(totalNumberofSteps); + if(path[robotIndex].changeLegs[i]) + { + setTimeout(function(){ changeEnd(robotIndex); }, path[robotIndex].timeout); + } + + moveLeg(robotIndex);//leg1 + + //rotate bill-e + if(i>0 && !path[robotIndex].changeRotation[i].equals(path[robotIndex].changeRotation[i-1])) + { + + setTimeout(function(){ rotateRobot(robotIndex,path[robotIndex].changeRotation[parseInt((path[robotIndex].currentPoint-path[robotIndex].number)/path[robotIndex].number/2)]); + }, path[robotIndex].timeout); + } + + moveLeg(robotIndex);//leg2 + +} + +function startMovement(robotIndex,home){ + // console.log("robot:"+robotIndex+" home:"+home); + + if(!home) + { + resetPath(robotIndex); + voxelBuilder(robotIndex,robotState[robotIndex].leg1Pos, robotState[robotIndex].leg2Pos, robotState[robotIndex].up,robotState[robotIndex].forward); + generatePoints(robotIndex); + + + if(steps[robotIndex].length>0) + { + for(var i=0;i<steps[robotIndex].length;i++) + { + step(robotIndex,i); + } + goHome[robotIndex]=true; + } + + + + //update z and rank + if(robotState[robotIndex].Z>globalZ) + { + globalZ=robotState[robotIndex].Z; + globalRank=robotState[robotIndex].rank; + }else if(robotState[robotIndex].Z<globalZ) + { + robotState[robotIndex].Z=globalZ; + robotState[robotIndex].rank=globalRank; + }else + { + if(robotState[robotIndex].rank>globalRank) + { + globalRank=robotState[robotIndex].rank; + }else if(robotState[robotIndex].rank<globalRank) + { + robotState[robotIndex].rank=globalRank; + } + } + + + if(voxelSlices[robotState[robotIndex].Z].length>0) + { + if(voxelSlicesCount[robotState[robotIndex].Z][robotState[robotIndex].rank] <voxelSlices[robotState[robotIndex].Z][robotState[robotIndex].rank].length-1) + { + voxelSlicesCount[robotState[robotIndex].Z][robotState[robotIndex].rank]++; + // console.log("robot:"+robotIndex+" f"+" home:"+home); + if(!goHome[robotIndex]) + { + startMovement(robotIndex,true); + } + } + else + { + if(robotState[robotIndex].rank<voxelSlicesCount[robotState[robotIndex].Z].length-1) + { + robotState[robotIndex].rank++; + // globalRank++; + // console.log("robot:"+robotIndex+" ff"+" home:"+home); + if(!goHome[robotIndex]) + { + startMovement(robotIndex,true); + } + } + else + { + if(robotState[robotIndex].Z<voxelSlicesCount.length-1) + { + robotState[robotIndex].Z++; + robotState[robotIndex].rank=0; + + // globalZ++; + // globalRank=0; + + // console.log("robot:"+robotIndex+" fff"+" home:"+home); + if(!goHome[robotIndex]) + { + startMovement(robotIndex,true); + } + + }else + { + console.log("DONEEE!!!"); + + } + } + } + } + else if(robotState[robotIndex].Z<voxelSlices.length) + { + // console.log("robot:"+robotIndex+" fffff"+" home:"+home); + robotState[robotIndex].Z++; + robotState[robotIndex].rank=0; + + // globalZ++; + // globalRank=0; + // console.log("ss"); + startMovement(robotIndex,false); + } + + } + else + { + resetPath(robotIndex); + reloadVoxel(robotIndex,robotState[robotIndex].leg1Pos, robotState[robotIndex].leg2Pos, robotState[robotIndex].up,robotState[robotIndex].forward); + generatePoints(robotIndex); + + for(var i=0;i<steps[robotIndex].length;i++) + { + step(robotIndex,i); + } + goHome[robotIndex]=false; + + + if(steps[robotIndex].length==0) + { + // console.log("nn st") + startMovement(robotIndex,false); + } + + } + +} + +function reloadVoxel(robotIndex,leg1Pos, leg2Pos, up ,forward){ + var succeeded; + + showTargetPosition(robotIndex,leg1Pos.clone(),false); + + state= pathPlan(robotIndex,leg1Pos, leg2Pos, up ,forward,startLocations[robotIndex]); + + robotState[robotIndex].leg1Pos=state[0].clone(); + robotState[robotIndex].leg2Pos=state[1].clone(); + robotState[robotIndex].up=state[2].clone(); + robotState[robotIndex].forward=state[3].clone(); + succeeded=state[4]; + + if(succeeded) + { + return [state[0],state[1],state[2],state[3]]; + + }else + { + // console.log("COULDN'T FIND PATH!!!"); + return false; + } + + +} + +function voxelBuilder(robotIndex,leg1Pos, leg2Pos, up ,forward){ + + var start=leg1Pos.clone(); + var state; + var succeeded; + var count=0; + + //get point with minimum distance + var min=Infinity; + var minIndex= new THREE.Vector3(0,0,0); + + + if(voxelSlices[robotState[robotIndex].Z].length>0) + { + + for(var i=0;i<voxelSlices[robotState[robotIndex].Z][robotState[robotIndex].rank].length;i++) + { + if(!voxelAt(voxelSlices[robotState[robotIndex].Z][robotState[robotIndex].rank][i].clone().multiplyScalar(1/voxelSpacing))) + { + + if(start.distanceTo(voxelSlices[robotState[robotIndex].Z][robotState[robotIndex].rank][i])<min) + { + min=start.distanceTo(voxelSlices[robotState[robotIndex].Z][robotState[robotIndex].rank][i]); + minIndex=voxelSlices[robotState[robotIndex].Z][robotState[robotIndex].rank][i]; + + } + + } + } + + grid[minIndex.x/voxelSpacing][minIndex.y/voxelSpacing][minIndex.z/voxelSpacing]=true; + + showTargetPosition(robotIndex,minIndex,true); + + // console.log(minIndex); + + state= pathPlan(robotIndex,leg1Pos, leg2Pos, up ,forward,minIndex); + + robotState[robotIndex].leg1Pos=state[0].clone(); + robotState[robotIndex].leg2Pos=state[1].clone(); + robotState[robotIndex].up=state[2].clone(); + robotState[robotIndex].forward=state[3].clone(); + succeeded=state[4]; + + if(succeeded) + { + return [state[0],state[1],state[2],state[3]]; + + }else + { + // console.log("COULDN'T FIND PATH!!!"); + return false; + } + + } + +} + +//////////////////////////load Voxels//////////////////////////// +function loadVoxel(){ + var geometry = new THREE.BufferGeometry(); + // create a simple square shape. We duplicate the top left and bottom right + // vertices because each vertex needs to appear once per triangle. + var vertices = voxelData; + var normals = voxelNormalData; + var uv = voxelUVData; + + // itemSize = 3 because there are 3 values (components) per vertex + geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + geometry.setAttribute( 'uv', new THREE.BufferAttribute( uv, 2 ) ); + + var material = new THREE.MeshLambertMaterial( { color: 0xbbc3ce } ); + var object = new THREE.Mesh( geometry, material ); + // object.scale.x=0.04; + // object.scale.y=0.04; + // object.scale.z=0.04; + // object.position.z=-1.5; + object.scale.x=0.5/3.0; + object.scale.y=0.5/3.0; + object.scale.z=0.5/3.0; + object.position.x=-15; + object.position.y=-15; + voxel=object; +} + +function frepVoxel(rangeX,rangeY,rangeZ,stringFunction){ + //build grid + for(var i=0;i<gridSize;i++) + { + var t=[]; + grid.push(t) + for(var j=0;j<gridSize;j++) + { + var tt=[]; + grid[i].push(tt); + for(var k=0;k<gridSize;k++) + { + grid[i][j].push(false); + } + } + } + //build first layer + for(var i=0;i<gridSize;i++) + { + for(var j=0;j<gridSize;j++) + { + buildVoxelAt(new THREE.Vector3(i*voxelSpacing,j*voxelSpacing,0)); + grid[i][j][0]=true; + } + } + + // string function= "Math.min(Math.min(Math.min(Math.min(X-(-1),(1)-X),Math.min(Y-(-1),(1)-Y)),Math.min(Z-(-1),(1)-Z)),-(Math.min(Math.min(Math.min(X-(-0.8),(0.8)-X),Math.min(Y-(-0.8),(0.8)-Y)),Math.min(Z-(-0.8),(0.8)-Z))))"; + var maxZslices=[]; + var tempVoxelSlices=[]; + for (var Z=0;Z<gridSize;Z++) + { + voxelSlices.push([]); + tempVoxelSlices.push([]); + voxelSlicesCount.push([]); + var max=- Infinity; + var maxIndex=new THREE.Vector3(0,0,0); + for (var Y=0;Y<gridSize;Y++) + { + for (var X=0;X<gridSize;X++) + { + var func= frep(X,Y,Z); + + if(func>=0 && !grid[X][Y][Z]) + { + if(func>max) + { + max=func; + maxIndex=new THREE.Vector3(X*voxelSpacing,Y*voxelSpacing, Z*voxelSpacing); + + } + var loc=new THREE.Vector3(X*voxelSpacing,Y*voxelSpacing,Z*voxelSpacing); + tempVoxelSlices[Z].push(loc); + voxelNum++; + } + } + } + maxZslices.push(maxIndex);//check if right later + } + + for (var Z=0;Z<gridSize;Z++) + { + + for(var i=0;i<tempVoxelSlices[Z].length;i++) + { + var rank = Math.ceil(maxZslices[Z].distanceTo(tempVoxelSlices[Z][i]) /voxelSpacing); + while(voxelSlices[Z].length<=rank) + { + voxelSlicesCount[Z].push([]); + + voxelSlices[Z].push([]); + + voxelSlicesCount[Z][voxelSlices[Z].length-1]=0; + + voxelSlices[Z][voxelSlices[Z].length-1]=[]; + } + voxelSlices[Z][rank].push(tempVoxelSlices[Z][i]); + } + } + + ////////// + for(var i=0; i<numberOfRobots;i++) + { + buildHelperMeshes(i); + } + + +} + +function frep(X,Y,Z){ + // return (3*3-((X-(5))*(X-(5))+(Y-(5))*(Y-(5))+(Z-(3))*(Z-(3)))); //sphere FIX!! + // return Math.min(Math.min(Math.min(Math.min(X-(2),(7)-X),Math.min(Y-(2),(7)-Y)),Math.min(Z-(1),(5)-Z)),-(Math.min(Math.min(Math.min(X-(4),(8)-X),Math.min(Y-(4),(8)-Y)),Math.min(Z-(0),(4)-Z)))); + // return Math.min(Math.min(Math.min(Math.min(X-(2),(5)-X),Math.min(Y-(2),(5)-Y)),Math.min(Z-(1),(5)-Z)),-(Math.min(Math.min(Math.min(X-(3),(6)-X),Math.min(Y-(3),(6)-Y)),Math.min(Z-(0),(4)-Z)))); + // return Math.min(Math.min(Math.min(Math.min(X-(2),(7)-X),Math.min(Y-(2),(7)-Y)),Math.min(Z-(1),(6)-Z)),-(Math.min(Math.min(Math.min(X-(3),(6)-X),Math.min(Y-(3),(6)-Y)),Math.min(Z-(0),(7)-Z))));//empty cube + // return Math.min(((6)-Z),(Math.min((Z-(1)),(((3*(5-(Z-(1)))/5)*(3*(5-(Z-(1)))/5)-((X-(5))*(X-(5))+(Y-(5))*(Y-(5))))))));//CONE + return Math.min(Math.min(Math.min(X-(2),((gridSize-3))-X),Math.min(Y-(2),((gridSize-3))-Y)),Math.min(Z-(1),((gridSize-4))-Z)); //CUBE +} + +function buildVoxelAt(loc){ + var object1=voxel.clone(); + object1.position.x=loc.x; + object1.position.y=loc.y; + object1.position.z=loc.z+voxelSpacing/2; + // if(loc.z==0){ + // var color1= new THREE.Color( 0, 0, 0 ) + + // var material = new THREE.MeshLambertMaterial( { color: 0x000000 } ); + + // // var color = new THREE.Color( 1/globalRank, 0, 0 ); + + // object1.material=material.clone(); + // object1.material.color=color1; + + // } + + + + scene.add( object1 ); +} + +//////////////////////////path planing/////////////////////////// + +function pathPlan(robotIndex,leg1Pos, leg2Pos, up ,forward,targetPos){ + + if(voxelAt(targetPos.clone().multiplyScalar(1/voxelSpacing))) + { + var succeded=false; + var p0=findShortestPath(robotIndex,leg1Pos.clone(), leg2Pos.clone(), up.clone(),forward.clone(), targetPos); + succeded=p0[4]; + steps[robotIndex]=steps[robotIndex].concat(p0[5]); + return [p0[0] , p0[1] , p0[2] , p0[3] , true]; + if(!succeded) + { + // console.log("see path from neighbours"); + var neighbours=getNeighbours(targetPos); + + for(var i=0;i<neighbours.length;i++) + { + var p=findShortestPath(robotIndex,leg1Pos.clone(), leg2Pos.clone(), up.clone(),forward.clone(),neighbours[i]); + succeded=p[4]; + if(succeded) + { + var stepsFirst=p[5].slice(); + var p1=findShortestPath(robotIndex,p[0], p[1], p[2],p[3],targetPos); + succeded=p[4]; + if(succeded) + { + for(var j=0;j<steps[robotIndex].length;j++) + { + stepsFirst.push(p1[5][j]); + } + + steps[robotIndex]=steps[robotIndex].concat(stepsFirst.slice()); + return [p[0] , p[1] , p[2] , p[3] , true]; + } + } + } + return [leg1Pos, leg2Pos, up ,forward , false]; + } + + } + else + { + // console.log("TARGET VOXEL DOESN'T EXIST!"); + return [leg1Pos, leg2Pos, up ,forward , false]; + + } + + +} + +function findShortestPath(robotIndex,leg1Pos, leg2Pos, up,forward, targetPos){ + var stepsTemp=[]; + var stepsAlternative=[]; + var orderAlternatives=[[0,1,2],[0,2,1],[1,2,0],[1,0,2],[2,1,0],[2,0,1]]; + for(var i=0;i<orderAlternatives.length;i++) + { + stepsAlternative.push(findPath(robotIndex,leg1Pos.clone(), leg2Pos.clone(), up.clone(),forward.clone(), targetPos, orderAlternatives[i]).slice()); + } + stepsAlternative.sort(function(a, b){ + // ASC -> a.length - b.length + // DESC -> b.length - a.length + return a[4].length - b[4].length; + }); + stepsTemp=stepsAlternative[0][4].slice(); + if(stepsTemp.length>stepsCap) + { + stepsTemp=[]; + // console.log("CAN'T FIND ANY PATH TO THIS POINT"); + return [stepsAlternative[0][0],stepsAlternative[0][1],stepsAlternative[0][2],stepsAlternative[0][3],false,stepsTemp]; + }else + { + return [stepsAlternative[0][0],stepsAlternative[0][1],stepsAlternative[0][2],stepsAlternative[0][3],true,stepsTemp]; + } +} + +function findPath(robotIndex,leg1Pos, leg2Pos, up, forward, targetPos, order){ + var logg=false; + if (logg) console.log(order); + var stepsTemp=[]; + // leg1Pos.multiplyScalar(1/voxelSpacing); + // leg2Pos.multiplyScalar(1/voxelSpacing); + var difference=getDifference(leg1Pos, targetPos); + var forwardAlt=new THREE.Vector3(0,0,0); + var pos=leg1Pos.clone(); + + + while(difference[0]!=0||difference[1]!=0||difference[2]!=0) + { + for(var count=0;count<3;count++) + { + var i= order[count]; + var startingLeg1Pos=leg1Pos.clone(); + var startingLeg2Pos=leg2Pos.clone(); + var exit=false; + var previousDifference=Math.pow(10, 1000);//infinity + + if(i==0) + { + forwardAlt=new THREE.Vector3(-difference[0],0,0); + forwardAlt.normalize(); + if(up.x==0) + { + forward=forwardAlt.clone(); + } + }else if(i==1) + { + forwardAlt=new THREE.Vector3(0,-difference[1],0); + forwardAlt.normalize(); + if(up.y==0) + { + forward=forwardAlt.clone(); + } + }else if(i==2) + { + forwardAlt=new THREE.Vector3(0,0,-difference[2]); + forwardAlt.normalize(); + if(up.z==0) + { + forward=forwardAlt.clone(); + } + } + + while (Math.abs(difference[i])>0 && !exit) + { + + if(voxelAt(pos.clone().add(forward.clone().add(up)))) + { + if( voxelAt( pos.clone().add( forward.clone().add(up.clone().add(up)) ))) + { + //step there //rotate convex + if (logg) console.log("rotate convex"); + leg1Pos=pos.clone().add( forward.clone().add(up.clone().add(up))); + leg2Pos=pos.clone().add(forward.clone().add(up)); + var temp=up.clone(); + up= new THREE.Vector3(0,0,0).sub(forward); + forward=temp.clone(); + stepsTemp.push([leg1Pos.clone().multiplyScalar(voxelSpacing),leg2Pos.clone().multiplyScalar(voxelSpacing),up]); + difference=getDifference(leg1Pos, targetPos); + pos=leg1Pos.clone(); + } + else //if pos+forward+up+up empty + { + //step there //step up + if (logg) console.log("step up"); + leg2Pos=leg1Pos.clone(); + leg1Pos=pos.clone().add(forward.clone().add(up)); + stepsTemp.push([leg1Pos.clone().multiplyScalar(voxelSpacing),leg2Pos.clone().multiplyScalar(voxelSpacing),up]); + difference=getDifference(leg1Pos, targetPos); + pos=leg1Pos.clone(); + } + } + else //if(pos +foward +up) empty + { + if (voxelAt(pos.clone().add(forward.clone()))) + { + //step there //step forward + if (logg) console.log("step forward"); + leg2Pos=leg1Pos.clone(); + leg1Pos=pos.clone().add(forward.clone()); + stepsTemp.push([leg1Pos.clone().multiplyScalar(voxelSpacing),leg2Pos.clone().multiplyScalar(voxelSpacing),up]); + difference=getDifference(leg1Pos, targetPos); + pos=leg1Pos.clone(); + } + else //if pos+forward empty + { + if(voxelAt(pos.clone().add(forward.clone().sub(up)))) + { + //step there //step down + if (logg) console.log("step down"); + leg2Pos=leg1Pos.clone(); + leg1Pos=pos.clone().add(forward.clone().sub(up)); + stepsTemp.push([leg1Pos.clone().multiplyScalar(voxelSpacing),leg2Pos.clone().multiplyScalar(voxelSpacing),up]); + difference=getDifference(leg1Pos, targetPos); + pos=leg1Pos.clone(); + } + else if (voxelAt(pos.clone().sub(up))) //pos-up full + { + if (logg) console.log("rotate concave"); + leg2Pos=leg1Pos.clone(); + leg1Pos=pos.clone().sub(up); + var temp=up.clone(); + up= forward.clone(); + forward=new THREE.Vector3(0,0,0).sub(temp); + stepsTemp.push([leg1Pos.clone().multiplyScalar(voxelSpacing),leg2Pos.clone().multiplyScalar(voxelSpacing),up]); + difference=getDifference(leg1Pos, targetPos); + pos=leg1Pos.clone(); + } + else + { + if (logg) console.log("fail"); + exit=true; + } + } + } + + if( previousDifference<0 &&difference[i]<=0 ) + { + + if(Math.abs(previousDifference)<Math.abs(difference[i])) + { + exit=true; + if (logg) console.log("exit"); + } + + } + else + { + if(previousDifference<Math.abs(difference[i])) + { + exit=true; + if (logg) console.log("exit"); + } + + } + previousDifference=difference[i]; + if(stepsTemp.length>stepsCap)//cap number of steps in case of failuer // change later based on complexity of the model + { + exit=true; + if (logg) console.log("Too many steps,choose another alternative"); + } + + } + + + } + if(stepsTemp.length>stepsCap)//cap number of steps in case of failuer // change later based on complexity of the model + { + difference[0]=0; + difference[1]=0; + difference[2]=0; + if (logg) console.log("Too many steps,choose another alternative"); + } + + } + return [leg1Pos, leg2Pos, up,forward,stepsTemp]; + +} + +function voxelAt(location){ + if(location.x<0||location.y<0||location.z<0||location.x>=gridSize||location.y>=gridSize||location.z>=gridSize) + { + return false; + } + return grid[location.x][location.y][location.z]; +} + +function getDifference(location, targetPos){ + var diff=[]; + diff.push(location.x-targetPos.x/voxelSpacing);//0 + diff.push(location.y-targetPos.y/voxelSpacing);//1 + diff.push(location.z-targetPos.z/voxelSpacing);//2 + return diff.slice(); +} + +function getNeighbours( targetPos){ + var n=[]; + var t=targetPos.clone().multiplyScalar(1/voxelSpacing); + // for(var i=-1;i<2;i++) + // { + // for(var j=-1;j<2;j++) + // { + // for(var k=-1;k<2;k++) + // { + // var tt=new THREE.Vector3(t.x+i,t.y+j,t.z+k); + // if(!(tt.equals(t)) && voxelAt(tt) ) + // { + // n.push(new THREE.Vector3(tt*voxelSpacing,tt*voxelSpacing,tt*voxelSpacing)); + // } + // } + // } + // } + var tt=new THREE.Vector3(t.x+1,t.y,t.z); + if(voxelAt(tt)) + { + n.push(new THREE.Vector3(tt.x*voxelSpacing,tt.y*voxelSpacing,tt.z*voxelSpacing)); + } + tt=new THREE.Vector3(t.x-1,t.y,t.z); + if(voxelAt(tt)) + { + n.push(new THREE.Vector3(tt.x*voxelSpacing,tt.y*voxelSpacing,tt.z*voxelSpacing)); + } + tt=new THREE.Vector3(t.x,t.y+1,t.z); + if(voxelAt(tt)) + { + n.push(new THREE.Vector3(tt.x*voxelSpacing,tt.y*voxelSpacing,tt.z*voxelSpacing)); + } + tt=new THREE.Vector3(t.x,t.y-1,t.z); + if(voxelAt(tt)) + { + n.push(new THREE.Vector3(tt.x*voxelSpacing,tt.y*voxelSpacing,tt.z*voxelSpacing)); + } + tt=new THREE.Vector3(t.x,t.y,t.z+1); + if(voxelAt(tt)) + { + n.push(new THREE.Vector3(tt.x*voxelSpacing,tt.y*voxelSpacing,tt.z*voxelSpacing)); + } + tt=new THREE.Vector3(t.x,t.y,t.z-1); + if(voxelAt(tt)) + { + n.push(new THREE.Vector3(tt.x*voxelSpacing,tt.y*voxelSpacing,tt.z*voxelSpacing)); + } + return n; +} + +//////////////////////////utilities//////////////////////////// +function ClosestPointOnLine(s ,d ,vPoint ){ + var tempvPoint=new THREE.Vector3(0,0,0); + tempvPoint.copy(vPoint); + var tempD=new THREE.Vector3(0,0,0); + tempD.copy(d); + var temps=new THREE.Vector3(0,0,0); + temps.copy(d); + + + var vVector1 = tempvPoint.sub(temps) ; + + var vVector2 = tempD.normalize(); + + var t = vVector2.dot( vVector1); + + if (t <= 0) + { + return s; + } + + var vVector3 = vVector2.multiplyScalar(t) ; + + var vClosestPoint = temps.add(vVector3); + + return vClosestPoint; +} + +function buildHelperMeshes(robotIndex){ + var material = new THREE.MeshLambertMaterial({ color:0xff7171,}); + var geometry = new THREE.SphereGeometry(0.5, 0.5, 0.5); + targetPositionMesh[robotIndex] = new THREE.Mesh(geometry, material); + targetPositionMesh[robotIndex].scale.x=0.8; + targetPositionMesh[robotIndex].scale.y=0.8; + targetPositionMesh[robotIndex].scale.z=0.8; + scene.add(targetPositionMesh[robotIndex]); + + for (var count=0; count<startLocations.length;count++) + { + geometry = new THREE.BoxGeometry(voxelSpacing*1.1, voxelSpacing*1.1, voxelSpacing*1.1); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x=startLocations[count].x; + mesh.position.y=startLocations[count].y; + mesh.position.z=startLocations[count].z; + scene.add(mesh); + } + +} + +function showTargetPosition(robotIndex,targetPos,show){ + if(show) + { + + targetPositionMesh[robotIndex].position.x=targetPos.x; + targetPositionMesh[robotIndex].position.y=targetPos.y; + targetPositionMesh[robotIndex].position.z=targetPos.z+voxelSpacing/2; + carriedVoxel[robotIndex].visible=true; + targetPositionMesh[robotIndex].visible=true; + + }else + { + carriedVoxel[robotIndex].visible=false; + targetPositionMesh[robotIndex].visible=false; + + + } + +} + + + + + + diff --git a/01_Code/physical_computing_interface/globals.js b/01_Code/physical_computing_interface/globals.js index 20982711ac7710d0a77941c7f073396a51309e91..699946c5910f4cb3da6be801896fbf4658faff0c 100644 --- a/01_Code/physical_computing_interface/globals.js +++ b/01_Code/physical_computing_interface/globals.js @@ -89,6 +89,7 @@ globals.prototype.selectNode=function (x,y,z){ //////////////////////utils////////////////// function utilities(){ }; + utilities.prototype.getXYZfromName=function(name){ var nums=name.match(/\d+/g); var x=nums[0]; @@ -96,6 +97,7 @@ utilities.prototype.getXYZfromName=function(name){ var z=nums[2]; return new THREE.Vector3(x,y,z); } + utilities.prototype.getNeighboursList=function(grid,x,y,z){ var list=[]; for(var i=0;i<grid.neighbors.length;i++){ diff --git a/01_Code/physical_computing_interface/threejs/grid.html b/01_Code/physical_computing_interface/threejs/grid.html index 86908dc8cb6e0863e9288aa8939873891024f380..f48f738825e0661927a43b04313e156bbd4b0894 100644 --- a/01_Code/physical_computing_interface/threejs/grid.html +++ b/01_Code/physical_computing_interface/threejs/grid.html @@ -12,7 +12,7 @@ <script src="../lib/OrbitControls.js"></script> <!-- code --> -<script src="./grid1.js"></script> +<script src="./StandAloneGrid.js"></script> </body> </html> \ No newline at end of file