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.

Ray Tracer half transparent mirror

Hi, I'm using the Ray Tracer in Onshape for optical simulation. In my situation I need a half transparent mirror with transmission and reflection. In the Ray Tracer settings I can only set the reflection or the transmission. I need booth. It is possible to set both values? Or is there an other solution?
Thanks for all answares.

Best Answer

Answers

  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,173
    This is not currently possible with that feature -- its source code would have to be modified to support this.
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • norbert_walter937norbert_walter937 Member Posts: 3
    Thanks for your answare. The Ray Tracer is very interesting for me. Specially in combination with mecanical designs. In this case I can check the system functions over the completely design. I d'ont need a full function Ray Tracer. Easy functionality is enough. For a better integration is following necaessary:
    • transmission and reflection at same time
    • easy light source generator with multible beam array
    • roughness as property for objekts (for different reflection directions)
    • wave length as property (refraction calculation)
    • easy lens generator (nice to have)
    Are this topics possible in a future version?

    I think this features are necessary and usefully also for education.
  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,173
    The ray tracer is a sample custom feature developed by @maximilian_schommer while he was a summer intern with us -- it is not a part of Onshape's core functionality and so we do not have resources we can allocate to its development.  If you are comfortable with code, you can add the functionality you describe yourself or you may be able to interest someone in the community (or may be an Onshaper on their own time) to build it.
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • norbert_walter937norbert_walter937 Member Posts: 3
    Thanks for yours answares.
    @ilya_baran : I understand the situation. At the moment I have no experiences with code developping in Onshape. I'll try it soon.
    @philip_thomas :
    This is exactly what I need. The solution is a usefully nice trick. Thanks :-)
  • maximilian_schommermaximilian_schommer Member Posts: 32 ✭✭
    Hi @norbert_walter937

    The ray tracer originally did give both the reflected and refracted ray, and it would be pretty simple to make a check box so that functionality is added to it again.

    I'll see if I can get to it soon! Thanks for the suggestion!

    Maximilian Schommer
  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    Perhaps to avoid a lot of unnecessary rays it would be more helpful to add the 'both' as a characteristic of the body and not of the ray! :)
    Philip Thomas - Onshape
  • maximilian_schommermaximilian_schommer Member Posts: 32 ✭✭
    Thanks @philip_thomas and @norbert_walter937 for the suggestions! 

    I was able to implement the semi-silvered mirror functionality, which appears in the setup as being able to check a box for reflection and refraction for each part selected.  The other suggestions about roughness, wavelength, etc. are great ideas, and I'll hopefully get around to them soon! Release 4 is where the new functionality lives.

    Maximilian Schommer


  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    It occurred to me afterwards that asking for this meant you had to deal with recursion as every bounce produced two more rays to follow!
    Nicely done! :)
    Philip Thomas - Onshape
  • ivan_skachkoivan_skachko Member Posts: 4
    Ray Tracer is indeed a very useful and efficient custom feature, however I encountered a bug in it. As the attached image shows, RT works well tracing a beam in the FRONT plane (the same plane where I sketched lens' profile to be revolved), but fails for the beam with identical parameters in the TOP plane. The problem seems to  stem from feature script code ( var rayEndPoint = getRayIntersection(context, id, rayToTrace); ) not being able to compute the intersection of the ray with the back surface of the lens for some reason. Looking at the script for this feature I am generally curious why it takes so much code to find an intersection between a line and a surface of a body.
    I understand that Ray Tracer will not be developed further by Onshape, but I wanted to bring the issue to the attention of the users.
  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    Philip Thomas - Onshape
  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    @ivan_skachko - Max (the author of this custom feature) may chose to shed light on the other part of your statement, but my understanding based on discussions before Max started working on this, was that the following is true. There is no call in Parasolid to determine the intersection of a line and a body/face. For that reason, Max's code generates an artificial entity (i think its a surface), to be able to get that. I hope that puts your mind at rest :)
     
    Philip Thomas - Onshape
  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
     There is no call in Parasolid to determine the intersection of a line and a body/face.
    but what about evDistance, it can use a Line for one of the sides of measurement as stated in documentation. so you can detect if it intersects the face and evaluate normal in intersection point. or i'm missing something?
  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,173
    @philip_thomas Parasolid definitely has such calls.  They were just not exposed to FS as is.  The problem with evDistance is that it returns some intersection, not just the closest one, which is what is needed here.
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • Jake_RosenfeldJake_Rosenfeld Moderator, Onshape Employees, Developers Posts: 1,646
    @konstantin_shiriazdanov

    There are issues with the evDistance approach.  If you use it to intersect a Line and a cylindrical face which it passes through twice, it will be very hard to detect the situation, and hard to gather the other intersections using evDistance alone.  Also looping though every face of the selected bodies and running an evDistance could get very expensive.

    Parasolid does support arbitrary intersections between curves and faces, but this is not currently exposed to FeatureScript, which is where the extrude 'up_to_next' approach came from.  It's an easy built-in way to find the next intersection from a location to a set of bodies.
    Jake Rosenfeld - Modeling Team
  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    I love it when all the smart people turn up to the same party :)
    Philip Thomas - Onshape
  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    Also looping though every face of the selected bodies and running an evDistance could get very expensive.
    my Fold FS for unfolding arbitary extruded surface evaluates evDistance every 0.5 mm along the bounding edges of the face. that what should be called "very expensive" :)
    the ambiguity of evDistace in the case of several possible intersections seems to be the main problem
  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    @konstantin_shiriazdanov - wait, did you write a flattener????
    I am very excited (i want one :)) - is it public?

    Many thanks - Philip
    Philip Thomas - Onshape
  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    hi @philip_thomas
    they are all in the same document, because they share a little library of functions for curve transforms, the one you want is Fold FS with fold/unfold options. It works with rather common extracted surfaces but makes some geometry distrosions (about 0.5 % of length).
    https://cad.onshape.com/documents/0bb13c1b6ed6d4a6dd75cf99/w/b4493d47a45c27ce485c84b9/e/1299ae499a697e3b28bca1e4

  • maximilian_schommermaximilian_schommer Member Posts: 32 ✭✭
    Ray Tracer is indeed a very useful and efficient custom feature, however I encountered a bug in it. As the attached image shows, RT works well tracing a beam in the FRONT plane (the same plane where I sketched lens' profile to be revolved), but fails for the beam with identical parameters in the TOP plane. The problem seems to  stem from feature script code ( var rayEndPoint = getRayIntersection(context, id, rayToTrace); ) not being able to compute the intersection of the ray with the back surface of the lens for some reason. Looking at the script for this feature I am generally curious why it takes so much code to find an intersection between a line and a surface of a body.
    I understand that Ray Tracer will not be developed further by Onshape, but I wanted to bring the issue to the attention of the users.
    @ivan_skachko

    Thanks for the bug find! I'll take a look at it when I have time! The reason that it takes so much code to find the intersection of a ray and a surface is because there is no function in onshape that lets you calculate that uniquely. I'm using a workaround, where to "project" the ray, I'm extruding a very thin surface, and finding where the surface intersects another body. It's a bit strange, but it's the only way I know of to trace rays reliably. There have been strange bugs I've had to deal with such as rays intersecting surfaces at locations like the center point of a revolve, where a normal isn't properly defined, and I'll take a look at this bug and see what the issue is. Thanks again for pointing it out!

    Max
  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    @konstantin_shiriazdanov - THANK YOU - i am thrilled! :)


    Philip Thomas - Onshape
  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    @philip_thomas
    i should specifically mention - this fs was made to make correct flats from faces of surfaces that might be obtained by extraction operation. and the face should be situated to the one side of the "profile edge". in your case i see that you apply it to the lofted face. if the resulting region mathches original face by area and length of bounding edges I would be surprised a lot  :o
  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    @konstantin_shiriazdanov - hmm :)

    So the difference in surface area is 2.8% in this case.

    My apologies for not understanding what you mean by - "the face should be situated to the one side of the "profile edge"". 
    Would you please explain?
    Is there anything else i can do to improve accuracy?
    I would like 1% or better.

    Many thanks - Philip
    Philip Thomas - Onshape
  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
     "the face should be situated to the one side of the "profile edge"
    I meant that when you select face or edge to transform and then generatrix profile edge the generatrix profile shouldn't cross any edge you need to flatten (but it may be one of the bounding edges) because internally it doesn't differ the direction since evDistance return unsigned value.

    >Is there anything else i can do to improve accuracy?
    sorry, can't think about something suitable right now, it uses numerical algorhytms of evDistance and of finding length parameter and seem like most numeric mistake is in x coordinate which is evaluated by my numeric function. if i find how to improove it i'll tell you.
  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    @konstantin_shiriazdanov - Please accept my apologies - your English is 100 times better than my Russian (Ukrainian?), but i have no idea what you just said! :)

    I think you said that when flattened, the profile edge should not cross the surface being flattened.
    My apologies again if this is way off.

    As it stands right now, this custom feature is a huge help and I am very grateful!

    Thank you for all your support in the forums :)
    Philip Thomas - Onshape
  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    @philip_thomas
    sorry, that i'm training my english on you)
    I think you said that when flattened, the profile edge should not cross the surface being flattened.
    that is it! an example of what happens when selected profile edge cross flattening entities

  • philip_thomasphilip_thomas Member, Moderator, Onshape Employees, Developers Posts: 1,381
    @konstantin_shiriazdanov - got it - thank you! :)
    Philip Thomas - Onshape
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,034 EDU
    Thanks for the bug find! I'll take a look at it when I have time! The reason that it takes so much code to find the intersection of a ray and a surface is because there is no function in onshape that lets you calculate that uniquely. I'm using a workaround, where to "project" the ray, I'm extruding a very thin surface, and finding where the surface intersects another body. It's a bit strange, but it's the only way I know of to trace rays reliably. There have been strange bugs I've had to deal with such as rays intersecting surfaces at locations like the center point of a revolve, where a normal isn't properly defined, and I'll take a look at this bug and see what the issue is. Thanks again for pointing it out!

    Max
    @maximilian_schommer

     Here is an edited version of your FeatureScript that uses evRaycast rather than extrude up-to-next.
    FeatureScript 1174;
    import(path : "onshape/std/geometry.fs", version : "1174.0");
    
    
    annotation { "Feature Type Name" : "Ray Tracer" }
    export const rayTracer = defineFeature(function(context is Context, id is Id, definition is map)
        precondition
        {
            annotation { "Name" : "Origin Point", "Filter" : EntityType.VERTEX, "MaxNumberOfPicks" : 1 }
            definition.origin is Query;
    
            annotation { "Name" : "Direction Point", "Filter" : EntityType.VERTEX }
            definition.directionPoint is Query;
    
            annotation { "Name" : "Maximum Depth", "UIHint" : UIHint.REMEMBER_PREVIOUS_VALUE }
            isInteger(definition.maxDepth, POSITIVE_COUNT_BOUNDS);
    
        }
        {
            const bodies = qAttributeFilter(qBodyType(qEverything(EntityType.BODY), BodyType.SOLID), {});
            const directionPoints = evaluateQuery(context, definition.directionPoint);
            const originQ = definition.origin;
            const patternTransformOrigin = getRemainderPatternTransform(context, {
                    "references" : originQ
            });
            const patternTransformDirections = getRemainderPatternTransform(context, {
                    "references" : qUnion(directionPoints)
            });
            const originPoint = patternTransformOrigin * evVertexPoint(context, {
                    "vertex" : originQ
            });
            for (var i = 0; i < size(directionPoints); i += 1)
            {
                var directionPoint is Vector = patternTransformDirections * evVertexPoint(context, {
                        "vertex" : directionPoints[i]
                    });
                var direction is Vector = normalize(directionPoint - originPoint);
    
                try silent
                {
                    definition.medRef = getVariable(context, "mediumRefractionIndex");
                }
                catch
                {
                    throw regenError("Set Up has not been performed");
                }
    
                var rayToTrace = line(originPoint, direction);
                var traceRayDef = {
                    "rayToTrace" : rayToTrace,
                    "recursionDepth" : 1,
                    "medRef" : definition.medRef,
                    "maxDepth" : definition.maxDepth,
                    "hitsDone" : 0,
                    "bodies" : bodies
                };
                const subId = id + unstableIdComponent(i);
                setExternalDisambiguation(context, subId, directionPoints[i]);
                traceSingleRay(context, subId + "curve", traceRayDef);
                try
                {
                    opExtractWires(context, subId + "extract", {
                                "edges" : qCreatedBy(subId + "curve", EntityType.EDGE)
                            });
                }
                catch
                {
                    opPattern(context, subId + "pattern", {
                                "entities" : qCreatedBy(subId + "curve", EntityType.BODY),
                                "instanceNames" : ["1"],
                                "transforms" : [identityTransform()]
                            });
                }
            }
            opDeleteBodies(context, id + "deleteBodies1", {
                        "entities" : qCreatedBy(id + ANY_ID + "curve", EntityType.BODY)
                    });
        });
    
    
    
    export function traceSingleRay(context is Context, id is Id, definition is map)
    {
    
        var rayToTrace = definition.rayToTrace;
    
        var rayIntersectData = getRayIntersection(context, id, rayToTrace, definition.bodies);
        if (rayIntersectData == false)
        {
            debug(context, rayToTrace);
            return false;
        }
        const rayEndPoint = rayIntersectData.intersection;
        var intersectFace = rayIntersectData.entity;
    
        var n1 = definition.medRef;
        var n2 = getAttributes(context, {
                            "entities" : qOwnerBody(intersectFace)
                        })[0].indexOfRefraction;
        if(!(n2 is number))
            return false;
    
        var isReflective is boolean = getAttributes(context, {
                    "entities" : qOwnerBody(intersectFace)
                })[0].isReflective;
        var isRefractive is boolean = getAttributes(context, {
                    "entities" : qOwnerBody(intersectFace)
                })[0].isRefractive;
    
        var parameterVector = rayIntersectData.parameter;
    
        var surfaceNormal = zeroVector(3);
        try silent
        {
            surfaceNormal = evFaceTangentPlane(context, {
                            "face" : intersectFace,
                            "parameter" : parameterVector
                        }).normal;
        }
        catch
        {
            surfaceNormal = evFaceTangentPlane(context, {
                            "face" : intersectFace,
                            "parameter" : jitter(parameterVector)
                        }).normal;
        }
    
    
        //Makes sure that the normal is facing the right direction for our reflection/refraction calculations
        if (dot(surfaceNormal, rayToTrace.direction) > 0)
        {
            n1 = n2;
            n2 = definition.medRef;
            surfaceNormal *= -1;
        }
    
        definition.hitDone += 
        if (definition.hitsDone < definition.maxDepth && isReflective)
        {
            var reflectedRay is Line = reflectRay(line(rayEndPoint, rayToTrace.direction), surfaceNormal);
            reflectedRay.origin = reflectedRay.origin + reflectedRay.direction * .000001 * inch;
            definition.rayToTrace = reflectedRay;
    
            traceSingleRay(context, id + "recurseReflect", definition);
        }
        if (definition.hitsDone < definition.maxDepth && isRefractive)
        {
            var refractedRay = refractRay(line(rayEndPoint, rayToTrace.direction), surfaceNormal, n1, n2);
            definition.rayToTrace = refractedRay;
            if (refractedRay != false)
            {
                traceSingleRay(context, id + "recurseRefract_R", definition);
            }
            else
            {
                if (!isReflective)
                {
                    var reflectedRay is Line = reflectRay(context, line(rayEndPoint, rayToTrace.direction), surfaceNormal);
                    reflectedRay.origin = reflectedRay.origin + reflectedRay.direction * .000001 * inch;
                    definition.rayToTrace = reflectedRay;
                    traceSingleRay(context, id + "recurseReflect_R", definition);
                }
    
            }
        }
    }
    
    export function jitter(paramVec is Vector)
    {
        var jitterAmount = .0001; // This is so that at the very center of revolved lens, you don't throw an error.
        paramVec[0] = clamp(paramVec[0], jitterAmount, 1 - jitterAmount);
        paramVec[1] = clamp(paramVec[1], jitterAmount, 1 - jitterAmount);
        return paramVec;
    }
    
    export function getRayIntersection(context is Context, id is Id, rayToTrace is Line, bodies is Query)
    {
        var rayResult = evRaycast(context, {
                "entities" : qSubtraction(qOwnedByBody(bodies, EntityType.FACE), qWithinRadius(qEverything(EntityType.FACE), rayToTrace.origin, TOLERANCE.zeroLength * meter)),
                "ray" : rayToTrace
            });
        if (rayResult == [])
        {
            opFitSpline(context, id + "endFitSpline", {
                        "points" : [
                                rayToTrace.origin,
                                rayToTrace.origin + rayToTrace.direction * inch
                            ]
                    });
            return false;
        }
        rayResult = rayResult[0];
    
    
        opFitSpline(context, id + "fitSpline1", {
                    "points" : [
                            rayToTrace.origin,
                            rayResult.intersection
                        ]
                });
        return rayResult;
    }
    
    export function reflectRay(rayToReflect is Line, normal)
    {
        var reflectedRay = -2 * (dot(rayToReflect.direction, normal)) * normal + rayToReflect.direction;
        return line(rayToReflect.origin, reflectedRay);
    }
    
    //n1 is the material that the light is coming from, n2 is the material the light is going into.
    export function refractRay(rayToRefract is Line, normal, n1, n2)
    {
        var thetaI = angleBetween(-1 * rayToRefract.direction, normal);
        var radical = 1 - (n1 / n2) ^ 2 * (1 - cos(thetaI) ^ 2);
        if (radical < 0)
        {
            return false;
        }
    
        var v is Vector = n1 / n2 * normalize(rayToRefract.direction) + (n1 / n2 * cos(thetaI) - sqrt(radical)) * normalize(normal);
    
        return line(rayToRefract.origin, v);
    }
    
    PS:
    It can also be patterned using feature pattern :)


    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • maximilian_schommermaximilian_schommer Member Posts: 32 ✭✭
    Thanks @MBartlett21 , I just incorporated the adaption! The new release works on my test cases, but if anyone could let me know if they have issues with the new code that would be really helpful!


    Max

Sign In or Register to comment.