Running solve multiple timese - optimization

I need to run a solve three times. Every time solve needs to have different input from different columns of a tuple. That is why I need to access the loop variable with in the OPL as a parameter and need to change that parameter with every loop. Please suggest how to do that in ODM OPL.
(I am able to do it when running a standalone model with a physical .dat file by introducing a int in dat file and changing its values with each loop, but same is not possible when running through an ODM application).

You can do this using a scripting main() function:
.dat file:
param = 0; // This value is actually never used
.mod file:
tuple T {
int round1;
int round2;
}
T t = <1, 2>;
int param = ...;
dvar float x;
minimize x;
subject to { x >= param; }
main {
thisOplModel.generate();
var def = thisOplModel.modelDefinition;
var data = thisOplModel.dataElements;
for (var i = 0; i < 2; ++i) {
if (i == 0)
data.param = thisOplModel.t.round1;
else
data.param = thisOplModel.t.round2;
var opl = new IloOplModel(def, cplex);
opl.addDataSource(data);
opl.generate();
cplex.solve();
writeln("Round " + i + ": " + cplex.getObjValue() + ", " + data.param);
opl.end();
}
}
The scripting code modifies the data before creating a new model in each iteration. You have a more elaborate version of code like this in the cutstock_main.mod example that ships with CPLEX.

What Daniel wrote works fine. If you do not want to have the non necessary .dat file you could write
sub.mod
tuple T {
int round1;
int round2;
}
T t = <1, 2>;
int param = ...;
dvar float x;
minimize x;
subject to { x >= param; }
and then in another model that will be the main one:
tuple T {
int round1;
int round2;
}
T t = <1, 2>;
main {
thisOplModel.generate();
var src = new IloOplModelSource("sub.mod");
var def=new IloOplModelDefinition(src);
var data = new IloOplDataElements();;
for (var i = 0; i < 2; ++i) {
if (i == 0)
data.param = thisOplModel.t.round1;
else
data.param = thisOplModel.t.round2;
var opl = new IloOplModel(def, cplex);
opl.addDataSource(data);
opl.generate();
cplex.solve();
writeln("Round " + i + ": " + cplex.getObjValue() + ", " + data.param);
opl.end();
}
}
which will give
Round 0: 1, 1
Round 1: 2, 2
and
tuple T {
int round1;
int round2;
}
T t = <1, 2>;
int solutions[0..1];
main {
thisOplModel.generate();
var src = new IloOplModelSource("sub.mod");
var def=new IloOplModelDefinition(src);
var data = new IloOplDataElements();;
for (var i = 0; i < 2; ++i) {
if (i == 0)
data.param = thisOplModel.t.round1;
else
data.param = thisOplModel.t.round2;
var opl = new IloOplModel(def, cplex);
opl.addDataSource(data);
opl.generate();
cplex.solve();
writeln("Round " + i + ": " + cplex.getObjValue() + ", " + data.param);
thisOplModel.solutions[i]=opl.x.solutionValue;
opl.end();
}
writeln(thisOplModel.solutions);
}
to address your next question about populating tables
which gives
Round 0: 1, 1
Round 1: 2, 2
[1 2]

Related

How to Convert Geohash to Geometry in BigQuery?

