Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.

First time visiting? Here are some places to start:
  1. Looking for a certain topic? Check out the categories filter or use Search (upper right).
  2. Need support? Ask a question to our Community Support category.
  3. Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
  4. 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.

Help with for loop and incrementing variable

mason_weavermason_weaver Member Posts: 6
I'm trying to write a script that will help make a crankshaft. It is supposed to take a basic unit of the crankshaft, translate it (and copy it), then rotate it. Then it will repeat the process until it has satisfied the proper number of segments. Here is what I'm having trouble with right now: 
for (var i = 1; i < definition.instances; i += 1)
        {
            linearPattern(context, id + "linearPattern" + i, {
               "patternType" : PatternType.PART,
               "entities" : definition.startBody,
               "directionOne" : definition.frontFace,
               "distance" : (movement.distance * i),
               "instanceCount" : 2,
               "oppositeDirection" : true,
               "operationType" : NewBodyOperationType.NEW
            });
       
            opTransform(context, id + "transform" + i, {
                "bodies" : qCreatedBy(id + "linearPattern" + i, EntityType.BODY),
                "transform" : rotationAround(rotationAxis, definition.twist)
            }); 
        }

It works like it should with only the linear pattern, but once I add the rotation, it doesn't work. It worked before I added the loop, without "i." What do I need to change to get "bodies" under opTransform to query the body created by the linearPattern?

Best Answer

  • Alex_KempenAlex_Kempen Member Posts: 93 EDU
    Accepted Answer
    Addressing your specific question, the reason why your code no longer works when you add the rotation to the for loop is because your ids have an invalid structure, which causes them to fail when you have more than one operation in the for loop. Instead of writing your ids as id + "yourName" + i, write your ids like this: id + ("yourName" ~ i)

    It also looks like your for loop might be terminating an instance too early. Try making your stop condition (the middle statement) i <= definition.instances, or start with i = 0 instead of i = 1.

    As an added bonus, here's how you can condense what you're trying to do to a single opPattern operation. opPattern is actually a lot like opTransform, except it copies instead of patterning. By stacking your transforms correctly, you can bypass the need for the linear pattern feature.
    // get the plane represented by your plane
    const evalautedPlane = evPlane(context, { "face" : definition.frontFace });
    
    // set the direction based on the normal of the plane and the state of definition.flipDirection (which you should add)
    const direction = definition.flipDirection ? -evaluatedPlane.normal : evaluatedPlane.normal;
    
    // start your for loop from zero; it makes indexing into an array possible
    for(var i = 0; i < definition.numInstances; i += 1)
    {
        // multiply your transforms together to apply them one after another (from right to left)
        const computedTransform = rotationAround(rotationAxis, definition.twist) * transform(direction * distance * (i + 1));
       
       // opPattern is like opTransform, but it also takes an instanceNames argument and does our copying for us, too
        opPattern(context, id + ("linearPattern" ~ i), {
            "bodies" : definition.startBody,
            "transform" : [computedTransform],
            "instanceNames" : ["pattern"]
        });
    }
    Lastly, feel free to take a look through some of the heavily annotated FeatureScript examples I have which can be found here. I hope some of this helps!

