//-----------------------------------------------------------------------------
// Some sample code for slvs.lib. We draw some geometric entities, provide
// initial guesses for their positions, and then constrain them. The solver
// calculates their new positions, in order to satisfy the constraints.
//
// This code is provide for evaluation purposes only. To purchase a license,
// please visit:
//
//     http://solvespace.com/
//
// Copyright 2009, Jonathan Westhues
//-----------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>

#include "slvs.h"

Slvs_System sys;

void *CheckMalloc(size_t n)
{
    void *r = malloc(n);
    if(!r) {
        printf("out of memory!\n");
        exit(-1);
    }
    return r;
}

//-----------------------------------------------------------------------------
// An example of a constraint in 3d. We create 
//-----------------------------------------------------------------------------
void Example3d(void)
{
    // This will contain a single group, which will arbitrarily number 1.
    int g = 1;

    // A point, initially at (x y z) = (10 10 10)
    sys.param[sys.params++] = Slvs_MakeParam(1, g, 10.0);
    sys.param[sys.params++] = Slvs_MakeParam(2, g, 10.0);
    sys.param[sys.params++] = Slvs_MakeParam(3, g, 10.0);
    sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
    // and a second point at (20 20 20)
    sys.param[sys.params++] = Slvs_MakeParam(4, g, 20.0);
    sys.param[sys.params++] = Slvs_MakeParam(5, g, 20.0);
    sys.param[sys.params++] = Slvs_MakeParam(6, g, 20.0);
    sys.entity[sys.entities++] = Slvs_MakePoint3d(102, g, 4, 5, 6);
    // and a line segment connecting them.
    sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g, 
                                        SLVS_FREE_IN_3D, 101, 102);

    // The distance between the points should be 30.0 units.
    sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
                                            1, g,
                                            SLVS_C_PT_PT_DISTANCE,
                                            SLVS_FREE_IN_3D,
                                            30.0,
                                            101, 102, 0, 0);

    // Let's tell the solver to keep the second point as close to constant
    // as possible, instead moving the first point.
    sys.dragged[0] = 4;
    sys.dragged[1] = 5;
    sys.dragged[2] = 6;

    // Now that we have written our system, we solve.
    Slvs_Solve(&sys, g);

    if(sys.result == SLVS_RESULT_OKAY) {
        printf("okay; now at (%.3f %.3f %.3f)\n"
               "             (%.3f %.3f %.3f)\n",
                sys.param[0].val, sys.param[1].val, sys.param[2].val,
                sys.param[3].val, sys.param[4].val, sys.param[5].val);
        printf("%d DOF\n", sys.dof);
    } else {
        printf("solve failed");
    }
}