PostGIS has this function ST_GeomFromGeoHash to get the bounding box geometry of the geohash area (https://postgis.net/docs/ST_GeomFromGeoHash.html), but it has not been ported to BigQuery yet. Is there any workaround?
I've implemented the following BigQuery UDF that converts a geohash of arbitrary precision to a bounding box geometry:
CREATE OR REPLACE FUNCTION dataset.geohash_to_bbox(geohash STRING)
RETURNS STRING
LANGUAGE js AS """
var BASE32_CODES = "0123456789bcdefghjkmnpqrstuvwxyz";
var BASE32_CODES_DICT = {};
for (var i = 0; i < BASE32_CODES.length; i++) {
BASE32_CODES_DICT[BASE32_CODES.charAt(i)] = i;
}
var ENCODE_AUTO = 'auto';
var MIN_LAT = -90;
var MAX_LAT = 90;
var MIN_LON = -180;
var MAX_LON = 180;
var decode_bbox = function (hash_string) {
var isLon = true,
maxLat = MAX_LAT,
minLat = MIN_LAT,
maxLon = MAX_LON,
minLon = MIN_LON,
mid;
var hashValue = 0;
for (var i = 0, l = hash_string.length; i < l; i++) {
var code = hash_string[i].toLowerCase();
hashValue = BASE32_CODES_DICT[code];
for (var bits = 4; bits >= 0; bits--) {
var bit = (hashValue >> bits) & 1;
if (isLon) {
mid = (maxLon + minLon) / 2;
if (bit === 1) {
minLon = mid;
} else {
maxLon = mid;
}
} else {
mid = (maxLat + minLat) / 2;
if (bit === 1) {
minLat = mid;
} else {
maxLat = mid;
}
}
isLon = !isLon;
}
}
return "POLYGON (( " + minLon + " " + minLat + ", " + maxLon + " " + minLat + ", " + maxLon + " " + maxLat + ", " + minLon + " " + maxLat + ", " + minLon + " " + minLat + "))";
};
return decode_bbox(geohash);
""";
Example usage:
select <dataset>.geohash_to_geom("ttnfv2u");
>> POLYGON((77.2119140625 28.6083984375, 77.2119140625 28.65234375, 77.255859375 28.65234375, 77.255859375 28.6083984375, 77.2119140625 28.6083984375))
BigQuery has ST_GEOGPOINTFROMGEOHASH which returns the central point. There is currently no function that returns the box though. The UDF in another answer is often a reasonable workaround, but you should be aware of its usage limitation.
GeoHash normally represents a rectangle on a flat 2D map. BigQuery works with Geography, with geodesic edges, so an edge between two points with same latitude does not follow the parallel, but being geodesic line is a shorter route closer to the pole. So the BigQuery polygon is a bit different from 2D box. You can often ignore the differences, but it might give you wrong results depending on how you use this polygon.

Mixed Integer Programming - Problem of Writing Constraints in A Resource Allocation Problem

There are a number of orders, which needs to be shipped. For each order, there may be 1 to 3 route options. The problem here is to find out the best allocation (combination) of orders among these routes.
Assumptions:
Suppose the full capacity and per km transport cost for each type of truck are:
7.6(size) -> 5300(weight), 4.4(cost per km);
9.6(size) -> 7100(weight), 4.81(cost per km);
15(size) -> 12000(weight), 6.34(cost per km);
Suppose all routes has 3 number of transit times to reach the destination, then:
transit cost of each route = transit times*order quantity allocated to this route*4.5
Suppose transport cost of route is based on number of trucks used and the travling distance, then:
transport cost of each route = number of trucks allocated to this route*route distance
// Variables
x[j] -> 0-1 variables, if route x1,x2,x3....xj is used.
a[i][j] -> is the weight proportion of order i allocated to route j
t[j][s] -> truck number of certain size needed for each routes
Objective Function:
Min (total tranpsort cost + total transit cost) -> MinĪ£(x[j]*t[j][s]*Distance[j]*Cost[j] + x[j]*a[i][j]*OrderQuantity[i]*TransitTimes
Subject to:
1. if a[i][j] is the proportion of weight allocation among the feasible routes of an order, then:
a1+a2+a3 = 1
2. orders combined together must choose the same route
3. orders combined together must be the same route type
4. all orders must be allocated to one or more trucks and shipped
5. the chosen of the truck size must be within the truck options for each route
6. total weight loaded on the truck must not exceed the total capacity of the truck
This is how the input date may look like:
This is the CSV formate of the input data:
OrderID,Order_Category,Quantity,Weight,Route1,Route1_Distance,Route1_Type,Route1_TruckOption,Route2,Route2_Distance,Route2_Type,Route2_TruckOption,Route3,Route3_Distance,Route3_Type,Route3_TruckOption
1,B,2000,4000,010W,2000,1,13.5-15,010WE,1750,1,13.5-15,022X,2200,1,13.5-15
2,B,4000,8000,010W,2000,1,13.5-15,010WE,1750,1,13.5-15,022X,2200,1,13.5-15
3,B,1000,2000,010W,2000,1,13.5-15,010WE,1750,1,13.5-15,022X,2200,1,13.5-15
4,B,3500,7000,010WE,1750,1,13.5-15,022X,2200,1,13.5-15,,,,
5,B,2500,5000,020WB,2100,1,13.5-15,022X,2200,1,13.5-15,,,,
6,B,2500,5000,311W,1500,1,13.5-15,022X,2200,1,13.5-15,,,,
7,B,1000,2000,755WF,3500,2,7.6-9.6,755WE,3300,2,7.6-9.6,,,,
8,C,1000,2000,471W,3200,1,13.5-15,010WE,1750,1,13.5-15,022X,2200,1,13.5-15
9,C,1500,3000,471W,3200,1,13.5-15,010WE,1750,1,13.5-15,022X,2200,1,13.5-15
10,C,2000,4000,769WX,2750,2,7.6-9.6,663WA,2600,2,7.6-9.6,,,,
11,C,1000,2000,769WX,2750,2,7.6-9.6,754W,2900,2,7.6-9.6,663WA,2600,2,7.6-9.6
12,C,2000,4000,769WX,2750,2,7.6-9.6,755WE,3100,2,7.6-9.6,,,,
The optimization result might look like this:
My questions:
I am not sure how to write the constraints 2,3,5,6.
In particular, I am not sure how to
1). model the relationship between weight allocated and the associated truck quantity and selection of suitable size;
2) make sure orders combined using the same route and same route type
An MIP code example using Google OR tools or Gurobi python would be helpful.
Since no one offers a suggested solution I have worked out a solution on my own.
public static void TruckAllocation() {
Loader.loadNativeLibraries();
// Data
// Dict<orderID, feasible paths)
Map<Integer, List<String>> myPath = new HashMap<Integer, List<String>>();
List<String> order1 = Arrays.asList("010W", "010WE", "022X");
List<String> order2 = Arrays.asList("010W", "010WE", "022X");
List<String> order3 = Arrays.asList("010W", "010WE", "022X");
List<String> order4 = Arrays.asList("010WE", "022X");
List<String> order5 = Arrays.asList("020WB", "022X");
List<String> order6 = Arrays.asList("331W", "022X");
List<String> order7 = Arrays.asList("755WF", "755WE");
List<String> order8 = Arrays.asList("010WE", "022X");
List<String> order9 = Arrays.asList("010WE", "022X");
List<String> order10 = Arrays.asList("663WA");
List<String> order11 = Arrays.asList("754W", "663WA");
List<String> order12 = Arrays.asList("755WE");
myPath.put(0, order1);
myPath.put(1, order2);
myPath.put(2, order3);
myPath.put(3, order4);
myPath.put(4, order5);
myPath.put(5, order6);
myPath.put(6, order7);
myPath.put(7, order8);
myPath.put(8, order9);
myPath.put(9, order10);
myPath.put(10, order11);
myPath.put(11, order12);
// Dict<pathname, truck options)
Map<String, List<Double>> truckOption = new HashMap<String, List<Double>>();
// List<Double> _010W = Arrays.asList(8500.0, 12000.0);
// List<Double> _010WE = Arrays.asList(8500.0, 12000.0);
// List<Double> _020WB = Arrays.asList(8500.0, 12000.0);
// List<Double> _311W = Arrays.asList(8500.0, 12000.0);
// List<Double> _755WF = Arrays.asList(5300.0, 7100.0);
// List<Double> _471W = Arrays.asList(8500.0, 12000.0);
// List<Double> _769WX = Arrays.asList(5300.0, 7100.0);
// List<Double> _022X = Arrays.asList(8500.0, 12000.0);
// List<Double> _755WE = Arrays.asList(5300.0, 7100.0);
// List<Double> _663WA = Arrays.asList(5300.0, 7100.0);
// List<Double> _754W = Arrays.asList(5300.0, 7100.0);
List<Double> _010W = Arrays.asList(13.5, 15.0);
List<Double> _010WE = Arrays.asList(13.5, 15.);
List<Double> _020WB = Arrays.asList(13.5, 15.);
List<Double> _311W = Arrays.asList(13.5, 15.);
List<Double> _755WF = Arrays.asList(7.6, 9.6);
List<Double> _471W = Arrays.asList(13.5, 15.);
List<Double> _769WX = Arrays.asList(7.6, 9.6);
List<Double> _022X = Arrays.asList(13.5, 15.);
List<Double> _755WE = Arrays.asList(7.6, 9.6);
List<Double> _663WA = Arrays.asList(7.6, 9.6);
List<Double> _754W = Arrays.asList(7.6, 9.6);
truckOption.put("010W", _010W);
truckOption.put("010WE", _010WE);
truckOption.put("020WB", _020WB);
truckOption.put("311W", _311W);
truckOption.put("755WF", _755WF);
truckOption.put("471W", _471W);
truckOption.put("769WX", _769WX);
truckOption.put("022X", _022X);
truckOption.put("755WE", _755WE);
truckOption.put("663WA", _663WA);
truckOption.put("754W", _754W);
// Dict<pathname, distance)
Map<String, Integer> dist_matrix = new HashMap<String, Integer>();
dist_matrix.put("010W", 2177);
dist_matrix.put("010WE", 2177);
dist_matrix.put("020WB", 2177);
dist_matrix.put("311W", 1749);
dist_matrix.put("755WF", 77);
dist_matrix.put("471W", 2450);
dist_matrix.put("769WX", 3);
dist_matrix.put("022X", 2125);
dist_matrix.put("755WE", 77);
dist_matrix.put("663WA", 33);
dist_matrix.put("754W", 372);
List<String> pathName = new ArrayList();
List<Double> truckOpt = new ArrayList();
List<String> routes = Arrays.asList(
"010W",
"010WE",
"020WB",
"311W",
"755WF",
"471W",
"769WX",
"022X",
"755WE",
"663WA",
"754W");
List<Double> wgt = Arrays.asList(
4000.0,
8000.0,
2000.0,
7000.0,
5000.0,
5000.0,
2000.0,
2000.0,
3000.0,
4000.0,
2000.0,
4000.0
);
List<Double> qty = Arrays.asList(
2000.0,
4000.0,
1000.0,
3500.0,
2500.0,
2500.0,
1000.0,
1000.0,
1500.0,
2000.0,
1500.0,
2000.0
);
for (String s: routes) {
List<String> temp = Collections.nCopies(10, s);
pathName.addAll(temp);
for (Double i: truckOption.get(s)) {
List<Double> cap = Collections.nCopies(5, i);
truckOpt.addAll(cap);
}
}
int numOrder = 12;
int numTruck = truckOpt.size();
System.out.println("Route size: " + routes.size());
System.out.println("truckOpt size: " + numTruck);
// Solver
// Create the linear solver with the SCIP backend.
MPSolver solver = MPSolver.createSolver("SCIP");
// Variables
// x[i][j] is an array of 0-1 variables, which will be the proportion
// of order i is assigned to path j's truck n.
MPVariable[][] x = new MPVariable[numOrder][numTruck];
for (int i = 0; i < numOrder; i++) {
String name = "wgt: " + wgt.get(i) + " feasible route: " + String.valueOf(myPath.get(i));
for (int j = 0; j < numTruck; j++) {
x[i][j] = solver.makeNumVar(0, 1, name);
// x[i][j] = solver.makeNumVar(0, 1, "");
System.out.println("x_" + i +"-"+ j);
}
}
// y[j][n] is an array of 0-1 variables, which indicates if truck j is opened.
MPVariable[] y = new MPVariable[numTruck];
for (int j = 0; j < numTruck; j++) {
// System.out.println("uy[j]: " + j);
// String name = String.valueOf(truckOpt.get(j));
String name = pathName.get(j) + "-" + String.valueOf(truckOpt.get(j)) + "-"
+ String.valueOf(GetTruckCost(truckOpt.get(j)));
y[j] = solver.makeIntVar(0, 1, name);
System.out.println("y[j]: " + y[j].name());
// System.out.println("dy[j]: " + j);
}
System.out.println("pathName: " + pathName);
System.out.println("truckOption: " + truckOpt);
// Constraints
// Each order is assigned to at least one or all paths' trucks.
for (int i = 0; i < numOrder; ++i) {
// MPConstraint constraint = solver.makeConstraint(1, numTruck, "");
MPConstraint constraint = solver.makeConstraint(1, 1, "");
for (int j = 0; j < numTruck; ++j) {
List<String> my_path = new ArrayList<String>();
my_path.addAll(myPath.get(i));
// System.out.println("my path: " + my_path);
if (my_path.contains(GetProperty(y[j].name(),0))) {
constraint.setCoefficient(x[i][j], 1);
// System.out.println("true");
}
else {
constraint.setCoefficient(x[i][j], 0);
// System.out.println("false");
}
}
}
// all orders loaded cannot exceed the loaded truck's full capacity.
double infinity = java.lang.Double.POSITIVE_INFINITY;
for (int j = 0; j < numTruck; ++j) {
MPConstraint constraint = solver.makeConstraint(-infinity, 0, "");
for (int i = 0; i < numOrder; ++i) {
constraint.setCoefficient(x[i][j], wgt.get(i));
constraint.setCoefficient(y[j], -GetFullCap(Double.parseDouble(GetProperty(y[j].name(),1))));
// System.out.println("wij_wgiht: "+wgt.get(i)+ " truck capcaity: "
// + GetFullCap(Double.parseDouble(GetProperty(y[j].name(),1))));
}
}
// all trucks can be 0 (not open) or numTruck (all open).
for (int j = 0; j < numTruck; ++j) {
MPConstraint constraint = solver.makeConstraint(0, numTruck, "");
constraint.setCoefficient(y[j], 1);
}
// Objective
MPObjective objective = solver.objective();
for (int j = 0; j < numTruck; ++j) {
double cof = Double.parseDouble(GetProperty(y[j].name(),2))*dist_matrix.get(GetProperty(y[j].name(),0));
objective.setCoefficient(y[j], cof);
}
objective.setMinimization();
// Solve
MPSolver.ResultStatus resultStatus = solver.solve();
// Print solution.
// Check that the problem has a feasible solution.
if (resultStatus == MPSolver.ResultStatus.OPTIMAL
|| resultStatus == MPSolver.ResultStatus.FEASIBLE) {
System.out.println("Total cost: " + objective.value() + "\n");
for (int i = 0; i < numOrder; ++i) {
for (int j = 0; j < numTruck; ++j) {
// Test if x[i][j] is 0 or 1 (with tolerance for floating point
// arithmetic).
if (x[i][j].solutionValue() > 0.0000001) {
System.out.println(
"Order " + i + " -> weight: " + x[i][j].name()
);
System.out.println(
"Order " + i + " assigned to truck " + j + "-> " + y[j].name() + ". weight = " + Double.valueOf(String.format("%.2f", x[i][j].solutionValue()*wgt.get(i))));
System.out.println(
"Order " + i + " assigned to truck " + j + ". % = " + Double.valueOf(String.format("%.2f", x[i][j].solutionValue())));
System.out.println("---------------");
System.out.println("");
}
}
}
// for (int j = 0; j < numTruck; ++j) {
// System.out.println(
// "truck " + j + ". isopen? " + y[j].solutionValue());
// }
}
else {
System.err.println("No solution found.");
}
System.out.println("");
System.out.println("");
System.out.println("");
}

