// this builds a VC problem from a list of targets. For each target, we request size, visibility, vertical
    // and horizontal orientation
    void buildVCProblem(List <GameObject> targetObjects, float desiredSize, bool addChildren)
    {
        // define list of properties
        List <CLVisualProperty> properties    = new List <CLVisualProperty> ();
        List <CLVisualProperty> allProperties = new List <CLVisualProperty> ();
        List <CLTarget>         targets       = new List <CLTarget> ();
        List <float>            weights       = new List <float> ();

        int layerMask = (1 << 2);

        float preferredSize = desiredSize / (targetObjects.Count);

        // for each game object in the list
        foreach (GameObject targetobj in targetObjects)
        {
            List <GameObject> renderables = getChildrenWithRenderers(targetobj);
            List <GameObject> colliders   = getChildrenWithColliders(targetobj);

            if ((renderables.Count > 0) && (colliders.Count > 0))
            {
                CLTarget target = new CLTarget(targetobj, renderables, colliders, layerMask, CLTarget.VisibilityPointGenerationMethod.UNIFORM_IN_BB, 6);
                targets.Add(target);

                List <CLTarget> properties_targets = new List <CLTarget> ();
                properties_targets.Add(target);

                //area property with 2.5 weight
                List <float> sizeSatFuncCtrlX = new List <float> {
                    0.0f, 0.002f, preferredSize, 0.4f, 0.5f, 1.0f
                };
                List <float> sizeSatFuncCtrlY = new List <float> {
                    0.0f, 0.1f, 0.8f, 1.0f, 0.1f, 0.0f
                };
                CLSizeProperty sizeP = new CLSizeProperty(CLSizeProperty.SizeMode.AREA, targetobj.name + " size", properties_targets, sizeSatFuncCtrlX, sizeSatFuncCtrlY);
                weights.Add(2.5f);
                properties.Add(sizeP);

                // orientation property (see from front) with w = 1.0
                List <float> hORFuncCtrlX = new List <float> {
                    -180.0f, -90f, 0.0f, 90f, 180.0f
                };
                List <float> hORFuncCtrlY = new List <float> {
                    0.0f, 0.1f, 1.0f, 0.1f, 0.0f
                };
                CLOrientationProperty orP = new CLOrientationProperty(CLOrientationProperty.OrientationMode.HORIZONTAL, targetobj.name + " orientation",
                                                                      properties_targets, hORFuncCtrlX, hORFuncCtrlY);
                properties.Add(orP);
                weights.Add(1.0f);


                // v-orientation property (see from 90 to top), w=1.5
                List <float> vORFuncCtrlX = new List <float> {
                    0.0f, 90.0f, 95f, 180.0f
                };
                List <float> vORFuncCtrlY = new List <float> {
                    0.0f, 1.0f, 0.1f, 0.0f
                };
                CLOrientationProperty orPV = new CLOrientationProperty(CLOrientationProperty.OrientationMode.VERTICAL_WORLD,
                                                                       targetobj.name + " vorientation", properties_targets, vORFuncCtrlX, vORFuncCtrlY);
                properties.Add(orPV);
                weights.Add(1.5f);

                // occlusion property with 4.0 weight
                List <float> occlFuncCtrlX = new List <float> {
                    0.0f, 0.5f, 0.6f, 1.0f
                };
                List <float> occlFuncCtrlY = new List <float> {
                    1.0f, 0.7f, 0.1f, 0.0f
                };
                CLOcclusionProperty occlP = new CLOcclusionProperty(targetobj.name + " occlusion", properties_targets, occlFuncCtrlX, occlFuncCtrlY, doubleSidedVisibilityChecking, randomRayCasts);
                weights.Add(4.0f);
                properties.Add(occlP);
            }
        }

        CLTradeOffSatisfaction satFunction = new CLTradeOffSatisfaction("all", targets, properties, weights);

        allProperties.AddRange(properties);
        allProperties.Insert(0, satFunction);

        camLibCam.SetSpecification(allProperties);
    }
    // this method generates a random viewpoint with more probability where
    // target properties will be satisfied. It assumes we have at least a size
    // property for the target, plus optional angle and occlusion properties

    public Vector3 GenerateRandomSatisfyingPosition(CLCameraMan camera, bool considerVisibility = false)
    {
        Vector3 result = new Vector3();

        bool found = false;

        int ntries = 0;

        float yFOV = (camera.cameraDomain.yFOVBounds [0] + camera.cameraDomain.yFOVBounds [1]) / 2;

        // we allow for 30 tries before giving up
        while (ntries < 30 && !found)
        {
            float distance = 0.0f;
            float phi      = 0.0f;
            float theta    = 0.0f;

            foreach (CLGroundProperty p in groundProperties)
            {
                if (p is CLSizeProperty)
                {
                    CLSizeProperty sp = (CLSizeProperty)p;

                    // this returns a random area, or width, or height, depending on the type of size
                    // property, with more probability where the satisfaction is higher
                    float randomSize = p.satFunction.GenerateRandomXPoint();
                    if (randomSize < 0.0001f)
                    {
                        randomSize = 0.0001f;                         // to avoid computing an infinite distance.
                    }
                    // compute distance from target size
                    distance = ComputeDistanceFromSize(randomSize, sp.sizeType, camera, yFOV);
                }

                if (p is CLOrientationProperty)
                {
                    CLOrientationProperty op = (CLOrientationProperty)p;

                    if (op.orientation == CLOrientationProperty.OrientationMode.HORIZONTAL)
                    {
                        phi = Mathf.Deg2Rad * op.satFunction.GenerateRandomXPoint();                          // horizontal random angle
                    }
                    else
                    {
                        theta = Mathf.Deg2Rad * op.satFunction.GenerateRandomXPoint();                          // vertical random angle
                    }
                }
            }

            // if we are not inside bs sphere
            if (distance > radius)
            {
                result = ComputeWorldPosFromSphericalCoordinates(distance, phi, theta);


                if (camera.InSearchSpace(new float[] { result.x, result.y, result.z }))
                {
                    found = true;
                }
            }

            ntries++;
        }


        if (!found)
        {
            float[] randomCandidate = camera.cameraDomain.ComputeRandomViewpoint(3);
            result = new Vector3(randomCandidate [0], randomCandidate [1], randomCandidate [2]);
            //Debug.Log ("random candidate");
        }
        else
        {
            //Debug.Log ("smart candidate in " + ntries + " tries");
        }

        return(result);
    }