Import CAD projects and cad-router v1
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
const Z_AXIS = [0, 0, 1];
|
||||
const FLAT_ON_GROUND_TRANSFORM = [
|
||||
0, 0, -1, 0,
|
||||
0, 1, 0, 0,
|
||||
1, 0, 0, -322.461928,
|
||||
0, 0, 0, 1
|
||||
];
|
||||
|
||||
// CAD coordinates measured from the STEP occurrence bounds. The side-view
|
||||
// mechanism is a toggle four-bar: the lower pink rocker drives the yellow/red
|
||||
// panel coupler out to an over-center tangent pose, then the coupler returns on
|
||||
// the opposite branch with the panel flipped.
|
||||
const PIVOTS = {
|
||||
upperFrame: [-15.002, 229.609, 0],
|
||||
lowerFrame: [104.981, 29.599, 0],
|
||||
lowerBoard: [45.03, 229.61, 0],
|
||||
upperBoard: [44.984, 420.401, 0]
|
||||
};
|
||||
|
||||
const PANEL_COUPLER_PARTS = ["panel", "redLink", "driveRod", "hingeRod", "panelHardware"];
|
||||
const LOWER_ROCKER_PARTS = ["rocker2", "greenRod", "handleHardware"];
|
||||
const FRAME_DETAIL_PARTS = ["stopper", "bearings"];
|
||||
const FRAME_PARTS = ["frame", ...FRAME_DETAIL_PARTS, "frameHardware"];
|
||||
const HARDWARE = ["frameHardware", "handleHardware", "panelHardware"];
|
||||
const CROSS_RODS = ["hingeRod", "driveRod"];
|
||||
const MOVING_PARTS = ["rockerBlue", ...PANEL_COUPLER_PARTS, ...LOWER_ROCKER_PARTS];
|
||||
|
||||
const INITIAL_PANEL_ANGLE_DEG = angleDeg2(PIVOTS.lowerBoard, PIVOTS.upperBoard);
|
||||
const INITIAL_BLUE_ANGLE_DEG = angleDeg2(PIVOTS.upperFrame, PIVOTS.upperBoard);
|
||||
const INITIAL_LOWER_ROCKER_ANGLE_DEG = angleDeg2(PIVOTS.lowerFrame, PIVOTS.lowerBoard);
|
||||
const LOWER_LINK_LENGTH = distance2(PIVOTS.lowerFrame, PIVOTS.lowerBoard);
|
||||
const UPPER_LINK_LENGTH = distance2(PIVOTS.upperFrame, PIVOTS.upperBoard);
|
||||
const COUPLER_LINK_LENGTH = distance2(PIVOTS.lowerBoard, PIVOTS.upperBoard);
|
||||
const OVER_CENTER_CRANK_ANGLE_DEG = -3.22068;
|
||||
|
||||
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 lerpPoint(start, end, t) {
|
||||
return [
|
||||
lerp(start[0], end[0], t),
|
||||
lerp(start[1], end[1], t),
|
||||
lerp(start[2] || 0, end[2] || 0, t)
|
||||
];
|
||||
}
|
||||
|
||||
function distance2(start, end) {
|
||||
return Math.hypot(end[0] - start[0], end[1] - start[1]);
|
||||
}
|
||||
|
||||
function angleDeg2(start, end) {
|
||||
return Math.atan2(end[1] - start[1], end[0] - start[0]) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
function pointFromAngle(origin, length, angleDeg) {
|
||||
const angleRad = angleDeg * Math.PI / 180;
|
||||
return [
|
||||
origin[0] + (Math.cos(angleRad) * length),
|
||||
origin[1] + (Math.sin(angleRad) * length),
|
||||
origin[2] || 0
|
||||
];
|
||||
}
|
||||
|
||||
function circleIntersections(centerA, radiusA, centerB, radiusB) {
|
||||
const dx = centerB[0] - centerA[0];
|
||||
const dy = centerB[1] - centerA[1];
|
||||
const distance = Math.hypot(dx, dy);
|
||||
if (distance <= 1e-6) {
|
||||
return [centerA, centerA];
|
||||
}
|
||||
const maxDistance = radiusA + radiusB;
|
||||
const minDistance = Math.abs(radiusA - radiusB);
|
||||
const solvedDistance = clamp(distance, minDistance + 1e-6, maxDistance - 1e-6);
|
||||
const along = ((radiusA * radiusA) - (radiusB * radiusB) + (solvedDistance * solvedDistance)) / (2 * solvedDistance);
|
||||
const height = Math.sqrt(Math.max(0, (radiusA * radiusA) - (along * along)));
|
||||
const unitX = dx / distance;
|
||||
const unitY = dy / distance;
|
||||
const midX = centerA[0] + (along * unitX);
|
||||
const midY = centerA[1] + (along * unitY);
|
||||
const offsetX = -unitY * height;
|
||||
const offsetY = unitX * height;
|
||||
return [
|
||||
[midX + offsetX, midY + offsetY, centerA[2] || 0],
|
||||
[midX - offsetX, midY - offsetY, centerA[2] || 0]
|
||||
];
|
||||
}
|
||||
|
||||
function boardBranches(lower) {
|
||||
const branches = circleIntersections(PIVOTS.upperFrame, UPPER_LINK_LENGTH, lower, COUPLER_LINK_LENGTH);
|
||||
return branches.sort((a, b) => b[1] - a[1]);
|
||||
}
|
||||
|
||||
function translateVector(start, end) {
|
||||
return [
|
||||
end[0] - start[0],
|
||||
end[1] - start[1],
|
||||
(end[2] || 0) - (start[2] || 0)
|
||||
];
|
||||
}
|
||||
|
||||
function cycleFlip(cycle) {
|
||||
const phase = ((finite(cycle, 0) % 1) + 1) % 1;
|
||||
return 0.5 - (0.5 * Math.cos(phase * Math.PI * 2));
|
||||
}
|
||||
|
||||
function sampleMotion(rawFlip, { extension = 1, turnover = 1 } = {}) {
|
||||
const flip = clamp01(rawFlip);
|
||||
const travel = clamp(extension, 0.2, 1);
|
||||
const turnoverAmount = clamp(turnover, 0, 1);
|
||||
const extendedAngle = lerp(INITIAL_LOWER_ROCKER_ANGLE_DEG, OVER_CENTER_CRANK_ANGLE_DEG, travel);
|
||||
const firstHalf = flip <= 0.5;
|
||||
const legT = firstHalf ? smooth01(flip / 0.5) : smooth01((flip - 0.5) / 0.5);
|
||||
const lowerAngleDeg = firstHalf
|
||||
? lerp(INITIAL_LOWER_ROCKER_ANGLE_DEG, extendedAngle, legT)
|
||||
: lerp(extendedAngle, INITIAL_LOWER_ROCKER_ANGLE_DEG, legT);
|
||||
const lower = pointFromAngle(PIVOTS.lowerFrame, LOWER_LINK_LENGTH, lowerAngleDeg);
|
||||
const [closedBranch, openBranch] = boardBranches(lower);
|
||||
const upper = !firstHalf && turnoverAmount > 0 ? openBranch : closedBranch;
|
||||
const angleDeg = angleDeg2(lower, upper);
|
||||
return { lower, upper, angleDeg, lowerAngleDeg };
|
||||
}
|
||||
|
||||
function applyVisibility(effects, params) {
|
||||
effects.visible("*", true);
|
||||
effects.visible("frame", params.showFrame !== false);
|
||||
effects.visible(FRAME_DETAIL_PARTS, params.showFrame !== false && params.showDetails !== false);
|
||||
effects.visible("frameHardware", params.showFrame !== false && params.showHardware === true);
|
||||
effects.visible(["handleHardware", "panelHardware"], params.showHardware === true);
|
||||
effects.visible(CROSS_RODS, params.showCrossRods !== false);
|
||||
}
|
||||
|
||||
export default {
|
||||
manifest: {
|
||||
schemaVersion: 1,
|
||||
step: {
|
||||
path: "models/thang010146/180_degree_flip_mechanism.step"
|
||||
},
|
||||
label: "180 degree flip mechanism",
|
||||
description: "Planar four-bar viewer rig reconstructed from the reference video: blue upper link, pink lower rocker, red/board coupler, and an over-center turnover.",
|
||||
units: {
|
||||
length: "mm",
|
||||
angle: "deg",
|
||||
time: "s"
|
||||
},
|
||||
features: {
|
||||
frame: { ref: "#o1.1.1", label: "Frame", description: "Fixed frame body." },
|
||||
stopper: { ref: "#o1.1.2", label: "Stopper" },
|
||||
bearings: { ref: "#o1.1.3,o1.1.4", label: "Bearing blocks" },
|
||||
frameHardware: { ref: "#o1.1.5,o1.1.6,o1.1.7,o1.1.8,o1.1.9,o1.1.10,o1.1.11,o1.1.12,o1.1.13,o1.1.14,o1.1.15,o1.1.16,o1.1.17,o1.1.18,o1.1.19,o1.1.20,o1.1.21,o1.1.22,o1.1.23,o1.1.24,o1.1.25,o1.1.26,o1.1.27,o1.1.28", label: "Frame hardware" },
|
||||
|
||||
rockerBlue: { ref: "#o1.2", label: "Blue upper link", description: "Upper 200 mm link rotating about the upper fixed pivot." },
|
||||
rocker2: { ref: "#o1.3.1", label: "Pink lower rocker", description: "Lower rocker arm rotating about the lower fixed pivot." },
|
||||
greenRod: { ref: "#o1.3.2", label: "Handle rod" },
|
||||
handleHardware: { ref: "#o1.3.3,o1.3.4", label: "Handle hardware" },
|
||||
|
||||
redLink: { ref: "#o1.4.1", label: "Red coupler", description: "Rigid coupler between the upper and lower board pivots." },
|
||||
panel: { ref: "#o1.4.2", label: "Flip panel", description: "Panel fixed to the red coupler." },
|
||||
driveRod: { ref: "#o1.4.3", label: "Upper board rod" },
|
||||
hingeRod: { ref: "#o1.4.7", label: "Lower board rod" },
|
||||
panelHardware: { ref: "#o1.4.4,o1.4.5,o1.4.6,o1.4.8", label: "Panel nuts" }
|
||||
},
|
||||
parameters: {
|
||||
flip: { type: "number", label: "Flip", description: "Closed to open travel. The animation loops this 0 -> 1 -> 0.", default: 0, min: 0, max: 1, step: 0.01 },
|
||||
extension: { type: "number", label: "Extension", description: "How close the lower rocker drives to the over-center tangent pose.", default: 1, min: 0.2, max: 1, step: 0.01 },
|
||||
turnover: { type: "number", label: "Turnover", description: "Enables the returning coupler to use the open branch after the over-center pose.", default: 1, min: 0, max: 1, step: 1 },
|
||||
explode: { type: "number", label: "Explode", description: "Separates moving groups for inspection after the linkage motion is applied.", default: 0, min: 0, max: 1, step: 0.01 },
|
||||
showFrame: { type: "boolean", label: "Frame", default: true },
|
||||
showDetails: { type: "boolean", label: "Frame details", default: true },
|
||||
showCrossRods: { type: "boolean", label: "Cross rods", default: true },
|
||||
showHardware: { type: "boolean", label: "Hardware", default: true },
|
||||
highlightLinks: { type: "boolean", label: "Highlight moving parts", default: false }
|
||||
},
|
||||
animations: {
|
||||
flipLoop: {
|
||||
label: "Reference flip loop",
|
||||
description: "Closed -> over-center -> open -> over-center -> closed.",
|
||||
duration: 5,
|
||||
loop: true,
|
||||
update({ cycle, set }) {
|
||||
set("flip", cycleFlip(cycle));
|
||||
}
|
||||
},
|
||||
inspectExplode: {
|
||||
label: "Inspect explode",
|
||||
duration: 5,
|
||||
loop: true,
|
||||
update({ cycle, set }) {
|
||||
set("flip", cycleFlip(cycle));
|
||||
set("explode", Math.sin((((finite(cycle, 0) % 1) + 1) % 1) * Math.PI));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update({ params, effects }) {
|
||||
const extension = clamp(params.extension, 0.2, 1);
|
||||
const turnover = clamp(params.turnover, 0, 1);
|
||||
const explode = clamp01(params.explode);
|
||||
const pose = sampleMotion(params.flip, { extension, turnover });
|
||||
|
||||
const panelAngleDelta = pose.angleDeg - INITIAL_PANEL_ANGLE_DEG;
|
||||
const panelTranslate = translateVector(PIVOTS.lowerBoard, pose.lower);
|
||||
const blueAngleDelta = angleDeg2(PIVOTS.upperFrame, pose.upper) - INITIAL_BLUE_ANGLE_DEG;
|
||||
const lowerRockerAngleDelta = pose.lowerAngleDeg - INITIAL_LOWER_ROCKER_ANGLE_DEG;
|
||||
|
||||
applyVisibility(effects, params);
|
||||
|
||||
effects.style(FRAME_PARTS, { opacity: 1, edgeOpacity: 0.9 });
|
||||
effects.style(FRAME_DETAIL_PARTS, { opacity: 1, edgeOpacity: 0.8 });
|
||||
effects.style(HARDWARE, { opacity: 1, edgeOpacity: 0.7 });
|
||||
effects.style("panel", { opacity: 1, edgeOpacity: 1 });
|
||||
effects.style(["rocker2", "greenRod"], { opacity: 1, edgeOpacity: 1 });
|
||||
effects.style("rockerBlue", { opacity: 1, edgeOpacity: 1 });
|
||||
effects.style("redLink", { opacity: 1, edgeOpacity: 1 });
|
||||
effects.style(CROSS_RODS, { opacity: 1, edgeOpacity: 1 });
|
||||
|
||||
if (params.highlightLinks !== false) {
|
||||
effects.highlight(MOVING_PARTS, true);
|
||||
}
|
||||
|
||||
effects.transform(PANEL_COUPLER_PARTS, {
|
||||
transforms: [
|
||||
{
|
||||
rotate: {
|
||||
axis: Z_AXIS,
|
||||
origin: PIVOTS.lowerBoard,
|
||||
angleDeg: panelAngleDelta
|
||||
}
|
||||
},
|
||||
{ translate: [panelTranslate[0] + (explode * 16), panelTranslate[1] - (explode * 8), panelTranslate[2] + (explode * 24)] }
|
||||
]
|
||||
});
|
||||
|
||||
effects.transform("rockerBlue", {
|
||||
transforms: [
|
||||
{
|
||||
rotate: {
|
||||
axis: Z_AXIS,
|
||||
origin: PIVOTS.upperFrame,
|
||||
angleDeg: blueAngleDelta
|
||||
}
|
||||
},
|
||||
{ translate: [-explode * 10, -explode * 8, explode * 18] }
|
||||
]
|
||||
});
|
||||
|
||||
effects.transform(LOWER_ROCKER_PARTS, {
|
||||
transforms: [
|
||||
{
|
||||
rotate: {
|
||||
axis: Z_AXIS,
|
||||
origin: PIVOTS.lowerFrame,
|
||||
angleDeg: lowerRockerAngleDelta
|
||||
}
|
||||
},
|
||||
{ translate: [0, explode * 10, explode * 14] }
|
||||
]
|
||||
});
|
||||
|
||||
effects.transform("*", { matrix: FLAT_ON_GROUND_TRANSFORM });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,287 @@
|
||||
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)] }
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,288 @@
|
||||
const Z_AXIS = [0, 0, 1];
|
||||
|
||||
const GEAR_PITCH_RADIUS_MM = 20;
|
||||
const MAX_GEAR_ANGLE_DEG = 82;
|
||||
|
||||
const LEFT_GEAR_CENTER = [-40, 0, 14.000004];
|
||||
const RIGHT_GEAR_CENTER = [40, 0, 14.000004];
|
||||
const LEFT_CRANK_PIN_INITIAL = [-40, -15, 14.000004];
|
||||
const RIGHT_CRANK_PIN_INITIAL = [40, -15, 14.000004];
|
||||
const LEFT_PISTON_PIN_INITIAL = [-20, -10, 14.000004];
|
||||
const RIGHT_PISTON_PIN_INITIAL = [20, -10, 14.000004];
|
||||
|
||||
const LINK_LENGTH_MM = distance2(LEFT_PISTON_PIN_INITIAL, LEFT_CRANK_PIN_INITIAL);
|
||||
const INITIAL_LEFT_LINK_ANGLE_DEG = angleDeg2(LEFT_PISTON_PIN_INITIAL, LEFT_CRANK_PIN_INITIAL);
|
||||
const INITIAL_RIGHT_LINK_ANGLE_DEG = angleDeg2(RIGHT_PISTON_PIN_INITIAL, RIGHT_CRANK_PIN_INITIAL);
|
||||
|
||||
const MOVING_FEATURES = [
|
||||
"piston",
|
||||
"leftConrod",
|
||||
"rightConrod",
|
||||
"leftPinion",
|
||||
"rightPinion",
|
||||
"leftRack",
|
||||
"rightRack"
|
||||
];
|
||||
|
||||
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 distance2(start, end) {
|
||||
return Math.hypot(end[0] - start[0], end[1] - start[1]);
|
||||
}
|
||||
|
||||
function angleDeg2(start, end) {
|
||||
return Math.atan2(end[1] - start[1], end[0] - start[0]) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
function rotatePointZ(point, origin, angleDeg) {
|
||||
const angleRad = angleDeg * Math.PI / 180;
|
||||
const cosA = Math.cos(angleRad);
|
||||
const sinA = Math.sin(angleRad);
|
||||
const dx = point[0] - origin[0];
|
||||
const dy = point[1] - origin[1];
|
||||
return [
|
||||
origin[0] + (dx * cosA) - (dy * sinA),
|
||||
origin[1] + (dx * sinA) + (dy * cosA),
|
||||
point[2]
|
||||
];
|
||||
}
|
||||
|
||||
function normalizeAngleDeg(value) {
|
||||
let angle = finite(value, 0);
|
||||
while (angle > 180) angle -= 360;
|
||||
while (angle < -180) angle += 360;
|
||||
return angle;
|
||||
}
|
||||
|
||||
function solvePistonY(leftCrankPin) {
|
||||
const dx = LEFT_PISTON_PIN_INITIAL[0] - leftCrankPin[0];
|
||||
const allowedDx = clamp(dx, -LINK_LENGTH_MM + 1e-6, LINK_LENGTH_MM - 1e-6);
|
||||
const dy = Math.sqrt(Math.max(1e-6, (LINK_LENGTH_MM * LINK_LENGTH_MM) - (allowedDx * allowedDx)));
|
||||
|
||||
// The uploaded STEP starts with the piston pin slightly ahead of the crank
|
||||
// pin. Keep that same branch so the conrod never flips through the gear.
|
||||
return leftCrankPin[1] + dy;
|
||||
}
|
||||
|
||||
function samplePose(rawStroke) {
|
||||
const stroke = smooth01(rawStroke);
|
||||
const leftGearAngleDeg = stroke * MAX_GEAR_ANGLE_DEG;
|
||||
const rightGearAngleDeg = -leftGearAngleDeg;
|
||||
const rackTravelMm = (leftGearAngleDeg * Math.PI / 180) * GEAR_PITCH_RADIUS_MM;
|
||||
|
||||
const leftCrankPin = rotatePointZ(LEFT_CRANK_PIN_INITIAL, LEFT_GEAR_CENTER, leftGearAngleDeg);
|
||||
const rightCrankPin = rotatePointZ(RIGHT_CRANK_PIN_INITIAL, RIGHT_GEAR_CENTER, rightGearAngleDeg);
|
||||
const pistonY = solvePistonY(leftCrankPin);
|
||||
const pistonDeltaY = pistonY - LEFT_PISTON_PIN_INITIAL[1];
|
||||
|
||||
const leftPistonPin = [LEFT_PISTON_PIN_INITIAL[0], pistonY, LEFT_PISTON_PIN_INITIAL[2]];
|
||||
const rightPistonPin = [RIGHT_PISTON_PIN_INITIAL[0], pistonY, RIGHT_PISTON_PIN_INITIAL[2]];
|
||||
|
||||
return {
|
||||
stroke,
|
||||
pistonDeltaY,
|
||||
rackTravelMm,
|
||||
leftGearAngleDeg,
|
||||
rightGearAngleDeg,
|
||||
leftLinkAngleDeltaDeg: normalizeAngleDeg(angleDeg2(leftPistonPin, leftCrankPin) - INITIAL_LEFT_LINK_ANGLE_DEG),
|
||||
rightLinkAngleDeltaDeg: normalizeAngleDeg(angleDeg2(rightPistonPin, rightCrankPin) - INITIAL_RIGHT_LINK_ANGLE_DEG)
|
||||
};
|
||||
}
|
||||
|
||||
function cycleStroke(cycle) {
|
||||
const phase = ((finite(cycle, 0) % 1) + 1) % 1;
|
||||
if (phase < 0.38) {
|
||||
return smooth01(phase / 0.38);
|
||||
}
|
||||
if (phase < 0.50) {
|
||||
return 1;
|
||||
}
|
||||
if (phase < 0.88) {
|
||||
return 1 - smooth01((phase - 0.50) / 0.38);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function applyVisibility(effects, params) {
|
||||
effects.visible("*", true);
|
||||
effects.visible("hardware", params.showHardware !== false);
|
||||
}
|
||||
|
||||
export default {
|
||||
manifest: {
|
||||
schemaVersion: 1,
|
||||
step: {
|
||||
path: "models/thang010146/gear_rack_gripper.step"
|
||||
},
|
||||
label: "Robot gripper gear-rack assembly",
|
||||
description: "Uploaded STEP assembly with viewer-time animation based on the reference video: a sliding piston drives two blue conrods, counter-rotating pinions, and opposing rack jaws.",
|
||||
units: {
|
||||
length: "mm",
|
||||
angle: "deg",
|
||||
time: "s"
|
||||
},
|
||||
features: {
|
||||
base: { ref: "#o1.1.10", label: "Fixed base plate" },
|
||||
cylinder: { ref: "#o1.1.9", label: "Fixed green cylinder" },
|
||||
piston: { ref: "#o1.1.17", label: "Sliding piston rod" },
|
||||
leftRack: { ref: "#o1.1.11", label: "Left moving rack jaw" },
|
||||
rightRack: { ref: "#o1.1.12", label: "Right moving rack jaw" },
|
||||
leftPinion: {
|
||||
ref: "#o1.1.13",
|
||||
label: "Left yellow pinion and crank",
|
||||
origin: LEFT_GEAR_CENTER,
|
||||
axis: Z_AXIS
|
||||
},
|
||||
rightPinion: {
|
||||
ref: "#o1.1.14",
|
||||
label: "Right yellow pinion and crank",
|
||||
origin: RIGHT_GEAR_CENTER,
|
||||
axis: Z_AXIS
|
||||
},
|
||||
leftConrod: { ref: "#o1.1.15", label: "Left blue connecting rod" },
|
||||
rightConrod: { ref: "#o1.1.16", label: "Right blue connecting rod" },
|
||||
hardware: {
|
||||
ref: "#o1.1.1,#o1.1.2,#o1.1.3,#o1.1.4,#o1.1.5,#o1.1.6,#o1.1.7,#o1.1.8",
|
||||
label: "Fixed bolts and pins"
|
||||
},
|
||||
movingAssembly: {
|
||||
ref: "#o1.1.11,#o1.1.12,#o1.1.13,#o1.1.14,#o1.1.15,#o1.1.16,#o1.1.17",
|
||||
label: "Moving drive train"
|
||||
}
|
||||
},
|
||||
parameters: {
|
||||
stroke: {
|
||||
type: "number",
|
||||
label: "Stroke",
|
||||
description: "0 is the imported closed pose; 1 pushes the piston forward and opens the two rack jaws with the pinion pitch relationship preserved.",
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
},
|
||||
explode: {
|
||||
type: "number",
|
||||
label: "Explode",
|
||||
description: "Separates the moving groups after the solved pose for mechanism inspection.",
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
},
|
||||
showHardware: { type: "boolean", label: "Hardware", default: true },
|
||||
highlightMoving: { type: "boolean", label: "Highlight moving", default: false }
|
||||
},
|
||||
animations: {
|
||||
driveLoop: {
|
||||
label: "Piston rack drive",
|
||||
description: "Closed pose, piston-driven rack opening, short dwell, then return.",
|
||||
duration: 6,
|
||||
loop: true,
|
||||
update({ cycle, set }) {
|
||||
set("stroke", cycleStroke(cycle));
|
||||
}
|
||||
},
|
||||
inspectDrive: {
|
||||
label: "Exploded drive inspection",
|
||||
duration: 6,
|
||||
loop: true,
|
||||
update({ cycle, set }) {
|
||||
const stroke = cycleStroke(cycle);
|
||||
set("stroke", stroke);
|
||||
set("explode", Math.sin(stroke * Math.PI));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update({ params, effects }) {
|
||||
const pose = samplePose(params.stroke);
|
||||
const explode = clamp01(params.explode);
|
||||
|
||||
applyVisibility(effects, params);
|
||||
|
||||
if (params.highlightMoving === true) {
|
||||
effects.highlight(MOVING_FEATURES, true);
|
||||
}
|
||||
|
||||
effects.transform("piston", {
|
||||
translate: [0, pose.pistonDeltaY, explode * 12]
|
||||
});
|
||||
|
||||
effects.transform("leftConrod", {
|
||||
transforms: [
|
||||
{
|
||||
rotate: {
|
||||
axis: Z_AXIS,
|
||||
origin: LEFT_PISTON_PIN_INITIAL,
|
||||
angleDeg: pose.leftLinkAngleDeltaDeg
|
||||
}
|
||||
},
|
||||
{ translate: [0, pose.pistonDeltaY, explode * 16] }
|
||||
]
|
||||
});
|
||||
|
||||
effects.transform("rightConrod", {
|
||||
transforms: [
|
||||
{
|
||||
rotate: {
|
||||
axis: Z_AXIS,
|
||||
origin: RIGHT_PISTON_PIN_INITIAL,
|
||||
angleDeg: pose.rightLinkAngleDeltaDeg
|
||||
}
|
||||
},
|
||||
{ translate: [0, pose.pistonDeltaY, explode * 16] }
|
||||
]
|
||||
});
|
||||
|
||||
effects.transform("leftPinion", {
|
||||
transforms: [
|
||||
{
|
||||
rotate: {
|
||||
axis: Z_AXIS,
|
||||
origin: LEFT_GEAR_CENTER,
|
||||
angleDeg: pose.leftGearAngleDeg
|
||||
}
|
||||
},
|
||||
{ translate: [-explode * 8, -explode * 5, explode * 18] }
|
||||
]
|
||||
});
|
||||
|
||||
effects.transform("rightPinion", {
|
||||
transforms: [
|
||||
{
|
||||
rotate: {
|
||||
axis: Z_AXIS,
|
||||
origin: RIGHT_GEAR_CENTER,
|
||||
angleDeg: pose.rightGearAngleDeg
|
||||
}
|
||||
},
|
||||
{ translate: [explode * 8, -explode * 5, explode * 18] }
|
||||
]
|
||||
});
|
||||
|
||||
effects.transform("leftRack", {
|
||||
translate: [-pose.rackTravelMm - (explode * 14), 0, explode * 8]
|
||||
});
|
||||
|
||||
effects.transform("rightRack", {
|
||||
translate: [pose.rackTravelMm + (explode * 14), 0, explode * 8]
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
# Mechanism STEP Demos
|
||||
|
||||
Flattened STEP mechanism assemblies inspired by the YouTube channel
|
||||
[thang010146](https://www.youtube.com/@thang010146/videos). Original
|
||||
mechanism design, animation, and downloadable source files are credited to
|
||||
`thang010146`.
|
||||
|
||||
This folder is intentionally flat so CAD Viewer can catalog the top-level STEP
|
||||
assemblies and their hidden sidecars directly.
|
||||
|
||||
## Contents
|
||||
|
||||
- `*.step`: canonical mechanism assemblies.
|
||||
- `.*.step.glb`: generated render/selector sidecars paired with STEP files.
|
||||
- `.*.step.js`: optional interaction sidecars paired with STEP files.
|
||||
|
||||
Extracted source archives, Inventor files, SDF working files, videos, and
|
||||
intermediate generation scripts are intentionally omitted from this fixture
|
||||
bundle.
|
||||
|
||||
## Manifest
|
||||
|
||||
| Mechanism | STEP | Required sidecar | Optional sidecar | Source |
|
||||
|---|---|---|---|---|
|
||||
| 180 deg. flip mechanism | [180_degree_flip_mechanism.step](180_degree_flip_mechanism.step) | `.180_degree_flip_mechanism.step.glb` | `.180_degree_flip_mechanism.step.js` | [Video](https://www.youtube.com/watch?v=IGexfslM_5Y), [STEP archive](https://www.mediafire.com/file/pcjk004x96r6ibu/180FlipMechanismSTEP.zip/file) |
|
||||
| Table of adjustable height 2 | [adjustable_height_table_2.step](adjustable_height_table_2.step) | `.adjustable_height_table_2.step.glb` | `.adjustable_height_table_2.step.js` | [Video](https://www.youtube.com/watch?v=c30g2UszMws), [STEP archive](https://www.mediafire.com/file/ulf0n6zbbp1veo4/TableAdjustHeight2STEP.zip/file) |
|
||||
| Circular window iris shutter 2 | [circular_iris_shutter_2.step](circular_iris_shutter_2.step) | `.circular_iris_shutter_2.step.glb` | | [Video](https://www.youtube.com/watch?v=4ckPpHpe4TM), [STEP archive](https://www.mediafire.com/file/sz86hz0w9aknnen/CircularIrisShutter2STEP.zip/file) |
|
||||
| Deployable drone | [deployable_drone.step](deployable_drone.step) | `.deployable_drone.step.glb` | | [Video](https://www.youtube.com/watch?v=QYt_wUP52T0), [STEP archive](https://www.mediafire.com/file/7533syysab1zhrg/DeployableDroneSTEP.zip/file) |
|
||||
| Robot gripper of gear-rack drives | [gear_rack_gripper.step](gear_rack_gripper.step) | `.gear_rack_gripper.step.glb` | `.gear_rack_gripper.step.js` | [Video](https://www.youtube.com/watch?v=CP5q6YxyeQ8), user upload `RobotGripperGearRackSTEP.zip` |
|
||||
| Hinge of 2 DoF | [hinge_2_dof.step](hinge_2_dof.step) | `.hinge_2_dof.step.glb` | | [Video](https://www.youtube.com/watch?v=Mvmi2AyNqx0), [STEP archive](https://www.mediafire.com/file/nl2m3asaa31dvnf/Hinge2DoFSTEP.zip/file) |
|
||||
| Kinetic pyramid facade | [kinetic_pyramid_facade.step](kinetic_pyramid_facade.step) | `.kinetic_pyramid_facade.step.glb` | | [Video](https://www.youtube.com/watch?v=wA9d9uJIBGw), [STEP archive](https://www.mediafire.com/file/vri2hopfzj0k731/KineticPyramidFacadeSTEP.zip/file) |
|
||||
| Knuckle joint press | [knuckle_joint_press.step](knuckle_joint_press.step) | `.knuckle_joint_press.step.glb` | | [Video](https://www.youtube.com/watch?v=Vd-vErlQPPk), [STEP archive](https://www.mediafire.com/file/8cy26hg2dlovi1b/KnuckleJointPressSTEP.zip/file) |
|
||||
| Meat hash machine | [meat_hash_machine.step](meat_hash_machine.step) | `.meat_hash_machine.step.glb` | | [Video](https://www.youtube.com/watch?v=QVlepLAtYJk), [STEP archive](https://www.mediafire.com/file/q1hksit7cbdm8c6/MeatHashMachineSTEP.zip/file) |
|
||||
| Object moving along 8-shaped runway | [object_8_shaped_runway.step](object_8_shaped_runway.step) | `.object_8_shaped_runway.step.glb` | | [Video](https://www.youtube.com/watch?v=KUD4qqCNmZk), [STEP archive](https://www.mediafire.com/file/lrpeog3wavxumek/Object8runwaySTEP.zip/file) |
|
||||
| Planetary gearless mechanism 2 | [planetary_gearless_mechanism_2.step](planetary_gearless_mechanism_2.step) | `.planetary_gearless_mechanism_2.step.glb` | | [Video](https://www.youtube.com/watch?v=Vf1mpbv8DO4), [STEP archive](https://www.mediafire.com/file/jpwetalgq5ctqvr/PlanetaryGearlessMechanism2STEP.zip/file) |
|
||||
| RC plane retractable landing gear | [rc_plane_retractable_landing_gear.step](rc_plane_retractable_landing_gear.step) | `.rc_plane_retractable_landing_gear.step.glb` | | [Video](https://www.youtube.com/watch?v=J8K3uAWMSRo), [STEP archive](https://www.mediafire.com/file/ma5cw3isx7oncfw/RCPlaneRetractableLandingGearSTEP.zip/file) |
|
||||
| Screw 3-stage telescopic actuator 2b | [screw_3_stage_telescopic_actuator_2b.step](screw_3_stage_telescopic_actuator_2b.step) | `.screw_3_stage_telescopic_actuator_2b.step.glb` | | [Video](https://www.youtube.com/watch?v=LvDbXBtNrlQ), [STEP archive](https://www.mediafire.com/file/ohra998t0bjy8qj/Screw3stageTelescopicActuator2bSTEP.zip/file) |
|
||||
| Spatial planetary belt drive | [spatial_planetary_belt_drive.step](spatial_planetary_belt_drive.step) | `.spatial_planetary_belt_drive.step.glb` | | [Video](https://www.youtube.com/watch?v=wnk6a-lBYCg), [STEP archive](https://www.mediafire.com/file/gbmj0t4ad11ely9/SpatialPlanetaryBeltSTEP.zip/file) |
|
||||
| Static balancing mechanism using gears and spring | [static_balancing_gears_spring.step](static_balancing_gears_spring.step) | `.static_balancing_gears_spring.step.glb` | | [Video](https://www.youtube.com/watch?v=WnDc7tv79HM), [STEP archive](https://www.mediafire.com/file/1y4nqt4jer40uyc/StaticBalancingMechanismGearSpringSTEP.zip/file) |
|
||||
| Two cam mechanism for drawing rectangles | [two_cam_draw_rectangles.step](two_cam_draw_rectangles.step) | `.two_cam_draw_rectangles.step.glb` | | [Video](https://www.youtube.com/watch?v=rEmLvLY65E8), [STEP archive](https://www.mediafire.com/file/09cbcf5of21szpn/TwoCamDrawRectangleSTEP.zip/file) |
|
||||
| Two input shaft differential bevel gearbox | [two_input_shaft_differential_bevel_gearbox.step](two_input_shaft_differential_bevel_gearbox.step) | `.two_input_shaft_differential_bevel_gearbox.step.glb` | | [Video](https://www.youtube.com/watch?v=MLw1eTFPnDE), [STEP archive](https://www.mediafire.com/file/e09s8h9yoxe5luz/TwoInputShaftGearboxSTEP.zip/file) |
|
||||
| Two stage gear multiplier | [two_stage_gear_multiplier.step](two_stage_gear_multiplier.step) | `.two_stage_gear_multiplier.step.glb` | | [Video](https://www.youtube.com/watch?v=y9Z5eSPG2TU), [STEP archive](https://www.mediafire.com/file/yt8nu74susrgcv3/TwoStageGearMultiplierSTEP.zip/file) |
|
||||
| Converting two way linear motion into one way rotation 9b | [two_way_linear_to_one_way_rotation_9b.step](two_way_linear_to_one_way_rotation_9b.step) | `.two_way_linear_to_one_way_rotation_9b.step.glb` | | [Video](https://www.youtube.com/watch?v=PE_Wfw5BZXk), [STEP archive](https://www.mediafire.com/file/gue5rvk6b4dgt1s/Convert2WLto1WR9bSTEP.zip/file) |
|
||||
| Typewriter desk | [typewriter_desk.step](typewriter_desk.step) | `.typewriter_desk.step.glb` | | [Video](https://www.youtube.com/watch?v=OTLIxTTfnWk), [STEP archive](https://www.mediafire.com/file/xk4fsefxcdl3mtr/TypewriterDeskSTEP.zip/file) |
|
||||
| Wall mounted car shed | [wall_mounted_car_shed.step](wall_mounted_car_shed.step) | `.wall_mounted_car_shed.step.glb` | | [Video](https://www.youtube.com/watch?v=SnVo3Zg83Pk), [STEP archive](https://www.mediafire.com/file/gepccrx2g9h3d3b/WallMountedCarShedSTEP.zip/file) |
|
||||
Reference in New Issue
Block a user