QMSClient.SaveCALConfiguration doesn't seem to be working

Can anyone help me understand why this below would not remove named cals. It seems to work fine until the very last line where it does the save. I don't get any exceptions or error messages.
When i look in QV Management console under System>Licenses i still see the ones that were supposed to be removed (Named user CALs)
Client Build Number: 11.20.13314.0
QMSClient Client;
string QMS = "http://localhost:4799/QMS/Service";
Client = new QMSClient("BasicHttpBinding_IQMS", QMS);
string key = Client.GetTimeLimitedServiceKey();
ServiceKeyClientMessageInspector.ServiceKey = key;
List<ServiceInfo> MyQVS = Client.GetServices(ServiceTypes.QlikViewServer);
Client.ClearQVSCache(QVSCacheObjects.All);
CALConfiguration myCALs = Client.GetCALConfiguration(MyQVS[0].ID, CALConfigurationScope.NamedCALs);
List<AssignedNamedCAL> currentNamedCALs = myCALs.NamedCALs.AssignedCALs.ToList();
List<int> indexToRemove = new List<int>();
int cnt = 1;
for (int i = 0; i < currentNamedCALs.Count; i++)
{
if ((currentNamedCALs[i].QuarantinedUntil < System.DateTime.Now)
&& (currentNamedCALs[i].LastUsed < DateTime.Now.AddDays(daysFromToday)))
{
Console.WriteLine("[" + cnt + "] " + currentNamedCALs[i].UserName +
"; Last used: " + currentNamedCALs[i].LastUsed);
indexToRemove.Add(i);
cnt++;
}
}
Console.WriteLine();
for (int i = indexToRemove.Count; i > 0; i--)
{
if (currentNamedCALs[indexToRemove[i - 1]] != null)
{
currentNamedCALs.RemoveAt(indexToRemove[i - 1]);
}
}
Console.WriteLine("\nDone");
myCALs.NamedCALs.AssignedCALs = currentNamedCALs;
Client.SaveCALConfiguration(myCALs);

