Public
Edited
Sep 14, 2024
Paused
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Sketchfab = require(await FileAttachment("sketchfab-viewer-1.11.0.js").url())
Insert cell
Insert cell
loadSketchfab(uid, "api-frame");
Insert cell
loadSketchfab = (sceneuid, elementId) => {
const iframe = document.getElementById(elementId);
const client = new Sketchfab("1.11.0", iframe);

client.init(sceneuid, {
success: success,
error: () => console.error("Sketchfab API error"),
ui_controls: 0,
ui_infos: 0,
ui_watermark: 0,
ui_stop: 0,
preload: 1,
autostart: 1,
orbit_constraint_pan: 1,
});
};
Insert cell
getNodeByName = (nodemap, nodename) => {
return Object.values(nodemap).find((node) => {
if (node.type === "MatrixTransform" && node.name === nodename) {
return node;
}
});
};
Insert cell
// Rotate a node by a certain angle (in radians)
rotateNode = (api, instanceID, angle, RX, RY, RZ) => {
api.rotate(
instanceID,
[angle, RX, RY, RZ],
{
duration: 0.0,
easing: "linear"
}
);
};
Insert cell
degToRad = (deg) => (deg / 180) * Math.PI;
Insert cell
success = (api) => {
api.start(function () {
api.addEventListener("viewerready", function () {
api.getNodeMap(function (err, nodes) {
// console.log(nodes);
const hip = getNodeByName(nodes, "M0_16");
const thigh = getNodeByName(nodes, "M1_15");
const gear1 = getNodeByName(nodes, "gear_1_7");
const gear2 = getNodeByName(nodes, "gear_2_11");
const lever = getNodeByName(nodes, "lever_8");
const calf = getNodeByName(nodes, "Calf_3");

const MinAngle1 = -25;
const MaxAngle1 = 30;
const MinAngle2 = -65;
const MaxAngle2 = 80;
const MinAngle3 = -70;
const MaxAngle3 = 35;

// Retrieve the materials from the model and set opacity of the thigh
document.getElementById("opacity").addEventListener("input", function (event) {
const opacityValue = parseFloat(event.target.value);
api.getMaterialList(function (err, materials) {
const thighMaterial_1 = materials.find(material => material.name === "Thigh");
const thighMaterial_2 = materials.find(material => material.name === "thigh_protection");
thighMaterial_1.channels["Opacity"].factor = opacityValue;
thighMaterial_2.channels["Opacity"].factor = opacityValue;
api.setMaterial(thighMaterial_1);
api.setMaterial(thighMaterial_2);
});
});

// Enable/disable wireframe
document.getElementById("wireframe").addEventListener("change", function (event) {
const wireframeEnabled = event.target.checked;
api.setWireframe(wireframeEnabled);
});
// Hip rotation (Axis 1):
// Action on the node "hip" affects all children objects.
// Hence, this is the only node that need to be manipulated.
document
.getElementById("slider1")
.addEventListener("input", function (event) {
const sliderValue = parseFloat(event.target.value);
const Angle = sliderValue * (MaxAngle1 - MinAngle1) + MinAngle1;
const angle = degToRad(Angle);
rotateNode(api, hip.instanceID, angle, 1, 0, 0);
});

// Thigh rotation (Axis 2):
// Action on the node "thigh" affects all children objects.
document
.getElementById("slider2")
.addEventListener("input", function (event) {
const sliderValue = parseFloat(event.target.value);
const Angle = sliderValue * (MaxAngle2 - MinAngle2) + MinAngle2;
const angle = degToRad(Angle);
rotateNode(api, thigh.instanceID, angle, 0, 1, 0);
});

// Calf rotation (Axis 3):
// The mechanism consists of:
// * 2 gears (gear 1, gear 2): The gear ratio is 2:1
// * Lever
// * Calf
// Workaround for GLB format limitation:
// The lever is defined as a child of gear 2.
// However, we need to compensate for the rotation,
// as we only want to translate the lever.
document
.getElementById("slider3")
.addEventListener("input", function (event) {
const sliderValue = parseFloat(event.target.value);
const Angle = sliderValue * (MaxAngle3 - MinAngle3) + MinAngle3;
const angle = degToRad(Angle);
rotateNode(api, lever.instanceID, -angle, 0, 1, 0);
rotateNode(api, gear1.instanceID, -2 * angle, 0, 1, 0);
rotateNode(api, gear2.instanceID, angle, 0, 1, 0);
rotateNode(api, calf.instanceID, angle, 0, 1, 0);
});
});
});
});
};
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more