Answers

  • Alex_KempenAlex_Kempen Member Posts: 93 EDU
    Accepted Answer
    Addressing your specific question, the reason why your code no longer works when you add the rotation to the for loop is because your ids have an invalid structure, which causes them to fail when you have more than one operation in the for loop. Instead of writing your ids as id + "yourName" + i, write your ids like this: id + ("yourName" ~ i)

    It also looks like your for loop might be terminating an instance too early. Try making your stop condition (the middle statement) i <= definition.instances, or start with i = 0 instead of i = 1.

    As an added bonus, here's how you can condense what you're trying to do to a single opPattern operation. opPattern is actually a lot like opTransform, except it copies instead of patterning. By stacking your transforms correctly, you can bypass the need for the linear pattern feature.
    // get the plane represented by your plane
    const evalautedPlane = evPlane(context, { "face" : definition.frontFace });
    
    // set the direction based on the normal of the plane and the state of definition.flipDirection (which you should add)
    const direction = definition.flipDirection ? -evaluatedPlane.normal : evaluatedPlane.normal;
    
    // start your for loop from zero; it makes indexing into an array possible
    for(var i = 0; i < definition.numInstances; i += 1)
    {
        // multiply your transforms together to apply them one after another (from right to left)
        const computedTransform = rotationAround(rotationAxis, definition.twist) * transform(direction * distance * (i + 1));
       
       // opPattern is like opTransform, but it also takes an instanceNames argument and does our copying for us, too
        opPattern(context, id + ("linearPattern" ~ i), {
            "bodies" : definition.startBody,
            "transform" : [computedTransform],
            "instanceNames" : ["pattern"]
        });
    }
    Lastly, feel free to take a look through some of the heavily annotated FeatureScript examples I have which can be found here. I hope some of this helps!
  • mason_weavermason_weaver Member Posts: 6
    Thank you! That was exactly what I needed!
  • mason_weavermason_weaver Member Posts: 6
    Ok so fixing the original method works, but when I try the method using only opPattern, it throws "First operand of logical or conditional operator must be boolean, value is undefined." I think it is having a problem with the direction of the transform.

    Also, would you be able to point me in the direction of merging the created bodies into one, possibly with opCreateCompositePart, or with NewBodyOperationType.ADD?
  • Alex_KempenAlex_Kempen Member Posts: 93 EDU
    If the bodies are disjoint, you'll have to use opCreateCompositePart. If the patterned bodies touch, then opBoolean will let you merge them all together into one part:
    // maybe make this optional and/or check if the input tools are empty
    opBoolean(context, id + "boolean", {
        "tools" : qCreatedBy(id, EntityType.BODY)->qBodyType(BodyType.SOLID), // can also qUnion with input part, if desired
        "operationType" : BooleanOperationType.UNION

    To check if it is the transform which is failing, you can try adding an "is transform" check to your computedTransform declaration. You could also replace the transform with identityTransform() to see if it fixes your problem, which would also mean it is in fact an issue with your transforms.
    const computedTransform is transform = // transforms here
    I might have some theories as to why your transform might be failing, but it's hard to say without seeing your entire code. Is there any chance you could make your document public and post the link here?
  • mason_weavermason_weaver Member Posts: 6
    Update: I figured out how to use opBoolean and a few arrays to achieve what I wanted:

    const array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
            const array2 = resize(array1, definition.instances, 0);
            const toolArray = mapArray(array2, function(x)
                {
                    return qCreatedBy(id + ("linearPattern" ~ x), EntityType.BODY);
                });
            println(toolArray);

            opBoolean(context, id + "boolean1", {
                        "tools" : qUnion(concatenateArrays([[theBody], toolArray])),
                        "operationType" : BooleanOperationType.UNION
                    });

     
    Here is what I have so far
  • Alex_KempenAlex_Kempen Member Posts: 93 EDU
    Looking good! My only comment is that you can probably make life easier for yourself by passing in a parent id to opPattern, then using said parent id to query for all of the entities created by opPattern:
    const patternId = id + "pattern";
    for (var i = 0; i < size(numPattern); i += 1)
    {
        /** Each opPattern has ids as follows:
         *  id + "pattern" + "pattern0"
         *  id + "pattern" + "pattern1"
         *  id + "pattern" + "pattern2"
         *  etc.
         * Since ids are hierarchical (i.e. like a tree):
         * Both qCreatedBy(id, EntityType.BODY) and qCreatedBy(id + "pattern", EntityType.BODY) return all bodies created by the pattern.
         */
        opPattern(context, patternId + "pattern" ~ i, {
            "bodies" : definition.startBody,
            "transform" : computedTransform,
            "instanceNames" : ["pattern"]
        });
    }<br>

Sign In or Register to comment.