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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more