//-----------------------------------------------------------------------------
// An example of a constraint in 2d. In our first group, we create a workplane
// along the reference frame's xy plane. In a second group, we create some
// entities in that group and dimension them.
//-----------------------------------------------------------------------------
void Example2d(void)
{
    int g;
    double qw, qx, qy, qz;

    g = 1;
    // First, we create our workplane. Its origin corresponds to the origin
    // of our base frame (x y z) = (0 0 0)
    sys.param[sys.params++] = Slvs_MakeParam(1, g, 0.0);
    sys.param[sys.params++] = Slvs_MakeParam(2, g, 0.0);
    sys.param[sys.params++] = Slvs_MakeParam(3, g, 0.0);
    sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
    // and it is parallel to the xy plane, so it has basis vectors (1 0 0)
    // and (0 1 0).
    Slvs_MakeQuaternion(1, 0, 0,
                        0, 1, 0, &qw, &qx, &qy, &qz);
    sys.param[sys.params++] = Slvs_MakeParam(4, g, qw);
    sys.param[sys.params++] = Slvs_MakeParam(5, g, qx);
    sys.param[sys.params++] = Slvs_MakeParam(6, g, qy);
    sys.param[sys.params++] = Slvs_MakeParam(7, g, qz);
    sys.entity[sys.entities++] = Slvs_MakeNormal3d(102, g, 4, 5, 6, 7);

    sys.entity[sys.entities++] = Slvs_MakeWorkplane(200, g, 101, 102);

    // Now create a second group. We'll solve group 2, while leaving group 1
    // constant; so the workplane that we've created will be locked down,
    // and the solver can't move it.
    g = 2;
    // These points are represented by their coordinates (u v) within the
    // workplane, so they need only two parameters each.
    sys.param[sys.params++] = Slvs_MakeParam(11, g, 10.0);
    sys.param[sys.params++] = Slvs_MakeParam(12, g, 20.0);
    sys.entity[sys.entities++] = Slvs_MakePoint2d(301, g, 200, 11, 12);

    sys.param[sys.params++] = Slvs_MakeParam(13, g, 20.0);
    sys.param[sys.params++] = Slvs_MakeParam(14, g, 10.0);
    sys.entity[sys.entities++] = Slvs_MakePoint2d(302, g, 200, 13, 14);

    // And we create a line segment with those endpoints.
    sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g, 
                                        200, 301, 302);

    // Now three more points.
    sys.param[sys.params++] = Slvs_MakeParam(15, g, 100.0);
    sys.param[sys.params++] = Slvs_MakeParam(16, g, 120.0);
    sys.entity[sys.entities++] = Slvs_MakePoint2d(303, g, 200, 15, 16);

    sys.param[sys.params++] = Slvs_MakeParam(17, g, 120.0);
    sys.param[sys.params++] = Slvs_MakeParam(18, g, 110.0);
    sys.entity[sys.entities++] = Slvs_MakePoint2d(304, g, 200, 17, 18);

    sys.param[sys.params++] = Slvs_MakeParam(19, g, 115.0);
    sys.param[sys.params++] = Slvs_MakeParam(20, g, 115.0);
    sys.entity[sys.entities++] = Slvs_MakePoint2d(305, g, 200, 19, 20);

    // And arc, centered at point 303, starting at point 304, ending at
    // point 305.
    sys.entity[sys.entities++] = Slvs_MakeArcOfCircle(401, g, 200,
                                    303, 304, 305);

    // Now one more point, and a distance
    sys.param[sys.params++] = Slvs_MakeParam(21, g, 200.0);
    sys.param[sys.params++] = Slvs_MakeParam(22, g, 200.0);
    sys.entity[sys.entities++] = Slvs_MakePoint2d(306, g, 200, 21, 22);

    sys.param[sys.params++] = Slvs_MakeParam(23, g, 30.0);
    sys.entity[sys.entities++] = Slvs_MakeDistance(307, g, 200, 23);

    // And a complete circle, centered at point 306 with radius equal to
    // distance 307. The normal is 102, the same as our workplane.
    sys.entity[sys.entities++] = Slvs_MakeCircle(402, g, 200,
                                    306, 102, 307);


    // The length of our line segment is 30.0 units.
    sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
                                            1, g,
                                            SLVS_C_PT_PT_DISTANCE,
                                            200,
                                            30.0,
                                            301, 302, 0, 0);

    // And the distance from our line segment to the origin is 10.0 units.
    sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
                                            2, g,
                                            SLVS_C_PT_LINE_DISTANCE,
                                            200,
                                            10.0,
                                            101, 0, 400, 0);
    // And the line segment is vertical.
    sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
                                            3, g,
                                            SLVS_C_VERTICAL,
                                            200,
                                            0.0,
                                            0, 0, 400, 0);
    // And the distance from one endpoint to the origin is 15.0 units.
    sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
                                            4, g,
                                            SLVS_C_PT_PT_DISTANCE,
                                            200,
                                            15.0,
                                            301, 101, 0, 0);
/*
    // And same for the other endpoint; so if you add this constraint then
    // the sketch is overconstrained and will signal an error.
    sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
                                            5, g,
                                            SLVS_C_PT_PT_DISTANCE,
                                            200,
                                            18.0,
                                            302, 101, 0, 0); */

    // The arc and the circle have equal radius.
    sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
                                            6, g,
                                            SLVS_C_EQUAL_RADIUS,
                                            200,
                                            0.0,
                                            0, 0, 401, 402);
    // The arc has radius 17.0 units.
    sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
                                            7, g,
                                            SLVS_C_DIAMETER,
                                            200,
                                            17.0*2,
                                            0, 0, 401, 0);

    // If the solver fails, then ask it to report which constraints caused
    // the problem.
    sys.calculateFaileds = 1;

    // And solve.
    Slvs_Solve(&sys, g);

    if(sys.result == SLVS_RESULT_OKAY) {
        printf("solved okay\n");
        printf("line from (%.3f %.3f) to (%.3f %.3f)\n",
                sys.param[7].val, sys.param[8].val,
                sys.param[9].val, sys.param[10].val);

        printf("arc center (%.3f %.3f) start (%.3f %.3f) finish (%.3f %.3f)\n",
                sys.param[11].val, sys.param[12].val,
                sys.param[13].val, sys.param[14].val,
                sys.param[15].val, sys.param[16].val);

        printf("circle center (%.3f %.3f) radius %.3f\n",
                sys.param[17].val, sys.param[18].val,
                sys.param[19].val);
        printf("%d DOF\n", sys.dof);
    } else {
        int i;
        printf("solve failed: problematic constraints are:");
        for(i = 0; i < sys.faileds; i++) {
            printf(" %d", sys.failed[i]);
        }
        printf("\n");
        if(sys.result == SLVS_RESULT_INCONSISTENT) {
            printf("system inconsistent\n");
        } else {
            printf("system nonconvergent\n");
        }
    }
}

int main(void)
{
    memset(&sys, 0, sizeof(sys));
    sys.param      = CheckMalloc(50*sizeof(sys.param[0]));
    sys.entity     = CheckMalloc(50*sizeof(sys.entity[0]));
    sys.constraint = CheckMalloc(50*sizeof(sys.constraint[0]));

    sys.failed  = CheckMalloc(50*sizeof(sys.failed[0]));
    sys.faileds = 50;

//    Example3d();
    Example2d();
}

