Files
cadSet/text-to-cad/models/mechanisms/.adjustable_height_table_2.step.js
T
2026-07-22 19:38:36 +08:00

288 lines
11 KiB
JavaScript

const X_AXIS = [1, 0, 0];
const BOTTOM_FIXED_PIVOT = [14.610456, -171.775187, 23.0];
const TOP_FIXED_PIVOT_INITIAL = [14.610456, -171.775187, 69.00033];
const ROLLER_PIVOT_INITIAL = [14.610456, 124.677119, 69.00033];
const LOWER_ROLLER_CENTER_INITIAL = [14.610456, 124.677119, 23.0];
const UPPER_ROLLER_CENTER_INITIAL = [14.610456, 124.677119, 69.00033];
const ACTUATOR_PIVOT_INITIAL = [14.610456, -142.775187, 27.499913];
const INITIAL_HEIGHT = TOP_FIXED_PIVOT_INITIAL[2] - BOTTOM_FIXED_PIVOT[2];
const INITIAL_RUN = ROLLER_PIVOT_INITIAL[1] - BOTTOM_FIXED_PIVOT[1];
const LINK_LENGTH = Math.hypot(INITIAL_RUN, INITIAL_HEIGHT);
const INITIAL_LINK_ANGLE_DEG = angleDeg(INITIAL_HEIGHT, INITIAL_RUN);
const ACTUATOR_Y_OFFSET = ACTUATOR_PIVOT_INITIAL[1] - BOTTOM_FIXED_PIVOT[1];
const ROLLER_RADIUS = 20.0;
const RUNWAY_MIN_Y = -2.775187;
const RUNWAY_END_CLEARANCE = 12.0;
const MAX_RAISED_HEIGHT = maxSafeRaisedHeight();
const DEFAULT_RAISED_HEIGHT = 215;
const TOP_ASSEMBLY = ["topAssembly"];
const RISING_LINKAGE = ["risingLinkage"];
const DESCENDING_LINKAGE = ["descendingLinkage"];
const LOWER_ROLLERS = ["lowerRollers"];
const UPPER_ROLLERS = ["upperRollers"];
const ACTUATOR_ROD = ["piston", "actuatorCrossShaft"];
const ACTUATOR_SLIDER = ["actuatorSlider"];
const MOVING_PARTS = [
...TOP_ASSEMBLY,
...RISING_LINKAGE,
...DESCENDING_LINKAGE,
...LOWER_ROLLERS,
...UPPER_ROLLERS,
...ACTUATOR_ROD,
...ACTUATOR_SLIDER
];
function finite(value, fallback = 0) {
const numericValue = Number(value);
return Number.isFinite(numericValue) ? numericValue : fallback;
}
function clamp(value, min, max) {
return Math.min(Math.max(finite(value, min), min), max);
}
function clamp01(value) {
return clamp(value, 0, 1);
}
function smooth01(value) {
const t = clamp01(value);
return t * t * (3 - (2 * t));
}
function lerp(start, end, t) {
return start + ((end - start) * t);
}
function angleDeg(height, run) {
return Math.atan2(height, run) * 180 / Math.PI;
}
function maxSafeRaisedHeight() {
const minRollerCenterY = RUNWAY_MIN_Y + ROLLER_RADIUS + RUNWAY_END_CLEARANCE;
const minRun = minRollerCenterY - BOTTOM_FIXED_PIVOT[1];
return Math.sqrt(Math.max(0, (LINK_LENGTH * LINK_LENGTH) - (minRun * minRun)));
}
function cycleLift(cycle) {
const phase = ((finite(cycle, 0) % 1) + 1) % 1;
if (phase < 0.42) {
return smooth01(phase / 0.42);
}
if (phase < 0.52) {
return 1;
}
if (phase < 0.94) {
return 1 - smooth01((phase - 0.52) / 0.42);
}
return 0;
}
function sampleLift(rawLift, rawRaisedHeight) {
const lift = smooth01(rawLift);
const raisedHeight = clamp(rawRaisedHeight, INITIAL_HEIGHT + 5, MAX_RAISED_HEIGHT);
const height = lerp(INITIAL_HEIGHT, raisedHeight, lift);
const run = Math.sqrt(Math.max(1e-6, (LINK_LENGTH * LINK_LENGTH) - (height * height)));
const angle = angleDeg(height, run);
const rollerY = BOTTOM_FIXED_PIVOT[1] + run;
const heightDelta = height - INITIAL_HEIGHT;
const rollerYDelta = rollerY - ROLLER_PIVOT_INITIAL[1];
// The reference video shows the actuator rising vertically while a green
// slider follows the lower blue link. Solve that slider by intersecting the
// lower link with the fixed actuator centerline.
const actuatorRatio = clamp(ACTUATOR_Y_OFFSET / run, 0, 1);
const actuatorZ = BOTTOM_FIXED_PIVOT[2] + (height * actuatorRatio);
const actuatorZDelta = actuatorZ - ACTUATOR_PIVOT_INITIAL[2];
return {
angle,
heightDelta,
rollerYDelta,
actuatorZDelta,
risingAngleDelta: angle - INITIAL_LINK_ANGLE_DEG,
descendingAngleDelta: -(angle - INITIAL_LINK_ANGLE_DEG),
wheelSpinDeg: -(rollerYDelta / (2 * Math.PI * ROLLER_RADIUS)) * 360
};
}
function applyVisibility(effects, params) {
effects.visible("*", true);
effects.visible("actuator", params.showActuator !== false);
effects.visible("hardware", params.showHardware !== false);
}
export default {
manifest: {
schemaVersion: 1,
step: {
path: "models/thang010146/adjustable_height_table_2.step"
},
label: "Adjustable height table 2",
description: "Viewer-time scissor-lift animation matched to the reference video: the top stays level, right pivots roll in the runways, and the vertical actuator drives the lower-link slider.",
units: {
length: "mm",
angle: "deg",
time: "s"
},
features: {
baseAssembly: { ref: "#o1.1", label: "Base assembly" },
basePlate: { ref: "#o1.1.1", label: "Base plate" },
baseBearings: { ref: "#o1.1.2,o1.1.3", label: "Base fixed bearings" },
baseRunways: { ref: "#o1.1.9,o1.1.10", label: "Base roller runways" },
cylinder: { ref: "#o1.1.8", label: "Fixed cylinder" },
topAssembly: { ref: "#o1.2", label: "Moving table top" },
topPlate: { ref: "#o1.2.1", label: "Table top" },
topBearings: { ref: "#o1.2.2,o1.2.3", label: "Top fixed bearings" },
topRunways: { ref: "#o1.2.8,o1.2.9", label: "Top roller runways" },
risingLinkage: { ref: "#o1.3", label: "Rising scissor links", description: "Links from the lower fixed pivots to the upper rolling pivots." },
descendingLinkage: { ref: "#o1.4", label: "Descending scissor links", description: "Links from the upper fixed pivots to the lower rolling pivots." },
centralPivot: { ref: "#o1.3.3,o1.3.4,o1.3.5", label: "Central scissor pivot" },
upperRollerShafts: { ref: "#o1.3.6,o1.3.7,o1.3.8,o1.3.9,o1.3.10,o1.3.11", label: "Upper roller shafts" },
lowerRollerShafts: { ref: "#o1.4.3,o1.4.4,o1.4.5,o1.4.6,o1.4.7,o1.4.8", label: "Lower roller shafts" },
upperRollers: { ref: "#o1.8,o1.9", label: "Upper pink rollers" },
lowerRollers: { ref: "#o1.6,o1.7", label: "Lower pink rollers" },
piston: { ref: "#o1.5", label: "Piston rod" },
actuatorSlider: { ref: "#o1.10", label: "Green actuator slider" },
actuatorCrossShaft: { ref: "#o1.11", label: "Actuator cross shaft" },
actuator: { ref: "#o1.1.8,o1.5,o1.10,o1.11", label: "Hydraulic actuator" },
hardware: {
ref: "#o1.1.4,o1.1.5,o1.1.6,o1.1.7,o1.2.4,o1.2.5,o1.2.6,o1.2.7,o1.3.4,o1.3.5,o1.3.7,o1.3.9,o1.3.10,o1.3.11,o1.4.4,o1.4.5,o1.4.7,o1.4.8",
label: "Nuts and bolts"
}
},
parameters: {
lift: { type: "number", label: "Lift", description: "Collapsed to raised scissor-lift travel.", default: 0, min: 0, max: 1, step: 0.01 },
raisedHeightMm: { type: "number", label: "Raised height", description: "Upper fixed-pivot height above the base fixed pivots at full lift; clamped so rollers stay inside the runways.", default: DEFAULT_RAISED_HEIGHT, min: 80, max: MAX_RAISED_HEIGHT, step: 1, unit: "mm" },
explode: { type: "number", label: "Explode", description: "Separates the motion groups for inspection after the linkage pose is applied.", default: 0, min: 0, max: 1, step: 0.01 },
showActuator: { type: "boolean", label: "Actuator", default: true },
showHardware: { type: "boolean", label: "Hardware", default: true },
highlightMoving: { type: "boolean", label: "Highlight moving parts", default: false }
},
animations: {
liftLoop: {
label: "Reference lift loop",
description: "Low table, hydraulic lift to raised position, brief dwell, and return.",
duration: 8,
loop: true,
update({ cycle, set }) {
set("lift", cycleLift(cycle));
}
},
inspectExplode: {
label: "Lift with exploded groups",
duration: 8,
loop: true,
update({ cycle, set }) {
const lift = cycleLift(cycle);
set("lift", lift);
set("explode", Math.sin(lift * Math.PI));
}
}
}
},
update({ params, effects }) {
const pose = sampleLift(params.lift, params.raisedHeightMm);
const explode = clamp01(params.explode);
applyVisibility(effects, params);
effects.style("basePlate", { opacity: 1, edgeOpacity: 0.8 });
effects.style(["baseBearings", "topBearings"], { opacity: 1, edgeOpacity: 0.8 });
effects.style(["baseRunways", "topRunways"], { opacity: 1, edgeOpacity: 0.9 });
effects.style("topPlate", { opacity: 1, edgeOpacity: 0.85 });
effects.style("risingLinkage", { opacity: 1, edgeOpacity: 1 });
effects.style("descendingLinkage", { opacity: 1, edgeOpacity: 1 });
effects.style("centralPivot", { opacity: 1, edgeOpacity: 1 });
effects.style(["lowerRollerShafts", "upperRollerShafts", "actuatorCrossShaft"], { opacity: 1, edgeOpacity: 0.9 });
effects.style(["lowerRollers", "upperRollers"], { opacity: 1, edgeOpacity: 1 });
effects.style("cylinder", { opacity: 1, edgeOpacity: 1 });
effects.style("piston", { opacity: 1, edgeOpacity: 1 });
effects.style("actuatorSlider", { opacity: 1, edgeOpacity: 1 });
effects.style("hardware", { opacity: 1, edgeOpacity: 0.7 });
if (params.highlightMoving !== false) {
effects.highlight(MOVING_PARTS, true);
}
effects.transform("topAssembly", {
translate: [0, explode * -10, pose.heightDelta + (explode * 22)]
});
effects.transform("risingLinkage", {
transforms: [
{
rotate: {
axis: X_AXIS,
origin: BOTTOM_FIXED_PIVOT,
angleDeg: pose.risingAngleDelta
}
},
{ translate: [explode * 12, explode * -8, explode * 12] }
]
});
effects.transform("descendingLinkage", {
transforms: [
{
rotate: {
axis: X_AXIS,
origin: TOP_FIXED_PIVOT_INITIAL,
angleDeg: pose.descendingAngleDelta
}
},
{ translate: [explode * -12, explode * 8, pose.heightDelta + (explode * 12)] }
]
});
effects.transform("lowerRollers", {
transforms: [
{
rotate: {
axis: X_AXIS,
origin: LOWER_ROLLER_CENTER_INITIAL,
angleDeg: pose.wheelSpinDeg
}
},
{ translate: [0, pose.rollerYDelta + (explode * 10), explode * 8] }
]
});
effects.transform("upperRollers", {
transforms: [
{
rotate: {
axis: X_AXIS,
origin: UPPER_ROLLER_CENTER_INITIAL,
angleDeg: pose.wheelSpinDeg
}
},
{ translate: [0, pose.rollerYDelta + (explode * -10), pose.heightDelta + (explode * 18)] }
]
});
effects.transform(ACTUATOR_ROD, {
translate: [0, 0, pose.actuatorZDelta + (explode * 10)]
});
effects.transform("actuatorSlider", {
transforms: [
{
rotate: {
axis: X_AXIS,
origin: ACTUATOR_PIVOT_INITIAL,
angleDeg: pose.risingAngleDelta
}
},
{ translate: [0, 0, pose.actuatorZDelta + (explode * 14)] }
]
});
}
};