1284 lines
46 KiB
C++
1284 lines
46 KiB
C++
/**************************************************************************************
|
|
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 <Core/Utils.h>
|
|
#include <Core/Application/Application.h>
|
|
#include <Core/Application/Product.h>
|
|
#include <Core/Application/ValueInput.h>
|
|
#include <Core/Application/UnitsManager.h>
|
|
#include <Core/Application/ObjectCollection.h>
|
|
#include <Core/Geometry/Point3D.h>
|
|
#include <Core/Geometry/Matrix3D.h>
|
|
#include <Core/Geometry/Surface.h>
|
|
#include <Core/Geometry/Curve3D.h>
|
|
#include <Core/Geometry/Line3D.h>
|
|
#include <Core/UserInterface/UserInterface.h>
|
|
#include <Core/UserInterface/CommandCreatedEventHandler.h>
|
|
#include <Core/UserInterface/CommandCreatedEvent.h>
|
|
#include <Core/UserInterface/CommandCreatedEventArgs.h>
|
|
#include <Core/UserInterface/CommandEvent.h>
|
|
#include <Core/UserInterface/CommandEventArgs.h>
|
|
#include <Core/UserInterface/CommandEventHandler.h>
|
|
#include <Core/UserInterface/ValidateInputsEventHandler.h>
|
|
#include <Core/UserInterface/ValidateInputsEvent.h>
|
|
#include <Core/UserInterface/ValidateInputsEventArgs.h>
|
|
#include <Core/UserInterface/InputChangedEventHandler.h>
|
|
#include <Core/UserInterface/InputChangedEvent.h>
|
|
#include <Core/UserInterface/InputChangedEventArgs.h>
|
|
#include <Core/UserInterface/Command.h>
|
|
#include <Core/UserInterface/CommandDefinition.h>
|
|
#include <Core/UserInterface/CommandDefinitions.h>
|
|
#include <Core/UserInterface/CommandInputs.h>
|
|
#include <Core/UserInterface/ValueCommandInput.h>
|
|
#include <Core/UserInterface/StringValueCommandInput.h>
|
|
#include <Core/UserInterface/DropDownCommandInput.h>
|
|
#include <Core/UserInterface/ListItems.h>
|
|
#include <Core/UserInterface/ListItem.h>
|
|
#include <Core/UserInterface/ImageCommandInput.h>
|
|
#include <Core/UserInterface/TextBoxCommandInput.h>
|
|
#include <Core/UserInterface/ToolbarPanelList.h>
|
|
#include <Core/UserInterface/ToolbarPanels.h>
|
|
#include <Core/UserInterface/ToolbarPanel.h>
|
|
#include <Core/UserInterface/ToolbarControls.h>
|
|
#include <Core/UserInterface/ToolbarControl.h>
|
|
#include <Core/UserInterface/CommandControl.h>
|
|
#include <Core/UserInterface/Workspaces.h>
|
|
#include <Core/UserInterface/Workspace.h>
|
|
#include <Core/Application/Attributes.h>
|
|
#include <Core/Application/Attribute.h>
|
|
#include <Fusion/Fusion/Design.h>
|
|
#include <Fusion/Components/Component.h>
|
|
#include <Fusion/Components/Occurrences.h>
|
|
#include <Fusion/Components/Occurrence.h>
|
|
#include <Fusion/BRep/BRepFaces.h>
|
|
#include <Fusion/BRep/BRepFace.h>
|
|
#include <Fusion/BRep/BRepEdges.h>
|
|
#include <Fusion/BRep/BRepEdge.h>
|
|
#include <Fusion/BRep/BRepBody.h>
|
|
#include <Fusion/Construction/ConstructionPlane.h>
|
|
#include <Fusion/Sketch/Sketches.h>
|
|
#include <Fusion/Sketch/Sketch.h>
|
|
#include <Fusion/Sketch/SketchCurves.h>
|
|
#include <Fusion/Sketch/SketchLines.h>
|
|
#include <Fusion/Sketch/SketchLine.h>
|
|
#include <Fusion/Sketch/SketchArcs.h>
|
|
#include <Fusion/Sketch/SketchArc.h>
|
|
#include <Fusion/Sketch/SketchPoint.h>
|
|
#include <Fusion/Sketch/SketchFittedSplines.h>
|
|
#include <Fusion/Sketch/SketchFittedSpline.h>
|
|
#include <Fusion/Sketch/SketchCircles.h>
|
|
#include <Fusion/Sketch/SketchCircle.h>
|
|
#include <Fusion/Sketch/GeometricConstraints.h>
|
|
#include <Fusion/Sketch/TangentConstraint.h>
|
|
#include <Fusion/Sketch/Profile.h>
|
|
#include <Fusion/Sketch/Profiles.h>
|
|
#include <Fusion/Sketch/ProfileLoops.h>
|
|
#include <Fusion/Features/Features.h>
|
|
#include <Fusion/Features/ExtrudeFeatures.h>
|
|
#include <Fusion/Features/ExtrudeFeatureInput.h>
|
|
#include <Fusion/Features/ExtrudeFeature.h>
|
|
#include <Fusion/Features/FilletFeatures.h>
|
|
#include <Fusion/Features/FilletFeatureInput.h>
|
|
#include <Fusion/Features/FilletFeature.h>
|
|
#include <Fusion/Features/CircularPatternFeatures.h>
|
|
#include <Fusion/Features/CircularPatternFeatureInput.h>
|
|
#include <Fusion/Features/CircularPatternFeature.h>
|
|
#include <Fusion/Fusion/Timeline.h>
|
|
#include <Fusion/Fusion/TimelineObject.h>
|
|
#include <Fusion/Fusion/TimelineGroups.h>
|
|
#include <Fusion/Fusion/TimelineGroup.h>
|
|
|
|
#include <sstream>
|
|
#define _USE_MATH_DEFINES
|
|
#include <math.h>
|
|
#include <memory>
|
|
|
|
|
|
#if defined(_WINDOWS) || defined(_WIN32) || defined(_WIN64)
|
|
#include <windows.h>
|
|
#else
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
using namespace adsk::core;
|
|
using namespace adsk::fusion;
|
|
|
|
|
|
// Globals
|
|
Ptr<Application> _app;
|
|
Ptr<UserInterface> _ui;
|
|
std::string _units = "";
|
|
|
|
// Global command input declarations.
|
|
Ptr<ImageCommandInput> _imgInputEnglish;
|
|
Ptr<ImageCommandInput> _imgInputMetric;
|
|
Ptr<DropDownCommandInput> _standard;
|
|
Ptr<DropDownCommandInput> _pressureAngle;
|
|
Ptr<ValueCommandInput> _pressureAngleCustom;
|
|
Ptr<ValueCommandInput> _backlash;
|
|
Ptr<ValueCommandInput> _diaPitch;
|
|
Ptr<ValueCommandInput> _module;
|
|
Ptr<StringValueCommandInput> _numTeeth;
|
|
Ptr<ValueCommandInput> _rootFilletRad;
|
|
Ptr<ValueCommandInput> _thickness;
|
|
Ptr<ValueCommandInput> _holeDiam;
|
|
Ptr<TextBoxCommandInput> _pitchDiam;
|
|
Ptr<TextBoxCommandInput> _errMessage;
|
|
|
|
bool getCommandInputValue(Ptr<CommandInput> commandInput, std::string unitType, double *value);
|
|
bool is_digits(const std::string &str);
|
|
Ptr<Component> drawGear(Ptr<Design> design, double diametralPitch, int numTeeth, double thickness, double rootFilletRad, double pressureAngle, double backlash, double holeDiam);
|
|
|
|
|
|
bool checkReturn(Ptr<Base> 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<CommandEventArgs>& 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<Design> des = _app->activeProduct();
|
|
Ptr<Attributes> 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<Component> 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<InputChangedEventArgs>& eventArgs) override
|
|
{
|
|
Ptr<CommandInput> 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<Design> 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<ValidateInputsEventArgs>& 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<Design> 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 SpurGearCommandDestroyEventHandler : public adsk::core::CommandEventHandler
|
|
{
|
|
public:
|
|
void notify(const Ptr<CommandEventArgs>& eventArgs) override
|
|
{
|
|
// Terminate the script since the command has finished.
|
|
adsk::terminate();
|
|
}
|
|
} _gearCommandDestroy;
|
|
|
|
class SpurGearCommandCreatedEventHandler : public adsk::core::CommandCreatedEventHandler
|
|
{
|
|
public:
|
|
void notify(const Ptr<CommandCreatedEventArgs>& eventArgs) override
|
|
{
|
|
// Verify that a Fusion design is active.
|
|
Ptr<Design> 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<Attribute> 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<Attribute> pressureAngleAttrib = des->attributes()->itemByName("SpurGear", "pressureAngle");
|
|
if (checkReturn(pressureAngleAttrib))
|
|
{
|
|
pressureAngle = pressureAngleAttrib->value();
|
|
}
|
|
|
|
double pressureAngleCustom = 20 * (M_PI/180.0);
|
|
Ptr<Attribute> pressureAngleCustomAttrib = des->attributes()->itemByName("SpurGear", "pressureAngleCustom");
|
|
if (checkReturn(pressureAngleCustomAttrib))
|
|
{
|
|
pressureAngleCustom = std::stod(pressureAngleCustomAttrib->value());
|
|
}
|
|
|
|
std::string diaPitch = "2";
|
|
Ptr<Attribute> diaPitchAttrib = des->attributes()->itemByName("SpurGear", "diaPitch");
|
|
if (checkReturn(diaPitchAttrib))
|
|
{
|
|
diaPitch = diaPitchAttrib->value();
|
|
}
|
|
double metricModule = 25.4 / std::stod(diaPitch);
|
|
|
|
std::string backlash = "0";
|
|
Ptr<Attribute> backlashAttrib = des->attributes()->itemByName("SpurGear", "backlash");
|
|
if (checkReturn(backlashAttrib))
|
|
backlash = backlashAttrib->value();
|
|
|
|
std::string numTeeth = "24";
|
|
Ptr<Attribute> numTeethAttrib = des->attributes()->itemByName("SpurGear", "numTeeth");
|
|
if (checkReturn(numTeethAttrib))
|
|
numTeeth = numTeethAttrib->value();
|
|
|
|
std::string rootFilletRad = std::to_string(.0625 * 2.54);
|
|
Ptr<Attribute> rootFilletRadAttrib = des->attributes()->itemByName("SpurGear", "rootFilletRad");
|
|
if (checkReturn(rootFilletRadAttrib))
|
|
rootFilletRad = rootFilletRadAttrib->value();
|
|
|
|
std::string thickness = std::to_string(0.5 * 2.54);
|
|
Ptr<Attribute> thicknessAttrib = des->attributes()->itemByName("SpurGear", "thickness");
|
|
if (checkReturn(thicknessAttrib))
|
|
thickness = thicknessAttrib->value();
|
|
|
|
std::string holeDiam = std::to_string(0.5 * 2.54);
|
|
Ptr<Attribute> holeDiamAttrib = des->attributes()->itemByName("SpurGear", "holeDiam");
|
|
if (checkReturn(holeDiamAttrib))
|
|
holeDiam = holeDiamAttrib->value();
|
|
|
|
Ptr<Command> cmd = eventArgs->command();
|
|
cmd->isExecutedWhenPreEmpted(false);
|
|
Ptr<CommandInputs> 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> inputChangedEvent = cmd->inputChanged();
|
|
if (!inputChangedEvent)
|
|
return;
|
|
bool isOk = inputChangedEvent->add(&_gearCommandInputChanged);
|
|
if (!isOk)
|
|
return;
|
|
|
|
Ptr<ValidateInputsEvent> validateInputsEvent = cmd->validateInputs();
|
|
if (!validateInputsEvent)
|
|
return;
|
|
isOk = validateInputsEvent->add(&_gearCommandValidateInputs);
|
|
if (!isOk)
|
|
return;
|
|
|
|
Ptr<CommandEvent> executeEvent = cmd->execute();
|
|
if (!executeEvent)
|
|
return;
|
|
isOk = executeEvent->add(&_gearCommandExecute);
|
|
if (!isOk)
|
|
return;
|
|
|
|
Ptr<CommandEvent> destroyEvent = cmd->destroy();
|
|
if (!destroyEvent)
|
|
return;
|
|
|
|
isOk = destroyEvent->add(&_gearCommandDestroy);
|
|
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<CommandDefinition> cmdDef = _ui->commandDefinitions()->itemById("adskSpurGearCPPScript");
|
|
if (!cmdDef)
|
|
{
|
|
cmdDef = _ui->commandDefinitions()->addButtonDefinition("adskSpurGearCPPScript", "Spur Gear", "Creates a spur gear component", "Resources/SpurGear");
|
|
if (!checkReturn(cmdDef))
|
|
return false;
|
|
}
|
|
|
|
// Connect to the command created event.
|
|
Ptr<CommandCreatedEvent> commandCreatedEvent = cmdDef->commandCreated();
|
|
if (!checkReturn(commandCreatedEvent))
|
|
return false;
|
|
bool isOk = commandCreatedEvent->add(&_gearCommandCreated);
|
|
if (!isOk)
|
|
return false;
|
|
|
|
isOk = cmdDef->execute();
|
|
if (!isOk)
|
|
return false;
|
|
|
|
// Prevent this module from terminating so that the command can continue to run until
|
|
// the user completes the command.
|
|
adsk::autoTerminate(false);
|
|
|
|
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> commandInput, std::string unitType, double *value)
|
|
{
|
|
Ptr<ValueCommandInput> valCommandInput = commandInput;
|
|
if (!commandInput)
|
|
{
|
|
*value = 0;
|
|
return false;
|
|
}
|
|
|
|
// Verify that the expression is valid.
|
|
Ptr<Design> des = _app->activeProduct();
|
|
Ptr<UnitsManager> 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<Point3D> 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<Component> drawGear(Ptr<Design> 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<Occurrences> occs = design->rootComponent()->occurrences();
|
|
if (!checkReturn(occs))
|
|
return nullptr;
|
|
|
|
Ptr<Matrix3D> mat = adsk::core::Matrix3D::create();
|
|
if (!checkReturn(mat))
|
|
return nullptr;
|
|
|
|
Ptr<Occurrence> newOcc = occs->addNewComponent(mat);
|
|
if (!checkReturn(newOcc))
|
|
return nullptr;
|
|
|
|
Ptr<Component> newComp = newOcc->component();
|
|
if (!checkReturn(newComp))
|
|
return nullptr;
|
|
|
|
// Create a new sketch.
|
|
Ptr<Sketches> sketches = newComp->sketches();
|
|
if (!checkReturn(sketches))
|
|
return nullptr;
|
|
|
|
Ptr<ConstructionPlane> xyPlane = newComp->xYConstructionPlane();
|
|
if (!checkReturn(xyPlane))
|
|
return nullptr;
|
|
|
|
Ptr<Sketch> 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<Profile> prof = nullptr;
|
|
if (holeDiam - (_app->pointTolerance() * 2) > 0)
|
|
{
|
|
Ptr<SketchCircle> 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<Profile> 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<ExtrudeFeatures> extrudes = newComp->features()->extrudeFeatures();
|
|
if (!checkReturn(extrudes))
|
|
return nullptr;
|
|
|
|
Ptr<ExtrudeFeatureInput> extInput = extrudes->createInput(prof, NewBodyFeatureOperation);
|
|
if (!checkReturn(extInput))
|
|
return nullptr;
|
|
|
|
// Define that the extent is a distance extent of 5 cm.
|
|
Ptr<ValueInput> 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<ExtrudeFeature> baseExtrude = extrudes->add(extInput);
|
|
if (!checkReturn(baseExtrude))
|
|
return nullptr;
|
|
|
|
// Create a second sketch for the tooth.
|
|
Ptr<Sketch> toothSketch = sketches->add(xyPlane);
|
|
if (!checkReturn(toothSketch))
|
|
return nullptr;
|
|
|
|
// Calculate points along the involute curve.
|
|
int involutePointCount = 15;
|
|
double involuteIntersectionRadius = baseCircleDia / 2.0;
|
|
Ptr<Point3D> *involutePoints = new Ptr<Point3D>[involutePointCount];
|
|
std::unique_ptr<Ptr<Point3D>[]> involutePointsDeleter(involutePoints);
|
|
double involuteSize = (outsideDia - baseCircleDia) / 2.0;
|
|
for (int i=0; i<involutePointCount; ++i)
|
|
{
|
|
involuteIntersectionRadius = (baseCircleDia / 2.0) + ((involuteSize / (involutePointCount - 1)) * i);
|
|
Ptr<Point3D> 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<Point3D> 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; i<involutePointCount; ++i)
|
|
{
|
|
double newX = involutePoints[i]->x() * 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<Point3D> *involute2Points = new Ptr<Point3D>[involutePointCount];
|
|
std::unique_ptr<Ptr<Point3D>[]> involute2PointsDeleter(involute2Points);
|
|
for (int i=0; i<involutePointCount; ++i)
|
|
{
|
|
involute2Points[i] = adsk::core::Point3D::create(involutePoints[i]->x(), -involutePoints[i]->y(), 0);
|
|
}
|
|
|
|
double *curve1Angle = new double[involutePointCount];
|
|
std::unique_ptr<double[]> curve1AngleDeleter(curve1Angle);
|
|
for (int i=0; i<involutePointCount; ++i)
|
|
{
|
|
curve1Angle[i] = atan(involutePoints[i]->y() / involutePoints[i]->x());
|
|
}
|
|
|
|
double *curve2Angle = new double[involutePointCount];
|
|
std::unique_ptr<double[]> curve2AngleDeleter(curve2Angle);
|
|
for (int i=0; i<involutePointCount; ++i)
|
|
{
|
|
curve2Angle[i] = atan(involute2Points[i]->y() / involute2Points[i]->x());
|
|
}
|
|
|
|
toothSketch->isComputeDeferred(true);
|
|
|
|
// Create and load an object collection with the points.
|
|
Ptr<ObjectCollection> pointSet = adsk::core::ObjectCollection::create();
|
|
for (int i=0; i<involutePointCount; ++i)
|
|
{
|
|
pointSet->add(involutePoints[i]);
|
|
}
|
|
|
|
// Create the first spline.
|
|
Ptr<SketchFittedSpline> 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; i<involutePointCount; ++i)
|
|
{
|
|
pointSet->add(involute2Points[i]);
|
|
}
|
|
|
|
// Create the second spline.
|
|
Ptr<SketchFittedSpline> spline2 = toothSketch->sketchCurves()->sketchFittedSplines()->add(pointSet);
|
|
if (!checkReturn(spline2))
|
|
return nullptr;
|
|
|
|
// Draw the arc for the top of the tooth.
|
|
Ptr<Point3D> midPoint = adsk::core::Point3D::create((outsideDia / 2.0), 0, 0);
|
|
Ptr<SketchArc> 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<SketchLine> bottomLine = toothSketch->sketchCurves()->sketchLines()->addByTwoPoints(spline2->startSketchPoint(), spline1->startSketchPoint());
|
|
if (!checkReturn(bottomLine))
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
Ptr<Point3D> rootPoint1 = adsk::core::Point3D::create((rootDia / 2 - 0.001) * cos(curve1Angle[0] ), (rootDia / 2) * sin(curve1Angle[0]), 0);
|
|
Ptr<SketchLine> line1 = toothSketch->sketchCurves()->sketchLines()->addByTwoPoints(rootPoint1, spline1->startSketchPoint());
|
|
if (!checkReturn(line1))
|
|
return nullptr;
|
|
|
|
Ptr<Point3D> rootPoint2 = adsk::core::Point3D::create((rootDia / 2 - 0.001) * cos(curve2Angle[0]), (rootDia / 2) * sin(curve2Angle[0]), 0);
|
|
Ptr<SketchLine> line2 = toothSketch->sketchCurves()->sketchLines()->addByTwoPoints(rootPoint2, spline2->startSketchPoint());
|
|
if (!checkReturn(line2))
|
|
return nullptr;
|
|
|
|
Ptr<SketchLine> 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<GeometricConstraints> geomConstraints = toothSketch->geometricConstraints();
|
|
if (!checkReturn(geomConstraints))
|
|
return nullptr;
|
|
|
|
Ptr<TangentConstraint> 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<ExtrudeFeature> toothExtrude = extrudes->add(extInput);
|
|
if (!checkReturn(toothExtrude))
|
|
return nullptr;
|
|
|
|
Ptr<FilletFeature> 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<BRepFace> 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<ObjectCollection> edges = adsk::core::ObjectCollection::create();
|
|
for (Ptr<BRepEdge> edge : cylFace->edges())
|
|
{
|
|
Ptr<Line3D> tempLine = edge->geometry();
|
|
if (tempLine)
|
|
{
|
|
edges->add(edge);
|
|
}
|
|
}
|
|
|
|
// Create a fillet input to be able to define the input needed for a fillet.
|
|
Ptr<FilletFeatures> fillets = newComp->features()->filletFeatures();
|
|
if (!checkReturn(fillets))
|
|
return nullptr;
|
|
|
|
Ptr<FilletFeatureInput> filletInput = fillets->createInput();
|
|
if (!checkReturn(filletInput))
|
|
return nullptr;
|
|
|
|
// Define that the extent is a distance extent of 5 cm.
|
|
Ptr<ValueInput> 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<CircularPatternFeatures> circularPatterns = newComp->features()->circularPatternFeatures();
|
|
if (!checkReturn(circularPatterns))
|
|
return nullptr;
|
|
|
|
Ptr<ObjectCollection> entities = adsk::core::ObjectCollection::create();
|
|
entities->add(toothExtrude);
|
|
if (baseFillet)
|
|
{
|
|
entities->add(baseFillet);
|
|
}
|
|
|
|
Ptr<BRepFace> cylFace = baseExtrude->sideFaces()->item(0);
|
|
if (!checkReturn(cylFace))
|
|
return nullptr;
|
|
|
|
Ptr<CircularPatternFeatureInput> patternInput = circularPatterns->createInput(entities, cylFace);
|
|
if (!checkReturn(patternInput))
|
|
return nullptr;
|
|
|
|
Ptr<ValueInput> numTeethInput = adsk::core::ValueInput::createByString(std::to_string(numTeeth));
|
|
if (!checkReturn(numTeethInput))
|
|
return nullptr;
|
|
|
|
patternInput->quantity(numTeethInput);
|
|
patternInput->patternComputeOption(adsk::fusion::PatternComputeOptions::IdenticalPatternCompute);
|
|
Ptr<CircularPatternFeature> pattern = circularPatterns->add(patternInput);
|
|
if (!checkReturn(pattern))
|
|
return nullptr;
|
|
|
|
// Create an extra sketch that contains a circle of the diametral pitch.
|
|
Ptr<Sketch> diametralPitchSketch = sketches->add(xyPlane);
|
|
if (!checkReturn(diametralPitchSketch))
|
|
return nullptr;
|
|
|
|
Ptr<SketchCircle> 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> timelineGroups = design->timeline()->timelineGroups();
|
|
if (!checkReturn(timelineGroups))
|
|
return nullptr;
|
|
|
|
int newOccIndex = newOcc->timelineObject()->index();
|
|
int pitchSketchIndex = diametralPitchSketch->timelineObject()->index();
|
|
|
|
Ptr<TimelineGroup> 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<Attribute> 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 <windows.h>
|
|
|
|
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
|