Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.
First time visiting? Here are some places to start:- Looking for a certain topic? Check out the categories filter or use Search (upper right).
- Need support? Ask a question to our Community Support category.
- Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
- Be respectful, on topic and if you see a problem, Flag it.
If you would like to contact our Community Manager personally, feel free to send a private message or an email.
Extrude Profile Feature
jacob_kingery
Member Posts: 39 EDU
Hi everyone,
I'm one of a team of five college students who got the chance to work with FeatureScript this school year, and this is one of the features we made. Let us know what you think!
Extrude Profile is similar to the built-in extrude feature, but it uses predefined profiles from a separate Part Studio instead of requiring a sketch to be selected. To position the extrusion, sketch points or mate connectors in the Part Studio can be selected (selecting multiple creates the extrusion at each location). It sits on top of the existing
Here’s a frame built out of 80/20 aluminum extrusion made using Extrude Profile:
The full code is below:
I'm one of a team of five college students who got the chance to work with FeatureScript this school year, and this is one of the features we made. Let us know what you think!
Extrude Profile is similar to the built-in extrude feature, but it uses predefined profiles from a separate Part Studio instead of requiring a sketch to be selected. To position the extrusion, sketch points or mate connectors in the Part Studio can be selected (selecting multiple creates the extrusion at each location). It sits on top of the existing
extrude()
feature, so it has nearly all of the same capabilities: new/add/remove/intersect, up to next/face/part, second direction, draft, and so on. With the new linked documents, you can have the Extrude Profile Feature and the profiles in one document and use it in your other documents without needing to copy anything over.Here’s a frame built out of 80/20 aluminum extrusion made using Extrude Profile:
The full code is below:
FeatureScript 336;
export import(path : "onshape/std/geometry.fs", version : "336.0");
// The Part Studio that contains sketches of the profiles (here it is "Profiles")
// This will need to be changed if you make a copy of this document
// or want to use a different Part Studio
PROFILES::import(path : "5b738aa78b30dcacc632e732", version : "4d44e3f224a42b96e0225eb4");
// IDs for the different profiles in the imported Part Studio
// These can be found by viewing the Part Studio code and locating each profile
// This will need to be updated with your own profiles
export enum ProfileID
{
annotation { "Name" : "Keyed Shaft"}
FhZrcAwvpS53LxN_0,
annotation { "Name" : "8020 1515" }
FRq293wuvJmfxy1_0,
annotation { "Name" : "8020 1530" }
FNEWGsbExOTWz4f_0,
annotation { "Name" : "8020 1545" }
FKgkJvp8e2Xp26A_0,
annotation { "Name" : "8020 3030" }
Fl1OKjOr6f4slR2_0
}
export enum TransformMethod
{
annotation { "Name" : "Sketch point" }
SKETCHPOINT,
annotation { "Name" : "Mate connector" }
MATECONNECTOR
}
export function mateConnectorTransform(context is Context, mate_connector is Query)
{
return toWorld(evMateConnector(context, {
"mateConnector" : mate_connector
}));
}
export function sketchPointTransform(context is Context, sketch_point is Query, xaxis is Query, angle is ValueWithUnits)
{
var point is Vector = evVertexPoint(context, {
"vertex" : sketch_point
});
var sketch_plane is Plane = evOwnerSketchPlane(context, {
"entity" : sketch_point
});
// Make the point the sketch plane origin
sketch_plane.origin = point;
// Make the reference edge the sketch plane x-axis
if (size(evaluateQuery(context, xaxis)) != 0)
{
var new_x = evLine(context, {"edge" : xaxis}).direction;
if (!perpendicularVectors(sketch_plane.normal, new_x))
{
println("Selected x-axis is not perpendicular to the sketch plane's normal");
throw regenError(ErrorStringEnum.SKETCH_PERPENDICULAR_FAILED, xaxis);
}
sketch_plane.x = new_x;
}
// Rotate the sketch plane x-axis by the specified angle
sketch_plane.x = rotationMatrix3d(sketch_plane.normal, angle) * sketch_plane.x;
return planeToWorld(sketch_plane);
}
annotation { "Feature Type Name" : "Extrude Profile" }
export const extrudeProfile = defineFeature(function(context is Context, id is Id, definition is map)
precondition
{
// BEGINNING OF PRECONDITION FROM EXTRUDE.FS (modified to remove the SURFACE option)
booleanStepTypePredicate(definition);
// Entity input is replaced by our profile enum
annotation { "Name" : "Profile" }
definition.profile_id is ProfileID;
annotation { "Name" : "End type" }
definition.endBound is BoundingType;
if (definition.endBound != BoundingType.SYMMETRIC)
{
annotation { "Name" : "Opposite direction", "UIHint" : "OPPOSITE_DIRECTION" }
definition.oppositeDirection is boolean;
}
if (definition.endBound == BoundingType.UP_TO_SURFACE)
{
annotation { "Name" : "Up to face",
"Filter" : EntityType.FACE && SketchObject.NO,
"MaxNumberOfPicks" : 1 }
definition.endBoundEntityFace is Query;
}
else if (definition.endBound == BoundingType.UP_TO_BODY)
{
annotation { "Name" : "Up to surface or part",
"Filter" : EntityType.BODY && SketchObject.NO,
"MaxNumberOfPicks" : 1 }
definition.endBoundEntityBody is Query;
}
if (definition.endBound == BoundingType.BLIND ||
definition.endBound == BoundingType.SYMMETRIC)
{
annotation { "Name" : "Depth" }
isLength(definition.depth, LENGTH_BOUNDS);
}
annotation { "Name" : "Draft", "UIHint" : "DISPLAY_SHORT" }
definition.hasDraft is boolean;
if (definition.hasDraft == true)
{
annotation { "Name" : "Draft angle", "UIHint" : "DISPLAY_SHORT" }
isAngle(definition.draftAngle, ANGLE_STRICT_90_BOUNDS);
annotation { "Name" : "Opposite direction", "UIHint" : "OPPOSITE_DIRECTION" }
definition.draftPullDirection is boolean;
}
if (definition.endBound != BoundingType.SYMMETRIC)
{
annotation { "Name" : "Second end position" }
definition.hasSecondDirection is boolean;
if (definition.hasSecondDirection)
{
annotation { "Name" : "End type" }
definition.secondDirectionBound is SecondDirectionBoundingType;
annotation { "Name" : "Opposite direction", "UIHint" : "OPPOSITE_DIRECTION", "Default" : true }
definition.secondDirectionOppositeDirection is boolean;
if (definition.secondDirectionBound == SecondDirectionBoundingType.UP_TO_SURFACE)
{
annotation { "Name" : "Up to face",
"Filter" : EntityType.FACE && SketchObject.NO,
"MaxNumberOfPicks" : 1 }
definition.secondDirectionBoundEntityFace is Query;
}
else if (definition.secondDirectionBound == SecondDirectionBoundingType.UP_TO_BODY)
{
annotation { "Name" : "Up to surface or part",
"Filter" : EntityType.BODY && SketchObject.NO,
"MaxNumberOfPicks" : 1 }
definition.secondDirectionBoundEntityBody is Query;
}
if (definition.secondDirectionBound == SecondDirectionBoundingType.BLIND)
{
annotation { "Name" : "Depth" }
isLength(definition.secondDirectionDepth, LENGTH_BOUNDS);
}
if ((definition.secondDirectionOppositeDirection && !definition.oppositeDirection) ||
(!definition.secondDirectionOppositeDirection && definition.oppositeDirection))
{
annotation { "Name" : "Draft", "UIHint" : "DISPLAY_SHORT" }
definition.hasSecondDirectionDraft is boolean;
if (definition.hasSecondDirectionDraft)
{
annotation { "Name" : "Draft angle", "UIHint" : "DISPLAY_SHORT" }
isAngle(definition.secondDirectionDraftAngle, ANGLE_STRICT_90_BOUNDS);
annotation { "Name" : "Opposite direction", "UIHint" : "OPPOSITE_DIRECTION" }
definition.secondDirectionDraftPullDirection is boolean;
}
}
}
}
booleanStepScopePredicate(definition);
// END OF EXTRUDE.FS PRECONDITION
annotation { "Name" : "Transform method" }
definition.transform_method is TransformMethod;
if (definition.transform_method == TransformMethod.MATECONNECTOR)
{
annotation { "Name" : "Mounting mate connector(s)", "Filter" : BodyType.MATE_CONNECTOR }
definition.mounting_connectors is Query;
}
else if (definition.transform_method == TransformMethod.SKETCHPOINT)
{
annotation { "Name" : "Sketch point(s)", "Filter" : EntityType.VERTEX && SketchObject.YES }
definition.sketch_points is Query;
annotation { "Name" : "X-axis", "Filter" : EntityType.EDGE, "MaxNumberOfPicks" : 1 }
definition.xaxis is Query;
annotation { "Name" : "Rotation angle" }
isAngle(definition.angle, ANGLE_360_ZERO_DEFAULT_BOUNDS);
}
}
{
// Get PROFILES's context
var contextWithProfiles is Context = PROFILES::build();
// Delete its construction planes and origin
opDeleteBodies(contextWithProfiles, id + "delete_default_geometry", {
"entities" : qUnion([
qConstructionFilter(qBodyType(qEverything(EntityType.BODY), BodyType.SHEET), ConstructionObject.YES),
qCreatedBy(makeId("Origin"))
])
});
// Add PROFILES's context to the current Part Studio
opMergeContexts(context, id + "add_profiles", {
"contextFrom" : contextWithProfiles
});
// Calculate transforms for each position
var transforms = [];
if (definition.transform_method == TransformMethod.MATECONNECTOR)
{
const num_connectors = size(evaluateQuery(context, definition.mounting_connectors));
for (var i = 0; i < num_connectors; i += 1)
{
transforms = append(transforms, mateConnectorTransform(context, qNthElement(definition.mounting_connectors, i)));
}
}
else if (definition.transform_method == TransformMethod.SKETCHPOINT)
{
const num_points = size(evaluateQuery(context, definition.sketch_points));
for (var i = 0; i < num_points; i += 1)
{
transforms = append(transforms, sketchPointTransform(context, qNthElement(definition.sketch_points, i), definition.xaxis, definition.angle));
}
}
// If there are no transforms, use the original position
if (transforms == []) {
transforms = [identityTransform()];
}
// Transform profile to each location and extrude it there
const profile = qCreatedBy(id + "add_profiles" + (definition.profile_id as string), EntityType.BODY);
definition.entities = qSketchRegion(id + "add_profiles" + (definition.profile_id as string));
var last_tf = identityTransform();
var i = 0;
for (var tf in transforms)
{
opTransform(context, id + ("transform" ~ i), {
"bodies" : profile,
"transform" : tf * inverse(last_tf)
});
extrude(context, id + ("extrude" ~ i), definition);
last_tf = tf;
i += 1;
}
// Delete the imported PROFILES to clean up
opDeleteBodies(context, id + "delete_imported_geometry", {
"entities" : qCreatedBy(id + "add_profiles")
});
}, {});
1
Comments
Congratulations for the FS, It looks very nice. Reminds me the weldment profile used in Solidworks, the main difference is that you make a 3d sketch and use the lines to extrude the profile, I think is faster with the 3D sketch but yours have the same functionality with a different approach.
I'm trying to make something that can use most of your code, I'm developing something like New Extrude based on Raw Material, like a Round Bar, Rectangular tube , angle and etc. The basic ones I can draw a sketch, I already made those the problem is when I you have a specific profile. Could you share your document with me so I can see how do you link the profiles? I will have other issues like re-sizing them but is a good start.
The profiles are located in the Part Studio titled 'Profiles' and the
ProfileID
enum near the top of the script is what contains the IDs that identify the different sketches of the profiles. Let me know if you have any questions, and good luck with your feature!this is soooo cool.
Thanks for sharing,
you should put your name in your code:
I'm not sure what the copy write laws are for featurescripts and the ramification for posting the source code on a public forum. I know I use a lot of javascript from all over the place and if someone has added their name to the code, I don't remove it. Even after it's been obfuscated the name still appears. Give credit where credit is due.
When I copied your shared document, I didn't have to edit the profile links. The "Frame" part studio pointed to "Profiles" just fine. I double checked to make sure it wasn't pointing to your shared document and it wasn't. All references were inside my document and nothing was broke.
Seems like copying the document would have regenerated all new part studio & sketch id's but that didn't happen.
Your copied document worked fine for me.
Yep! "Copy workspace" repoints intra-workspace references to the right things. Same goes if you derive a part within a workspace and copy.
I think Jacob's comments were intended for before he added the public link, since he made this feature before FeatureScript documents could be public. If just copy-pasted his code, the imports would not resolve correctly.
If you want to add your own new profiles to the feature, you will have to modify the FS by adding the new sketch id to the ProfileID enum.
Round bars, SAE 1045, 1"
How would you do that to chose the profile?
I'm thinking about library names beginning with "library" or "lib" so I can perform a quick search in the document manager.
How I would group things:
document named "lib SAE shapes"
part studio named "square bars"
part studio named "round bars"
part studio named "angles"
part studio named "tees"
part studio named "I-beams"
I think this might work. If you build it, please let me know.
You'll have to maintain your enum's to match your part studios. Too bad a featurestudio can't write a featurestudio, this way you could write an app that updates your enum's based on your part studio's in the document.
I also know the app store, I'm thinking libraries really belong over there but it's not for everyone. App Store requires an SSL certificate which isn't cheap. It does give you your own database and your own programming languages. Not sure you can eval(featurescript) and get a featurescript to run from the app store. That's a question for Ilya or kevin. I know I could query "lib SAE shapes" from an app store app and I might be to edit a featurescript. This way I could maintain an index of your libraries and also provide search capabilities down to the sketch in a part studio. At least to the part studio, don't remember if I can get to the sketch.
@billy, that's probably how I'd do it; make a few separate Part Studios for the different
Couple of questions for the Onshape team: I see I can copy a FeatureScript to a new private document. Is it possible to copy it to an existing document? If we can only copy to a new document, how would we use multiple 'external' FeatureScripts? Manually cut-and-paste the code into a document?
Also do you foresee a github/npm style package manager for FeatureScripts so we can always use the latest (or fixed) version of a published script?
Thanks!
i am dont know how to create feature scripts but the other day i needed some profiles for a proyect and i made the sketchs for 45X45 ,40X80 and 40X160 here you have the link for the sketchs maybe they could help you to add to the list.
https://cad.onshape.com/documents/15230bf73e3e7c1ddbc2cc75/w/3310b0b6b8d8fbd172557c7d/e/1844e8d099c480cfce3f691d
wish it helps!
More recently than this thread, we've created a "Beams" feature that has more documentation about how to add your own profiles. Check out this blog post:
https://www.onshape.com/cad-blog/custom-feature-spotlight-using-the-beam-feature-to-create-weldments
The post also links to a webinar that shows how to create custom profiles for the feature step-by-step.
Hope this helps!
i saw it but i didnt have a look at it. it is a great feauture! but is there any possibility to add our sketches to the main feature like send it to the creator or something? so everybody has them with the principal feature.... i am askig because for the moment i didnt beging working with the features myself but i thought i could give my little grain of sand to this community
There are instructions there for how to add your own profiles.