/// <summary>
/// Read in a json script compatible with R.U.B.E. and create a LDPhysics model.
/// See https://www.iforce2d.net/rube for more details.
/// </summary>
/// <param name="fileName">The full path to the json file to read.</param>
/// <param name="scale">Scale all shapes, default 1 (no scaling).</param>
/// <param name="reverseY">Reverse the Y direction up to down ("True" or "False").</param>
/// <param name="stationary">Set all shapes to be initially at rest, joint motors are still enabled ("True" or "False").</param>
/// <param name="offsetX">Add an x coordinate offset to all shapes.</param>
/// <param name="offsetY">Add a y coordinate offset to all shapes, especially useful when reverseY is set.</param>
/// <returns>A text array containing the LDPhysics commands used to create the model.</returns>
public static Primitive ReadJson(Primitive fileName, Primitive scale, Primitive reverseY, Primitive stationary, Primitive offsetX, Primitive offsetY)
{
try
{
List<string> result = new List<string>();
//GraphicsWindow.Clear();
_Engine = new PhysicsEngine();
scale *= _Engine.scale;
JsonPhysics physicsJson = new JsonPhysics(_Engine);
JsonWorld world = physicsJson.Read(fileName);
result.Add("LDGraphicsWindow.State = 2");
result.Add("GraphicsWindow.PenWidth = 1");
result.Add("LDPhysics.Reset()");
Primitive x, y;
if (world.positionIterations > 0)
{
PositionIterations = world.positionIterations;
result.Add("LDPhysics.PositionIterations = " + world.positionIterations);
}
if (world.velocityIterations > 0)
{
VelocityIterations = world.velocityIterations;
result.Add("LDPhysics.VelocityIterations = " + world.velocityIterations);
}
if (world.stepsPerSecond > 0)
{
TimeStep = 1.0f / (float)world.stepsPerSecond;
result.Add("LDPhysics.TimeStep = " + Cast(1.0f / (float)world.stepsPerSecond, false));
}
if (null != world.gravity)
{
x = Cast(_Engine.scale * world.gravity.x, false);
y = Cast((reverseY ? -_Engine.scale : _Engine.scale) * world.gravity.y, false);
SetGravity(x, y);
result.Add("LDPhysics.SetGravity(" + x + "," + y + ")");
}
Dictionary<int, string> imageFiles = new Dictionary<int, string>();
Dictionary<int, string> bodyNames = new Dictionary<int, string>();
Dictionary<int, string> firstFixtureNames = new Dictionary<int, string>();
if (null != world.image)
{
foreach (JsonImage image in world.image)
{
imageFiles[image.body] = image.file;
}
}
if (null != world.body)
{
int iBody = 0;
int iImage = 0;
int iEllipse = 0;
int iTriangle = 0;
int iRectangle = 0;
int iPolygon = 0;
foreach (JsonBody body in world.body)
{
result.Add("");
result.Add("'Body " + iBody);
if (null == body.fixture || body.fixture.Count == 0)
{
result.Add("'WARNING: Body has no fixures");
iBody++;
continue;
}
//TextWindow.WriteLine("Body " + body.name);
string firstShapeName = "";
string firstFixtureName = "";
bool firstFixture = true;
body.fixture.Reverse(); //Last fixture was first added
foreach (JsonFixture fixture in body.fixture)
{
string imageFile;
string shapeName = "";
if (imageFiles.TryGetValue(iBody, out imageFile))
{
if (LoadImagesAsCircles == (null == fixture.circle))
{
LoadImagesAsCircles = null != fixture.circle;
result.Add("LDPhysics.LoadImagesAsCircles = " + (null != fixture.circle ? "\"True\"" : "\"False\""));
}
shapeName = Shapes.AddImage(ImageList.LoadImage(imageFile));
fixture.name = "Image" + ++iImage;
result.Add(fixture.name + " = Shapes.AddImage(ImageList.LoadImage(\"" + imageFile + "\"))");
}
else if (null != fixture.circle)
{
x = Cast(2 * scale * fixture.circle.radius, false);
y = Cast(2 * scale * fixture.circle.radius, false);
shapeName = Shapes.AddEllipse(x, y);
fixture.name = "Ellipse" + ++iEllipse;
result.Add(fixture.name + " = Shapes.AddEllipse(" + x + "," + y + ")");
}
else if (null != fixture.polygon)
{
Primitive points = "";
for (int i = 0; i < fixture.polygon.vertices.x.Count; i++)
{
Primitive point = "";
point[1] = offsetX + Cast(scale * (body.position.x + fixture.polygon.vertices.x[i]), false);
point[2] = offsetY + Cast(scale * (body.position.y + fixture.polygon.vertices.y[i]), reverseY);
points[i + 1] = point;
//TextWindow.WriteLine("Corner " + (i + 1) + " " + point[1] + ", " + point[2]);
}
if (fixture.polygon.vertices.x.Count == 3)//Triangle
{
shapeName = Shapes.AddTriangle(points[1][1], points[1][2], points[2][1], points[2][2], points[3][1], points[3][2]);
fixture.name = "Triangle" + ++iTriangle;
result.Add(fixture.name + " = Shapes.AddTriangle(" + points[1][1] + "," + points[1][2] + "," + points[2][2] + "," + points[2][2] + "," + points[3][2] + "," + points[3][2] + ")");
}
if (fixture.polygon.vertices.x.Count == 4 && points[1][1] == points[4][1] && points[2][1] == points[3][1] && points[1][2] == points[2][2] && points[4][2] == points[3][2])//Rectangle
{
Primitive dx = System.Math.Abs(points[2][1] - points[1][1]);
Primitive dy = System.Math.Abs(points[4][2] - points[1][2]);
shapeName = Shapes.AddRectangle(dx, dy);
fixture.name = "Rectangle" + ++iRectangle;
result.Add(fixture.name + " = Shapes.AddRectangle(" + dx + "," + dy + ")");
}
else
{
shapeName = LDShapes.AddPolygon(points);
fixture.name = "Polygon" + ++iPolygon;
result.Add(fixture.name + " = LDShapes.AddPolygon(\"" + points + "\")");
}
}
else
{
result.Add("'WARNING: Fixture is not an image, triangle, rectangle, circle or polygon");
}
if (shapeName != "")
{
x = body.position.x;
y = body.position.y;
if (null != fixture.circle)
{
x += fixture.circle.center.x;
y += fixture.circle.center.y;
}
else if (null != fixture.polygon)
{
for (int i = 0; i < fixture.polygon.vertices.x.Count; i++)
{
x += fixture.polygon.vertices.x[i] / (float)fixture.polygon.vertices.x.Count;
y += fixture.polygon.vertices.y[i] / (float)fixture.polygon.vertices.x.Count;
}
}
x = offsetX + Cast(scale * x, false);
y = offsetY + Cast(scale * y, reverseY);
bool bSetPosition = false;
if (null != fixture.circle || shapeName.StartsWith("Rectangle"))
{
LDShapes.Centre(shapeName, x, y);
result.Add("LDShapes.Centre(" + fixture.name + "," + x + "," + y + ")");
bSetPosition = true;
}
if (body.type == 0 || body.type == 1) //treat kinematic as static
{
//TextWindow.WriteLine("Fixed Shape");
AddFixedShape(shapeName, fixture.friction, fixture.restitution);
result.Add("LDPhysics.AddFixedShape(" + fixture.name + "," + Cast(fixture.friction, false) + "," + Cast(fixture.restitution, false) + ")");
}
else
{
//TextWindow.WriteLine("Moving Shape");
AddMovingShape(shapeName, fixture.friction, fixture.restitution, fixture.density);
result.Add("LDPhysics.AddMovingShape(" + fixture.name + "," + Cast(fixture.friction, false) + "," + Cast(fixture.restitution, false) + "," + Cast(fixture.density, false) + ")");
}
//TextWindow.WriteLine("Filter " + fixture.filter_categoryBits + " , " + fixture.filter_maskBits);
if (fixture.filter_categoryBits > 0 && fixture.filter_maskBits > 0 && (fixture.filter_categoryBits != 1 || fixture.filter_maskBits != 65535))
{
int _categoryBits = fixture.filter_categoryBits - 1;
Primitive _maskBits = "";
int index = 1;
for (int i = 1; i <= 16; i++)
{
if ((fixture.filter_maskBits & i) != 0) _maskBits[index++] = i - 1;
}
//TextWindow.WriteLine("Filter "+ _categoryBits + " , " + _maskBits);
SetGroup(shapeName, _categoryBits, _maskBits);
result.Add("LDPhysics.SetGroup(" + fixture.name + "," + _categoryBits + ",\"" + _maskBits + "\")");
}
if (fixture.sensor)
{
ToggleSensor(shapeName);
result.Add("LDPhysics.ToggleSensor(" + fixture.name + ")");
}
if (!bSetPosition)
{
body.angle = 0; //Position already includes any rotation
SetPosition(shapeName, x, y, 180.0f / System.Math.PI * body.angle);
result.Add("LDPhysics.SetPosition(" + fixture.name + "," + x + "," + y + "," + Cast(180.0f / System.Math.PI * body.angle, false) + ")");
}
if (firstFixture)
{
x = Cast(scale * body.linearVelocity.x, false);
y = Cast(scale * body.linearVelocity.y, reverseY);
if (!stationary)
{
SetVelocity(shapeName, x, y);
SetRotation(shapeName, 180.0f / System.Math.PI * body.angularVelocity);
if (body.linearVelocity.x != 0 || body.linearVelocity.y != 0) result.Add("LDPhysics.SetVelocity(" + fixture.name + "," + x + "," + y + ")");
if (body.angularVelocity != 0) result.Add("LDPhysics.SetRotation(" + fixture.name + "," + Cast(180.0f / System.Math.PI * body.angularVelocity, false) + ")");
}
if (body.bullet)
{
SetBullet(shapeName);
result.Add("LDPhysics.SetBullet(" + fixture.name + ")");
}
if (body.fixedRotation)
{
ToggleRotation(shapeName);
result.Add("LDPhysics.ToggleRotation(" + fixture.name + ")");
}
if (body.linearDamping > 0 || body.angularDamping > 0)
{
SetDamping(shapeName, body.linearDamping, body.angularDamping);
result.Add("LDPhysics.SetDamping(" + fixture.name + "," + Cast(body.linearDamping, false) + "," + Cast(body.angularDamping, false) + ")");
}
firstShapeName = shapeName;
bodyNames[iBody] = firstShapeName;
firstFixtureName = fixture.name;
firstFixtureNames[iBody] = firstFixtureName;
}
else
{
if (firstShapeName != "")
{
GroupShapes(shapeName, firstShapeName);
result.Add("LDPhysics.GroupShapes(" + fixture.name + "," + firstFixtureName + ")");
}
}
}
firstFixture = false;
}
iBody++;
}
}
if (null != world.joint)
{
jointNames.Clear();
foreach (JsonJoint joint in world.joint)
{
result.Add("");
string jointName = GetJointName(joint);
string jointVar = "";
Primitive parameters = "";
result.Add("'Joint (Body " + joint.bodyB + ",Body " + joint.bodyA + ")");
// "Distance" - damping ratio (default 0)
// "Gear" - gear ratio, first joint, second joint (default 1, auto detect joints)
// "Line" - X direction, Y direction, lower translation, upper translation (default line connecting shapes, no limits)
// "Mouse" - max acceleration, damping ratio (default 10000, 0.7)
// "Prismatic_H" - X direction, Y direction, lower translation, upper translation (default 1,0, no limits)
// "Prismatic_V" - X direction, Y direction, lower translation, upper translation (default 0,1, no limits)
// "Pulley" - pulley ratio (block and tackle) (default 1)
// "Revolute" - lower angle, upper angle (default no limits)
string name;
if (!firstFixtureNames.TryGetValue(joint.bodyA, out name) || !firstFixtureNames.TryGetValue(joint.bodyB, out name))
{
result.Add("'WARNING: Bodies not found");
continue;
}
switch (joint.type)
{
case "revolute":
{
if (joint.enableLimit)
{
parameters[1] = Cast(180.0f / System.Math.PI * joint.lowerLimit, false);
parameters[2] = Cast(180.0f / System.Math.PI * joint.upperLimit, false);
}
jointVar = AttachShapesWithJoint(bodyNames[joint.bodyB], bodyNames[joint.bodyA], "Revolute", joint.collideConnected, parameters);
result.Add(jointName + " = LDPhysics.AttachShapesWithJoint(" + firstFixtureNames[joint.bodyB] + "," + firstFixtureNames[joint.bodyA] + ",\"Revolute\"," + (joint.collideConnected ? "\"True\"" : "\"False\"") + ",\"" + parameters + "\")");
if (joint.enableMotor)
{
SetJointMotor(jointVar, 180.0f / System.Math.PI * joint.motorSpeed, scale * scale * joint.maxMotorTorque);
result.Add("LDPhysics.SetJointMotor(" + jointName + "," + Cast(180.0f / System.Math.PI * joint.motorSpeed, false) + "," + Cast(scale * scale * joint.maxMotorTorque, false) + ")");
}
}
break;
case "distance":
{
parameters[1] = Cast(joint.dampingRatio, false);
jointVar = AttachShapesWithJoint(bodyNames[joint.bodyB], bodyNames[joint.bodyA], "Distance", joint.collideConnected, parameters);
result.Add(jointName + " = LDPhysics.AttachShapesWithJoint(" + firstFixtureNames[joint.bodyB] + "," + firstFixtureNames[joint.bodyA] + ",\"Distance\"," + (joint.collideConnected ? "\"True\"" : "\"False\"") + ",\"" + parameters + "\")");
}
break;
case "prismatic":
{
parameters[1] = Cast(joint.localAxisA.x, false);
parameters[2] = Cast(joint.localAxisA.y, reverseY);
if (joint.enableLimit)
{
parameters[3] = Cast(scale * joint.lowerLimit, false);
parameters[4] = Cast(scale * joint.upperLimit, false);
}
jointVar = AttachShapesWithJoint(bodyNames[joint.bodyB], bodyNames[joint.bodyA], "Prismatic_H", joint.collideConnected, parameters);
result.Add(jointName + " = LDPhysics.AttachShapesWithJoint(" + firstFixtureNames[joint.bodyB] + "," + firstFixtureNames[joint.bodyA] + ",\"Prismatic_H\"," + (joint.collideConnected ? "\"True\"" : "\"False\"") + ",\"" + parameters + "\")");
if (joint.enableMotor)
{
SetJointMotor(jointVar, scale * joint.motorSpeed, scale * joint.maxMotorForce);
result.Add("LDPhysics.SetJointMotor(" + jointName + "," + Cast(scale * joint.motorSpeed, false) + "," + Cast(scale * joint.maxMotorForce, false) + ")");
}
}
break;
case "wheel":
{
//This is a non standard compound joint I think
//AttachShapesWithJoint(bodyNames[joint.bodyB], bodyNames[joint.bodyA], "Line", joint.collideConnected, "\"\"");
//result.Add("LDPhysics.AttachShapesWithJoint(" + firstFixtureNames[joint.bodyB] + "," + firstFixtureNames[joint.bodyA] + ",\"Line\"," + (joint.collideConnected ? "\"True\"" : "\"False\"") + ",\"\")");
jointVar = AttachShapesWithJoint(bodyNames[joint.bodyB], bodyNames[joint.bodyA], "Distance", joint.collideConnected, "\"\"");
result.Add(jointName + " = LDPhysics.AttachShapesWithJoint(" + firstFixtureNames[joint.bodyB] + "," + firstFixtureNames[joint.bodyA] + ",\"Distance\"," + (joint.collideConnected ? "\"True\"" : "\"False\"") + ",\"\")");
//AttachShapesWithRotation(bodyNames[joint.bodyB], bodyNames[joint.bodyA]);
//result.Add("LDPhysics.AttachShapesWithRotation(" + firstFixtureNames[joint.bodyB] + "," + firstFixtureNames[joint.bodyA] + ")");
jointName = GetJointName(joint);
if (joint.enableLimit)
{
parameters[1] = Cast(180.0f / System.Math.PI * joint.lowerLimit, false);
parameters[2] = Cast(180.0f / System.Math.PI * joint.upperLimit, false);
}
jointVar = AttachShapesWithJoint(bodyNames[joint.bodyB], bodyNames[joint.bodyA], "Revolute", joint.collideConnected, parameters);
result.Add(jointName + " = LDPhysics.AttachShapesWithJoint(" + firstFixtureNames[joint.bodyB] + "," + firstFixtureNames[joint.bodyA] + ",\"Revolute\"," + (joint.collideConnected ? "\"True\"" : "\"False\"") + ",\"" + parameters + "\")");
if (joint.enableMotor)
{
SetJointMotor(jointVar, 180.0f / System.Math.PI * joint.motorSpeed, scale * scale * joint.maxMotorTorque);
result.Add("LDPhysics.SetJointMotor(" + jointName + "," + Cast(180.0f / System.Math.PI * joint.motorSpeed, false) + "," + Cast(scale * scale * joint.maxMotorTorque, false) + ")");
}
}
break;
case "rope":
case "motor":
case "weld":
case "friction":
default:
{
result.Add("'WARNING: Type " + joint.type + " not found or inconsistent conversion");
}
break;
}
}
}
result.Add("");
result.Add("While(\"True\")");
result.Add(" LDPhysics.DoTimestep()");
result.Add(" Program.Delay(" + Cast(1000 * TimeStep, false) + ")");
result.Add(" GraphicsWindow.Title=LDPhysics.GetAllShapesAt(GraphicsWindow.MouseX,GraphicsWindow.MouseY)");
result.Add("EndWhile");
string code = "";
foreach (string line in result)
{
code += line + "\n";
}
return code;
}
catch (Exception ex)
{
Utilities.OnError(Utilities.GetCurrentMethod(), ex);
return "";
}
}