/************************************************************************************** Author-Brian Ekins Description-Creates a spur gear component. AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE UNINTERRUPTED OR ERROR FREE. ***************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _USE_MATH_DEFINES #include #include #if defined(_WINDOWS) || defined(_WIN32) || defined(_WIN64) #include #else #include #endif using namespace adsk::core; using namespace adsk::fusion; // Globals Ptr _app; Ptr _ui; std::string _units = ""; // Global command input declarations. Ptr _imgInputEnglish; Ptr _imgInputMetric; Ptr _standard; Ptr _pressureAngle; Ptr _pressureAngleCustom; Ptr _backlash; Ptr _diaPitch; Ptr _module; Ptr _numTeeth; Ptr _rootFilletRad; Ptr _thickness; Ptr _holeDiam; Ptr _pitchDiam; Ptr _errMessage; bool getCommandInputValue(Ptr commandInput, std::string unitType, double *value); bool is_digits(const std::string &str); Ptr drawGear(Ptr design, double diametralPitch, int numTeeth, double thickness, double rootFilletRad, double pressureAngle, double backlash, double holeDiam); bool checkReturn(Ptr returnObj) { if (returnObj) return true; else if (_app && _ui) { std::string errDesc; _app->getLastError(&errDesc); _ui->messageBox(errDesc); return false; } else return false; } // Event handler for the execute event. class GearCommandExecuteEventHandler : public adsk::core::CommandEventHandler { public: void notify(const Ptr& eventArgs) override { double diaPitch = 0.0; if( _standard->selectedItem()->name() == "English") { diaPitch = _diaPitch->value(); } else if (_standard->selectedItem()->name() == "Metric") { diaPitch = 25.4 / _module->value(); } // Save the current values as attributes. Ptr des = _app->activeProduct(); Ptr attribs = des->attributes(); attribs->add("SpurGear", "standard", _standard->selectedItem()->name()); attribs->add("SpurGear", "pressureAngle", _pressureAngle->selectedItem()->name()); attribs->add("SpurGear", "pressureAngleCustom", std::to_string(_pressureAngleCustom->value())); attribs->add("SpurGear", "diaPitch", std::to_string(diaPitch)); attribs->add("SpurGear", "numTeeth", _numTeeth->value()); attribs->add("SpurGear", "rootFilletRad", std::to_string(_rootFilletRad->value())); attribs->add("SpurGear", "thickness", std::to_string(_thickness->value())); attribs->add("SpurGear", "holeDiam", std::to_string(_holeDiam->value())); attribs->add("SpurGear", "backlash", std::to_string(_backlash->value())); // Get the current values. double pressureAngle = 0.0; if (_pressureAngle->selectedItem()->name() == "Custom") { pressureAngle = _pressureAngleCustom->value(); } else { if (_pressureAngle->selectedItem()->name() == "14.5 deg") { pressureAngle = 14.5 * (M_PI/180.0); } else if (_pressureAngle->selectedItem()->name() == "20 deg") { pressureAngle = 20.0 * (M_PI/180.0); } else if (_pressureAngle->selectedItem()->name() == "25 deg") { pressureAngle = 25.0 * (M_PI/180); } } int numTeeth = std::stoi(_numTeeth->value()); double rootFilletRad = _rootFilletRad->value(); double thickness = _thickness->value(); double holeDiam = _holeDiam->value(); double backlash = _backlash->value(); // Create the gear. Ptr gearComp; gearComp = drawGear(des, diaPitch, numTeeth, thickness, rootFilletRad, pressureAngle, backlash, holeDiam); if (gearComp) { std::string desc = ""; if (_standard->selectedItem()->name() == "English") { desc = "Spur Gear; Diametrial Pitch: " + std::to_string(diaPitch) + "; "; } else if (_standard->selectedItem()->name() == "Metric") { desc = "Spur Gear; Module: " + std::to_string(25.4 / diaPitch) + "; "; } desc += "Num Teeth: " + std::to_string(numTeeth) + "; "; desc += "Pressure Angle: " + std::to_string(pressureAngle * (180/M_PI)) + "; "; desc += "Backlash: " + des->unitsManager()->formatInternalValue(backlash, _units, true); gearComp->description(desc); } else { eventArgs->executeFailed(true); eventArgs->executeFailedMessage("Unexpected failure while constructing the gear."); } } } _gearCommandExecute; class GearCommandInputChangedHandler : public adsk::core::InputChangedEventHandler { public: void notify(const Ptr& eventArgs) override { Ptr changedInput = eventArgs->input(); if (changedInput->id() == "standard") { if (_standard->selectedItem()->name() == "English") { _imgInputMetric->isVisible(false); _imgInputEnglish->isVisible(true); _diaPitch->isVisible(true); _module->isVisible(false); _diaPitch->value(25.4 / _module->value()); _units = "in"; } else if (_standard->selectedItem()->name() == "Metric") { _imgInputMetric->isVisible(true); _imgInputEnglish->isVisible(false); _diaPitch->isVisible(false); _module->isVisible(true); _module->value(25.4 / _diaPitch->value()); _units = "mm"; } // Set each one to it's current value because otherwised if the user // has edited it, the value won't update in the dialog because // apparently it remembers the units when the value was edited. // Setting the value using the API resets this. _backlash->value(_backlash->value()); _backlash->unitType(_units); _rootFilletRad->value(_rootFilletRad->value()); _rootFilletRad->unitType(_units); _thickness->value(_thickness->value()); _thickness->unitType(_units); _holeDiam->value(_holeDiam->value()); _holeDiam->unitType(_units); } // Update the pitch diameter value. double diaPitch = 0; if (_standard->selectedItem()->name() == "English") { double value; if (getCommandInputValue(_diaPitch, "", &value)) { diaPitch = value; } } else if (_standard->selectedItem()->name() == "Metric") { double value; if (getCommandInputValue(_module, "", &value)) { diaPitch = 25.4 / value; } } if (diaPitch != 0) { if (is_digits(_numTeeth->value())) { double numTeeth = std::stoi(_numTeeth->value()); double pitchDia = numTeeth/diaPitch; // The pitch dia has been calculated in inches, but this expects cm as the input units. Ptr des = _app->activeProduct(); std::string pitchDiaText = des->unitsManager()->formatInternalValue(pitchDia * 2.54, _units, true); _pitchDiam->text(pitchDiaText); } else { _pitchDiam->text(""); } } else { _pitchDiam->text(""); } if (changedInput->id() == "pressureAngle") { if (_pressureAngle->selectedItem()->name() == "Custom") { _pressureAngleCustom->isVisible(true); } else { _pressureAngleCustom->isVisible(false); } } } } _gearCommandInputChanged; class GearCommandValidateInputsEventHandler : public adsk::core::ValidateInputsEventHandler { public: void notify(const Ptr& eventArgs) override { _errMessage->text(""); // Verify that at lesat 4 teath are specified. int numTeeth; if (!is_digits(_numTeeth->value())) { _errMessage->text("The number of teeth must be a whole number."); eventArgs->areInputsValid(false); return; } else { numTeeth = std::stoi(_numTeeth->value()); } if (numTeeth < 4) { _errMessage->text("The number of teeth must be 4 or more."); eventArgs->areInputsValid(false); return; } // Calculate some of the gear sizes to use in validation. double diaPitch = 0.0; if (_standard->selectedItem()->name() == "English") { double value; if (!getCommandInputValue(_diaPitch, "", &value)) { eventArgs->areInputsValid(false); return; } else { diaPitch = value; } } else if (_standard->selectedItem()->name() == "Metric") { double value; if (!getCommandInputValue(_module, "", &value)) { eventArgs->areInputsValid(false); return; } else { diaPitch = 25.4 / value; } } double diametralPitch = diaPitch / 2.54; double pitchDia = numTeeth / diametralPitch; double dedendum; if (diametralPitch < (20.0 *(M_PI/180.0))-0.000001) { dedendum = 1.157 / diametralPitch; } else { double circularPitch = M_PI / diametralPitch; if (circularPitch >= 20.0) { dedendum = 1.25 / diametralPitch; } else { dedendum = (1.2 / diametralPitch) + (.002 * 2.54); } } double rootDia = pitchDia - (2.0 * dedendum); double pressureAngle = 0.0; if (_pressureAngle->selectedItem()->name() == "Custom") { pressureAngle = _pressureAngleCustom->value(); } else { if (_pressureAngle->selectedItem()->name() == "14.5 deg") pressureAngle = 14.5 * (M_PI/180.0); else if (_pressureAngle->selectedItem()->name() == "20 deg") pressureAngle = 20.0 * (M_PI/180.0); else if (_pressureAngle->selectedItem()->name() == "25 deg") pressureAngle = 25.0 * (M_PI/180.0); } double baseCircleDia = pitchDia * cos(pressureAngle); double baseCircleCircumference = 2.0 * M_PI * (baseCircleDia / 2.0); Ptr des = _app->activeProduct(); double holeDiam; double value; if (!getCommandInputValue(_holeDiam, _units, &value)) { eventArgs->areInputsValid(false); return; } else { holeDiam = value; } if (holeDiam >= (rootDia - 0.01)) { _errMessage->text("The center hole diameter is too large. It must be less than " + des->unitsManager()->formatInternalValue(rootDia - 0.01, _units, true)); eventArgs->areInputsValid(false); return; } double toothThickness = baseCircleCircumference / (numTeeth * 2); if (_rootFilletRad->value() > toothThickness * 0.4) { _errMessage->text("The root fillet radius is too large. It must be less than " + des->unitsManager()->formatInternalValue(toothThickness * 0.4, _units, true)); eventArgs->areInputsValid(false); return; } } } _gearCommandValidateInputs; class SpurGearCommandCreatedEventHandler : public adsk::core::CommandCreatedEventHandler { public: void notify(const Ptr& eventArgs) override { // Verify that a Fusion design is active. Ptr des = _app->activeProduct(); if (!checkReturn(des)) { _ui->messageBox("A Fusion design must be active when invoking this command."); return; } std::string defaultUnits = des->unitsManager()->defaultLengthUnits(); // Determine whether to use inches or millimeters as the intial default. if (defaultUnits == "in" || defaultUnits == "ft") { _units = "in"; } else { _units = "mm"; } // Define the default values and get the previous values from the attributes. std::string standard; if (_units == "in") { standard = "English"; } else { standard = "Metric"; } Ptr standardAttrib = des->attributes()->itemByName("SpurGear", "standard"); if (checkReturn(standardAttrib)) standard = standardAttrib->value(); if (standard == "English") { _units = "in"; } else { _units = "mm"; } std::string pressureAngle = "20 deg"; Ptr pressureAngleAttrib = des->attributes()->itemByName("SpurGear", "pressureAngle"); if (checkReturn(pressureAngleAttrib)) { pressureAngle = pressureAngleAttrib->value(); } double pressureAngleCustom = 20 * (M_PI/180.0); Ptr pressureAngleCustomAttrib = des->attributes()->itemByName("SpurGear", "pressureAngleCustom"); if (checkReturn(pressureAngleCustomAttrib)) { pressureAngleCustom = std::stod(pressureAngleCustomAttrib->value()); } std::string diaPitch = "2"; Ptr diaPitchAttrib = des->attributes()->itemByName("SpurGear", "diaPitch"); if (checkReturn(diaPitchAttrib)) { diaPitch = diaPitchAttrib->value(); } double metricModule = 25.4 / std::stod(diaPitch); std::string backlash = "0"; Ptr backlashAttrib = des->attributes()->itemByName("SpurGear", "backlash"); if (checkReturn(backlashAttrib)) backlash = backlashAttrib->value(); std::string numTeeth = "24"; Ptr numTeethAttrib = des->attributes()->itemByName("SpurGear", "numTeeth"); if (checkReturn(numTeethAttrib)) numTeeth = numTeethAttrib->value(); std::string rootFilletRad = std::to_string(.0625 * 2.54); Ptr rootFilletRadAttrib = des->attributes()->itemByName("SpurGear", "rootFilletRad"); if (checkReturn(rootFilletRadAttrib)) rootFilletRad = rootFilletRadAttrib->value(); std::string thickness = std::to_string(0.5 * 2.54); Ptr thicknessAttrib = des->attributes()->itemByName("SpurGear", "thickness"); if (checkReturn(thicknessAttrib)) thickness = thicknessAttrib->value(); std::string holeDiam = std::to_string(0.5 * 2.54); Ptr holeDiamAttrib = des->attributes()->itemByName("SpurGear", "holeDiam"); if (checkReturn(holeDiamAttrib)) holeDiam = holeDiamAttrib->value(); Ptr cmd = eventArgs->command(); cmd->isExecutedWhenPreEmpted(false); Ptr inputs = cmd->commandInputs(); if (!checkReturn(inputs)) return; // Define the command dialog. _imgInputEnglish = inputs->addImageCommandInput("gearImageEnglish", "", "Resources/GearEnglish.png"); if (!checkReturn(_imgInputEnglish)) return; _imgInputEnglish->isFullWidth(true); _imgInputMetric = inputs->addImageCommandInput("gearImageMetric", "", "Resources/GearMetric.png"); if (!checkReturn(_imgInputMetric)) return; _imgInputMetric->isFullWidth(true); _standard = inputs->addDropDownCommandInput("standard", "Standard", TextListDropDownStyle); if (!checkReturn(_standard)) return; if (standard == "English") { _standard->listItems()->add("English", true); _standard->listItems()->add("Metric", false); _imgInputMetric->isVisible(false); } else { _standard->listItems()->add("English", false); _standard->listItems()->add("Metric", true); _imgInputEnglish->isVisible(false); } _pressureAngle = inputs->addDropDownCommandInput("pressureAngle", "Pressure Angle", TextListDropDownStyle); if (!checkReturn(_pressureAngle)) return; if (pressureAngle == "14.5 deg") { _pressureAngle->listItems()->add("14.5 deg", true); } else { _pressureAngle->listItems()->add("14.5 deg", false); } if (pressureAngle == "20 deg") { _pressureAngle->listItems()->add("20 deg", true); } else { _pressureAngle->listItems()->add("20 deg", false); } if (pressureAngle == "25 deg") { _pressureAngle->listItems()->add("25 deg", true); } else { _pressureAngle->listItems()->add("25 deg", false); } if (pressureAngle == "Custom") { _pressureAngle->listItems()->add("Custom", true); } else { _pressureAngle->listItems()->add("Custom", false); } _pressureAngleCustom = inputs->addValueInput("pressureAngleCustom", "Custom Angle", "deg", ValueInput::createByReal(pressureAngleCustom)); if (!checkReturn(_pressureAngleCustom)) return; if (pressureAngle != "Custom") { _pressureAngleCustom->isVisible(false); } _diaPitch = inputs->addValueInput("diaPitch", "Diametral Pitch", "", ValueInput::createByString(diaPitch)) ; _module = inputs->addValueInput("module", "Module", "", ValueInput::createByReal(metricModule)) ; if (!checkReturn(_module)) return; if (standard == "English") { _module->isVisible(false); } else if (standard == "Metric") { _diaPitch->isVisible(false); } _numTeeth = inputs->addStringValueInput("numTeeth", "Number of Teeth", numTeeth); if (!checkReturn(_pressureAngleCustom)) return; _backlash = inputs->addValueInput("backlash", "Backlash", _units, ValueInput::createByReal(std::stod(backlash))); if (!checkReturn(_backlash)) return; _rootFilletRad = inputs->addValueInput("rootFilletRad", "Root Fillet Radius", _units, ValueInput::createByReal(std::stod(rootFilletRad))); if (!checkReturn(_rootFilletRad)) return; _thickness = inputs->addValueInput("thickness", "Gear Thickness", _units, ValueInput::createByReal(std::stod(thickness))); if (!checkReturn(_thickness)) return; _holeDiam = inputs->addValueInput("holeDiam", "Hole Diameter", _units,ValueInput::createByReal(std::stod(holeDiam))); if (!checkReturn(_holeDiam)) return; _pitchDiam = inputs->addTextBoxCommandInput("pitchDiam", "Pitch Diameter", "", 1, true); if (!checkReturn(_pitchDiam)) return; _errMessage = inputs->addTextBoxCommandInput("errMessage", "", "", 2, true); if (!checkReturn(_errMessage)) return; _errMessage->isFullWidth(true); // Connect to the command related events. Ptr inputChangedEvent = cmd->inputChanged(); if (!inputChangedEvent) return; bool isOk = inputChangedEvent->add(&_gearCommandInputChanged); if (!isOk) return; Ptr validateInputsEvent = cmd->validateInputs(); if (!validateInputsEvent) return; isOk = validateInputsEvent->add(&_gearCommandValidateInputs); if (!isOk) return; Ptr executeEvent = cmd->execute(); if (!executeEvent) return; isOk = executeEvent->add(&_gearCommandExecute); if (!isOk) return; } } _gearCommandCreated; extern "C" XI_EXPORT bool run(const char* context) { _app = Application::get(); if (!_app) return false; _ui = _app->userInterface(); if (!_ui) return false; // Create a command definition and add a button to the CREATE panel. Ptr cmdDef = _ui->commandDefinitions()->addButtonDefinition("adskSpurGearCPPAddIn", "Spur Gear", "Creates a spur gear component", "Resources/SpurGear"); if (!checkReturn(cmdDef)) return false; Ptr createPanel = _ui->allToolbarPanels()->itemById("SolidCreatePanel"); if (!checkReturn(createPanel)) return false; Ptr gearButton = createPanel->controls()->addCommand(cmdDef); if (!checkReturn(gearButton)) return false; // Connect to the command created event. Ptr commandCreatedEvent = cmdDef->commandCreated(); if (!checkReturn(commandCreatedEvent)) return false; bool isOk = commandCreatedEvent->add(&_gearCommandCreated); if (!isOk) return false; std::string strContext = context; if (strContext.find("IsApplicationStartup", 0) != std::string::npos) { if (strContext.find("false", 0) != std::string::npos) { _ui->messageBox("The \"Spur Gear\" command has been added\nto the CREATE panel of the MODEL workspace."); } } return true; } extern "C" XI_EXPORT bool stop(const char* context) { Ptr createPanel = _ui->allToolbarPanels()->itemById("SolidCreatePanel"); if (!checkReturn(createPanel)) return false; Ptr gearButton = createPanel->controls()->itemById("adskSpurGearCPPAddIn"); if (checkReturn(gearButton)) gearButton->deleteMe(); Ptr cmdDef = _ui->commandDefinitions()->itemById("adskSpurGearCPPAddIn"); if (checkReturn(cmdDef)) cmdDef->deleteMe(); return true; } bool is_digits(const std::string &str) { return str.find_first_not_of("0123456789") == std::string::npos; } /* * Verfies that a value command input has a valid expression and returns the * value if it does. Otherwise it returns False. This works around a * problem where when you get the value from a ValueCommandInput it causes the * current expression to be evaluated and updates the display. Some new functionality * is being added in the future to the ValueCommandInput object that will make * this easier and should make this function obsolete. */ bool getCommandInputValue(Ptr commandInput, std::string unitType, double *value) { Ptr valCommandInput = commandInput; if (!commandInput) { *value = 0; return false; } // Verify that the expression is valid. Ptr des = _app->activeProduct(); Ptr unitsMgr = des->unitsManager(); if (unitsMgr->isValidExpression(valCommandInput->expression(), unitType)) { *value = unitsMgr->evaluateExpression(valCommandInput->expression(), unitType); return true; } else { *value = 0; return false; } } // Calculate points along an involute curve. Ptr involutePoint(double baseCircleRadius, double distFromCenterToInvolutePoint) { // Calculate the other side of the right-angle triangle defined by the base circle and the current distance radius. // This is also the length of the involute chord as it comes off of the base circle. double triangleSide = sqrt(pow(distFromCenterToInvolutePoint, 2.0) - pow(baseCircleRadius, 2.0)); // Calculate the angle of the involute. double alpha = triangleSide / baseCircleRadius; // Calculate the angle where the current involute point is. double theta = alpha - acos(baseCircleRadius / distFromCenterToInvolutePoint); // Calculate the coordinates of the involute point. double x = distFromCenterToInvolutePoint * cos(theta); double y = distFromCenterToInvolutePoint * sin(theta); // Create a point to return. return( adsk::core::Point3D::create(x, y, 0) ); } // Builds a spur gear. Ptr drawGear(Ptr design, double diametralPitch, int numTeeth, double thickness, double rootFilletRad, double pressureAngle, double backlash, double holeDiam) { // The diametral pitch is specified in inches but everthing // here expects all distances to be in centimeters, so convert // for the gear creation. diametralPitch = diametralPitch / 2.54; // Compute the various values for a gear. double pitchDia = (double)numTeeth / diametralPitch; //addendum = 1.0 / diametralPitch double dedendum; if (diametralPitch < (20 *(M_PI/180.0))-0.000001) { dedendum = 1.157 / diametralPitch; } else { double circularPitch = M_PI / diametralPitch; if (circularPitch >= 20.0) { dedendum = 1.25 / diametralPitch; } else { dedendum = (1.2 / diametralPitch) + (.002 * 2.54); } } double rootDia = pitchDia - (2.0 * dedendum); double baseCircleDia = pitchDia * cos(pressureAngle); double outsideDia = (double)(numTeeth + 2) / diametralPitch; // Create a new component by creating an occurrence. Ptr occs = design->rootComponent()->occurrences(); if (!checkReturn(occs)) return nullptr; Ptr mat = adsk::core::Matrix3D::create(); if (!checkReturn(mat)) return nullptr; Ptr newOcc = occs->addNewComponent(mat); if (!checkReturn(newOcc)) return nullptr; Ptr newComp = newOcc->component(); if (!checkReturn(newComp)) return nullptr; // Create a new sketch. Ptr sketches = newComp->sketches(); if (!checkReturn(sketches)) return nullptr; Ptr xyPlane = newComp->xYConstructionPlane(); if (!checkReturn(xyPlane)) return nullptr; Ptr baseSketch = sketches->add(xyPlane); if (!checkReturn(xyPlane)) return nullptr; // Draw a circle for the base. baseSketch->sketchCurves()->sketchCircles()->addByCenterRadius(adsk::core::Point3D::create(0,0,0), rootDia/2.0); if (!checkReturn(baseSketch)) return nullptr; // Draw a circle for the center hole, if the value is greater than 0. Ptr prof = nullptr; if (holeDiam - (_app->pointTolerance() * 2) > 0) { Ptr circ = baseSketch->sketchCurves()->sketchCircles()->addByCenterRadius(adsk::core::Point3D::create(0,0,0), holeDiam/2.0); if (!checkReturn(circ)) return nullptr; // Find the profile that uses both circles. for (Ptr tempProf : baseSketch->profiles()) { if (tempProf->profileLoops()->count() == 2) { prof = tempProf; break; } } } else { // Use the single profile. prof = baseSketch->profiles()->item(0); } if (!checkReturn(prof)) return nullptr; //////// Extrude the circle to create the base of the gear. // Create an extrusion input to be able to define the input needed for an extrusion // while specifying the profile and that a new component is to be created Ptr extrudes = newComp->features()->extrudeFeatures(); if (!checkReturn(extrudes)) return nullptr; Ptr extInput = extrudes->createInput(prof, NewBodyFeatureOperation); if (!checkReturn(extInput)) return nullptr; // Define that the extent is a distance extent of 5 cm. Ptr distance = adsk::core::ValueInput::createByReal(thickness); if (!checkReturn(distance)) return nullptr; bool result = extInput->setDistanceExtent(false, distance); if (!result) return nullptr; // Create the extrusion. Ptr baseExtrude = extrudes->add(extInput); if (!checkReturn(baseExtrude)) return nullptr; // Create a second sketch for the tooth. Ptr toothSketch = sketches->add(xyPlane); if (!checkReturn(toothSketch)) return nullptr; // Calculate points along the involute curve. int involutePointCount = 15; double involuteIntersectionRadius = baseCircleDia / 2.0; Ptr *involutePoints = new Ptr[involutePointCount]; std::unique_ptr[]> involutePointsDeleter(involutePoints); double involuteSize = (outsideDia - baseCircleDia) / 2.0; for (int i=0; i newPoint = involutePoint(baseCircleDia / 2.0, involuteIntersectionRadius); involutePoints[i] = newPoint; } // Get the point along the tooth that's at the pictch diameter and then // calculate the angle to that point. Ptr pitchInvolutePoint = involutePoint(baseCircleDia / 2.0, pitchDia / 2.0); double pitchPointAngle = atan(pitchInvolutePoint->y() / pitchInvolutePoint->x()); // Determine the angle defined by the tooth thickness as measured at // the pitch diameter circle. double toothThicknessAngle = (2.0 * M_PI) / (2.0 * (double)numTeeth); // Determine the angle needed for the specified backlash. double backlashAngle = (backlash / (pitchDia / 2.0)) * 0.25; // Determine the angle to rotate the curve. double rotateAngle = -((toothThicknessAngle/2.0) + pitchPointAngle - backlashAngle); // Rotate the involute so the middle of the tooth lies on the x axis. double cosAngle = cos(rotateAngle); double sinAngle = sin(rotateAngle); for (int i=0; ix() * cosAngle - involutePoints[i]->y() * sinAngle; double newY = involutePoints[i]->x() * sinAngle + involutePoints[i]->y() * cosAngle; involutePoints[i] = adsk::core::Point3D::create(newX, newY, 0); } // Create a new set of points with a negated y. This effectively mirrors the original // points about the X axis. Ptr *involute2Points = new Ptr[involutePointCount]; std::unique_ptr[]> involute2PointsDeleter(involute2Points); for (int i=0; ix(), -involutePoints[i]->y(), 0); } double *curve1Angle = new double[involutePointCount]; std::unique_ptr curve1AngleDeleter(curve1Angle); for (int i=0; iy() / involutePoints[i]->x()); } double *curve2Angle = new double[involutePointCount]; std::unique_ptr curve2AngleDeleter(curve2Angle); for (int i=0; iy() / involute2Points[i]->x()); } toothSketch->isComputeDeferred(true); // Create and load an object collection with the points. Ptr pointSet = adsk::core::ObjectCollection::create(); for (int i=0; iadd(involutePoints[i]); } // Create the first spline. Ptr spline1 = toothSketch->sketchCurves()->sketchFittedSplines()->add(pointSet); if (!checkReturn(spline1)) return nullptr; // Add the involute points for the second spline to an ObjectCollection. pointSet = adsk::core::ObjectCollection::create(); for (int i=0; iadd(involute2Points[i]); } // Create the second spline. Ptr spline2 = toothSketch->sketchCurves()->sketchFittedSplines()->add(pointSet); if (!checkReturn(spline2)) return nullptr; // Draw the arc for the top of the tooth. Ptr midPoint = adsk::core::Point3D::create((outsideDia / 2.0), 0, 0); Ptr topArc = toothSketch->sketchCurves()->sketchArcs()->addByThreePoints(spline1->endSketchPoint(), midPoint, spline2->endSketchPoint()); if (!checkReturn(topArc)) return nullptr; // Check to see if involute goes down to the root or not. If not, then // create lines to connect the involute to the root. if (baseCircleDia < rootDia) { Ptr bottomLine = toothSketch->sketchCurves()->sketchLines()->addByTwoPoints(spline2->startSketchPoint(), spline1->startSketchPoint()); if (!checkReturn(bottomLine)) return nullptr; } else { Ptr rootPoint1 = adsk::core::Point3D::create((rootDia / 2 - 0.001) * cos(curve1Angle[0] ), (rootDia / 2) * sin(curve1Angle[0]), 0); Ptr line1 = toothSketch->sketchCurves()->sketchLines()->addByTwoPoints(rootPoint1, spline1->startSketchPoint()); if (!checkReturn(line1)) return nullptr; Ptr rootPoint2 = adsk::core::Point3D::create((rootDia / 2 - 0.001) * cos(curve2Angle[0]), (rootDia / 2) * sin(curve2Angle[0]), 0); Ptr line2 = toothSketch->sketchCurves()->sketchLines()->addByTwoPoints(rootPoint2, spline2->startSketchPoint()); if (!checkReturn(line2)) return nullptr; Ptr bottomLine = toothSketch->sketchCurves()->sketchLines()->addByTwoPoints(line1->startSketchPoint(), line2->startSketchPoint()); if (!checkReturn(bottomLine)) return nullptr; // Make the lines tangent to the spline so the root fillet will behave correctly. if (!line1->isFixed(true)) return nullptr; if (!line2->isFixed(true)) return nullptr; Ptr geomConstraints = toothSketch->geometricConstraints(); if (!checkReturn(geomConstraints)) return nullptr; Ptr tangent = geomConstraints->addTangent(spline1, line1); if (!checkReturn(tangent)) return nullptr; tangent = geomConstraints->addTangent(spline2, line2); if (!checkReturn(tangent)) return nullptr; } toothSketch->isComputeDeferred(false); ////// Extrude the tooth. // Get the profile defined by the tooth. prof = toothSketch->profiles()->item(0); if (!checkReturn(prof)) return nullptr; // Create an extrusion input to be able to define the input needed for an extrusion // while specifying the profile and that a new component is to be created extInput = extrudes->createInput(prof, JoinFeatureOperation); if (!checkReturn(extInput)) return nullptr; // Define that the extent is a distance extent of 5 cm. distance = adsk::core::ValueInput::createByReal(thickness); if (!checkReturn(distance)) return nullptr; result = extInput->setDistanceExtent(false, distance); if (!result) return nullptr; // Create the extrusion. Ptr toothExtrude = extrudes->add(extInput); if (!checkReturn(toothExtrude)) return nullptr; Ptr baseFillet; if (rootFilletRad > 0) { ////// Find the edges between the base cylinder and the tooth. // Get the outer cylindrical face from the base extrusion by checking the number // of edges and if it's 2 get the other one. Ptr cylFace = baseExtrude->sideFaces()->item(0); if (cylFace->edges()->count() == 2) { cylFace = baseExtrude->sideFaces()->item(1); } if (!checkReturn(cylFace)) return nullptr; // Get the two linear edges, which are the connection between the cylinder and tooth. Ptr edges = adsk::core::ObjectCollection::create(); for (Ptr edge : cylFace->edges()) { Ptr tempLine = edge->geometry(); if (tempLine) { edges->add(edge); } } // Create a fillet input to be able to define the input needed for a fillet. Ptr fillets = newComp->features()->filletFeatures(); if (!checkReturn(fillets)) return nullptr; Ptr filletInput = fillets->createInput(); if (!checkReturn(filletInput)) return nullptr; // Define that the extent is a distance extent of 5 cm. Ptr radius = adsk::core::ValueInput::createByReal(rootFilletRad); if (!checkReturn(radius)) return nullptr; result = filletInput->addConstantRadiusEdgeSet(edges, radius, false); if (!result) return nullptr; // Create the extrusion. baseFillet = fillets->add(filletInput); if (!checkReturn(baseFillet)) return nullptr; } // Create a pattern of the tooth extrude and the base fillet. Ptr circularPatterns = newComp->features()->circularPatternFeatures(); if (!checkReturn(circularPatterns)) return nullptr; Ptr entities = adsk::core::ObjectCollection::create(); entities->add(toothExtrude); if (baseFillet) { entities->add(baseFillet); } Ptr cylFace = baseExtrude->sideFaces()->item(0); if (!checkReturn(cylFace)) return nullptr; Ptr patternInput = circularPatterns->createInput(entities, cylFace); if (!checkReturn(patternInput)) return nullptr; Ptr numTeethInput = adsk::core::ValueInput::createByString(std::to_string(numTeeth)); if (!checkReturn(numTeethInput)) return nullptr; patternInput->quantity(numTeethInput); patternInput->patternComputeOption(adsk::fusion::PatternComputeOptions::IdenticalPatternCompute); Ptr pattern = circularPatterns->add(patternInput); if (!checkReturn(pattern)) return nullptr; // Create an extra sketch that contains a circle of the diametral pitch. Ptr diametralPitchSketch = sketches->add(xyPlane); if (!checkReturn(diametralPitchSketch)) return nullptr; Ptr diametralPitchCircle = diametralPitchSketch->sketchCurves()->sketchCircles()->addByCenterRadius(adsk::core::Point3D::create(0,0,0), pitchDia/2.0); if (!checkReturn(diametralPitchCircle)) return nullptr; diametralPitchCircle->isConstruction(true); diametralPitchCircle->isFixed(true); // Group everything used to create the gear in the timeline. Ptr timelineGroups = design->timeline()->timelineGroups(); if (!checkReturn(timelineGroups)) return nullptr; int newOccIndex = newOcc->timelineObject()->index(); int pitchSketchIndex = diametralPitchSketch->timelineObject()->index(); Ptr timelineGroup = timelineGroups->add(newOccIndex, pitchSketchIndex); if (!checkReturn(timelineGroup)) return nullptr; timelineGroup->name("Spur Gear"); // Add an attribute to the component with all of the input values. This might // be used in the future to be able to edit the gear. std::string gearValues = "{"; gearValues += "'pressureAngle': '" + std::to_string(pressureAngle) + "',"; gearValues += "'numTeeth': '" + std::to_string(numTeeth) + "',"; gearValues += "'backlash': '" + std::to_string(backlash) + "',"; gearValues += "'holeDiam': '" + std::to_string(holeDiam) + "',"; gearValues += "'thickness': '" + std::to_string(thickness) + "',"; gearValues += "'rootFilletRad': '" + std::to_string(rootFilletRad) + "',"; gearValues += "'diametralPitch': '" + std::to_string(diametralPitch * 2.54) + "'}"; Ptr attrib = newComp->attributes()->add("SpurGear", "Values", gearValues); if (!checkReturn(attrib)) return nullptr; // Set the name of the component. newComp->name("Spur Gear (" + std::to_string(numTeeth) + " teeth)"); return newComp; } #ifdef XI_WIN #include BOOL APIENTRY DllMain(HMODULE hmodule, DWORD reason, LPVOID reserved) { switch (reason) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } #endif // XI_WIN