Quick help turning sum into mean

I was helped earlier in creating this code that would create a histogram of a randomint. Everything looks good except I accidently had the output as a sum instead of a mean of all the numbers that were randomly chosen.I dont want to mess anything up so I was just going to ask, How can I convert this sum into a mean output instead?
import java.util.Random;
class Assignment4
{
public static void main(String[] args)
{
Random r = new Random();
int sum = 0;
int[] bins = new int[10];
for(int i = 0; i < 100; i++)
{
int randomint = 1 + r.nextInt(10);
sum += randomint;
bins[randomint-1]++;
//System.out.print(randomint + ", ");
}
System.out.println("Sum = " + sum);
System.out.println("Data shown below: ");
for (int i = 0; i < bins.length; i++)
{
int binvalue = bins[i];
System.out.print((i+1) + ": ");
for(int j = 0; j < binvalue; j++)
{
System.out.print('*');
}
System.out.println(" (" + binvalue + ")");
}
}
}
Never mind figured it out.... just turned System.out.println("Sum = " + sum); into System.out.println("Mean = " + sum/100);

apply bind pose to a kinect skeleton

I want to normalize a skeleton in order to make it invariant to the size of the person
in front of the kinect; in the same way as the aveteering example.
But I don't want to animate a 3D model using XNA, the only thing I need is to normalize an
skeleton.
So in order to do this task, I have divided it in two functions:
(a) apply a bind pose to an skeleton in order to see how to work this matrix. Obviously this is not what i want to do, but it is a first step in order to
know how to work whit matrix, and so on.
(b) apply any arbitrary pose to a normalized-size-skeleton
First of all, I want to apply a bind pose to an skeleton (a).
First, I have to load the matrix that describe the bone length/ offset between bones and store it in
List BindPose.
Due to I have no idea how to do it, I modified the Aveteering example and write in a file all the Matrix that define
the BindPose, InverseBindPose and SkeletonHierarchy of the dude. I only need BindPose to this first task, but I have the
code prepared in order to do the second task (b)
The file looks like this:
1,331581E-06;-5,551115E-17;1;0;1;-4,16881E-11;-1,331581E-06;0;4,16881E-11;1;8,153579E-23;0;0,03756338;37,46099;2,230549;1
1,110223E-16;-4,435054E-22;1;0;1;1,426127E-06;-2,220446E-16;0;-1,426127E-06;1;-7,654181E-22;0;-0,9558675;-4,079016E-08;-6,266987E-12;1
0,9954988;-0,09477358;1,501821E-06;0;0,09477358;0,9954988;-4,019565E-06;0;-1,114112E-06;4,143805E-06;1;0;3,786007;-0,003599779;5,107028E-06;1
0,9948416;-0,101441;-3,23556E-07;0;0,101441;0,9948416;-2,266755E-08;0;3,241862E-07;-1,027114E-08;1;0;4,543321;-0,00359975;-1,33061E-07;1
0,9950595;0,09927933;2,388133E-07;0;-0,09927933;0,9950595;-2,333792E-08;0;-2,399506E-07;-4,86646E-10;1;0;4,544049;-0,003599948;6,324596E-08;1
0,9992647;0,02747673;0,02674458;0;-0,02928042;0,9971476;0,06956656;0;-0,02475683;-0,07029849;0,9972187;0;4,543965;-0,004398902;2,258555E-07;1
0,9154034;0,4025377;1,107153E-06;0;-0,4025377;0,9154033;-2,437432E-07;0;-1,109319E-06;-2,115673E-07;1;0;5,536249;-0,00288291;1,332601E-07;1
0,9812952;-0,1925096;-4,732622E-07;0;0,1925095;0,9812951;-3,00921E-08;0;4,697166E-07;-5,889972E-08;1;0;3,953898;1,702301E-07;4,88653E-08;1
.......
So each line is a 4X4 matrix defining the BindPose.
To generate this file, the code is like this:
private void ViewSkinningData(SkinningData data)
{
string nameFile = "bind_pose_transformations";
bool append = false;
// The using statement automatically closes the stream and calls IDisposable.Dispose on the stream object.
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#nameFile, append))
{
for (int i = 0; i < data.BindPose.Count; i++)
{
Matrix m = data.BindPose[i];
string matrixString = MatrixToString(m);
file.WriteLine(matrixString);
}
for (int i = 0; i < data.InverseBindPose.Count; i++)
{
Matrix m = data.InverseBindPose[i];
string matrixString = MatrixToString(m);
file.WriteLine(matrixString);
}
for (int i = 0; i < data.SkeletonHierarchy.Count; i++)
{
file.Write(data.SkeletonHierarchy[i] + ";");
}
}
}
string MatrixToString(Matrix m)
{
string result;
result = m.M11 + ";" + m.M12 + ";" + m.M13 + ";" + m.M14 + ";" + m.M21 + ";" + m.M22 + ";" + m.M23 + ";" + m.M24 + ";" + m.M31 + ";" + m.M32 + ";" + m.M33 + ";" + m.M34 + ";" + m.M41 + ";" + m.M42 + ";" + m.M43 + ";" + m.M44;
return result;
}
Next step is to load all this Skinning data in my program:
private void InitializeSkinningDataFromFile()
{
string filename = "bind_pose_transformations";
int number_avatar_joints = 58;
List<Matrix> binpose = new System.Collections.Generic.List<Matrix>();
List<Matrix> inversebindpose = new System.Collections.Generic.List<Matrix>();
List<int> skeletonhierarchy = new System.Collections.Generic.List<int>();
// The using statement automatically closes the stream and calls IDisposable.Dispose on the stream object.
using (System.IO.StreamReader file = new System.IO.StreamReader(filename))
{
string s;
int count = 0;
while (!String.IsNullOrEmpty(s = file.ReadLine()))
{
string[] values = s.Split(';');
Matrix m = BuildMatrix(values);
binpose.Add(m);
count++;
if (count == number_avatar_joints)
{
break;
}
}
count = 0;
while (!String.IsNullOrEmpty(s = file.ReadLine()))
{
string[] values = s.Split(';');
Matrix m = BuildMatrix(values);
inversebindpose.Add(m);
count++;
if (count == number_avatar_joints)
{
break;
}
}
string[] skeletonHierarchy = file.ReadLine().Split(';'); //lee un caracter de separacion al final...
//for (int i = 0; i < skeletonHierarchy.Count(); i++)
for (int i = 0; i < number_avatar_joints; i++)
{
skeletonhierarchy.Add(int.Parse(skeletonHierarchy[i]));
}
}
skinningDataValue = new SkinningData(binpose, inversebindpose, skeletonhierarchy);
}
After, I have to construct boneTransforms structure:
// Bone matrices for the "dude" model
this.boneTransforms = new Matrix[skinningDataValue.BindPose.Count];
this.skinningDataValue.BindPose.CopyTo(this.boneTransforms, 0);
Now boneTransforms have the transformation for my skeleton. So now, i have to apply these trasnformations to an skeleton
Skeleton skeleton = new Skeleton();
foreach (Joint joint in skeleton.Joints)
{
int indexMatrix = AvatarBoneToNuiJointIndex(joint.JointType);
Matrix transform;
if (indexMatrix >= 0)
{
transform = this.boneTransforms[indexMatrix];
}
else
{
transform = Matrix.Identity;
}
Joint aux = ApplyMatrixTransformationToJoint(joint, transform);
normalizeSkel.Joints[joint.JointType] = aux;
}
This is a helper function AvatarBoneToNuiJointIndex:
public int AvatarBoneToNuiJointIndex(JointType jointType)
{
switch (jointType)
{
case JointType.HipCenter:
return 1;
case JointType.Spine:
return 4;
case JointType.ShoulderCenter:
return 6;
case JointType.Head:
return 7;
case JointType.ShoulderLeft:
return 12;
case JointType.ElbowLeft:
return 13;
case JointType.WristLeft:
return 14;
case JointType.HandLeft:
return 15;
case JointType.ShoulderRight:
return 31;
case JointType.ElbowRight:
return 32;
case JointType.WristRight:
return 33;
case JointType.HandRight:
return 34;
case JointType.KneeLeft:
return 50;
case JointType.AnkleLeft:
return 51;
case JointType.FootLeft:
return 52;
case JointType.KneeRight:
return 54;
case JointType.AnkleRight:
return 55;
case JointType.FootRight:
return 56;
default: return -1;
}
}
This is a helper function ApplyMatrixTransformationToJoint:
public Joint ApplyMatrixTransformationToJoint(Joint skeletonJoint, Matrix tranformations)
{
Vector3 pos = SkeletonPointToVector3(skeletonJoint.Position);
Vector3 result = ApplyMatrixTransformationToVector(pos, tranformations);
SkeletonPoint newPosition = new SkeletonPoint()
{
X = result.X,
Y = result.Y,
Z = result.Z
};
skeletonJoint.Position = newPosition;
return skeletonJoint;
}
This is the code for ApplyMatrixTransformationToVector:
static Vector3 ApplyMatrixTransformationToVector(Vector3 v, Matrix m)
{
return Vector3.Transform(v, m);
}
But the problem is that I can't see anything.
I don't know if this approach is correct.
Any help would be fantastic.
Many thanks!