/// <summary> /// Write out a json script compatible with R.U.B.E. from current LDPhysics model. /// See https://www.iforce2d.net/rube for more details. /// </summary> /// <param name="fileName">The full path to the json file to create.</param> public static void WriteJson(Primitive fileName) { try { JsonPhysics physicsJson = new JsonPhysics(_Engine); physicsJson.Write(fileName); } catch (Exception ex) { Utilities.OnError(Utilities.GetCurrentMethod(), ex); } }
/// <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 ""; } }