diff --git a/examples/regression_jbeam/body-columns-repro.jbeam b/examples/regression_jbeam/body-columns-repro.jbeam new file mode 100644 index 00000000..fe3d3486 --- /dev/null +++ b/examples/regression_jbeam/body-columns-repro.jbeam @@ -0,0 +1,298 @@ +{ + "body_nodes" : { + "nodes" : [ + ["id", "posX", "posY", "posZ"], + {"collision" : true}, + {"frictionCoef" : 0.5}, + {"nodeMaterial" : "|NM_METAL"}, + {"nodeWeight" : 0.78}, + {"selfCollision" : true}, + {"group":"gen4_body"}, + + // Left side + ["bl0", 0.953, -1.967, 0.122], + ["bl1", 1.036, -1.807, 0.125], + ["bl2", 0.920, -1.953, 0.439], + ["bl3", 0.998, -1.791, 0.473], + ["bl4", 0.780, -1.815, 0.719], + ["bl44", 0.821, 2.738, 0.594], + ["bl45", 0.825, 2.536, 0.745], + ["bl46", 0.713, 2.464, 0.914], + ["bl47", 0.813, 2.763, 0.453], + + // Middle side + ["bmm0", 0.038, -0.997, 0.943], + ["bmm1", 0.038, -0.838, 0.949], + ["bmm2", 0.038, -0.087, 1.293], + ["bmm3", 0.038, 0.587, 1.350], + ["bmm4", 0.038, 1.055, 1.324], + + // Right side + ["br0", -0.877, -1.967, 0.122], + ["br1", -0.961, -1.807, 0.125], + ["br2", -0.845, -1.953, 0.439], + ["br3", -0.922, -1.791, 0.473], + ["br4", -0.704, -1.815, 0.719], + ["br44", -0.746, 2.738, 0.594], + ["br45", -0.749, 2.536, 0.745], + ["br46", -0.637, 2.464, 0.914], + ["br47", -0.738, 2.763, 0.453], + + // Support nodes + {"selfCollision":"false","group":""}, + ["bsl", 0.494, -1.391, 0.484], + ["bsl1", 0.457, 0.131, 0.484], + ["bsl2", 0.457, 1.761, 0.484], + ["bsm", 0.038, 0.293, 1.040], + ["bsr", -0.418, -1.391, 0.484], + ["bsr1", -0.381, 0.131, 0.484], + ["bsr2", -0.381, 1.761, 0.484] + ], + "beams" : [ + ["id1:", "id2:"], + + // Structural beams + {"beamType":"|NORMAL"}, + {"beamSpring":420000, "beamDamp":40}, + {"beamDeform":7000, "beamStrength":"FLT_MAX"}, + {"deformLimitExpansion":1.1}, + + // Fenders strong + + // Right + + // Left + + // Fenders weak + + // Right + ["br3", "br2"], + ["br3", "br1"], + ["br2", "br0"], + ["br1", "br0"], + ["br4", "br2"], + + // Left + ["bl4", "bl2"], + ["bl2", "bl0"], + ["bl1", "bl0"], + ["bl3", "bl2"], + ["bl3", "bl1"], + + // Door strong + + // Right + + // Left + + // Door weak + + // Right + + // Left + + // Roof strong + + // Roof weak + ["bmm2", "bmm3"], + ["bmm3", "bmm4"], + + // Roof body connect + + // Right + + // Left + + // Cowl panel strong + + // Cowl panel weak + ["bmm1", "bmm0"], + + // Quarterpanels connect + + // Right + + // Left + + // Quarterpanels + + // Right + ["br47", "br44"], + ["br45", "br46"], + ["br44", "br45"], + + // Left + ["bl45", "bl46"], + ["bl47", "bl44"], + ["bl44", "bl45"], + + // Crossing beams + + // Doors + + // Right + + // Left + + // Fenders + + // Right + ["br4", "br3"], + ["br3", "br0"], + ["br1", "br2"], + + // Left + ["bl3", "bl0"], + ["bl4", "bl3"], + ["bl1", "bl2"], + + // Fenders connect + + // Right + + // Left + + // Quarterpanels connect + + // Right + + // Left + + // Quarterpanels + + // Right + + // Left + + // Roof + + // Body roof connect + + // Right + + // Left + + // Support beams + {"beamSpring":250000,"beamDamp":100}, + + // Doors + + // Right + ["bl3", "bl2"], + + // Left + + // Fenders + + // Right + ["bsr", "bmm0"], + ["bsr", "br3"], + ["br1", "bsr"], + ["br2", "bsr"], + ["br0", "bsr"], + ["br4", "bsr"], + ["bmm1", "bsr"], + + // Left + ["bl2", "bsl"], + ["bsl", "bmm0"], + ["bl1", "bsl"], + ["bmm1", "bsl"], + ["bl0", "bsl"], + ["bsl", "bl3"], + ["bl4", "bsl"], + + // Quarterpanels + + // Right + ["bsr2", "br44"], + ["br46", "bsr2"], + ["br47", "bsr2"], + ["br45", "bsr2"], + + // Left + ["bl47", "bsl2"], + ["bsl2", "bl44"], + ["bl45", "bsl2"], + ["bl46", "bsl2"], + + // Roof + ["bsm", "bmm2"], + ["bmm4", "bsm"], + ["bmm3", "bsm"] + + //Connecting beams + {"beamType":"|NORMAL"}, + {"beamSpring":501000,"beamDamp":75}, + {"deformLimitExpansion":""}, + + //Fenders front + {"beamType":"|NORMAL"}, + {"beamDeform":8500,"beamStrength":30000}, + ["bl0","rll4"], + ["bl0","rll2"], + ["bl1","rll4"], + ["bl2","rll3"], + ["bl2","rll5"], + ["bl3","rll5"], + ["bl3","ftl1"], + ["bl4","ftl0"], + ["bl4","fsl1"], + + ["br0","rlr4"], + ["br0","rlr2"], + ["br1","rlr4"], + ["br2","rlr3"], + ["br2","rlr5"], + ["br3","rlr5"], + ["br3","ftr1"], + ["br4","ftr0"], + ["br4","fsr1"], + + //Fenders middle + {"beamDeform":8500,"beamStrength":"FLT_MAX"}, + + //Fenders rear + {"beamDeform":12000}, + + //Cowl panel + + ["bmm0","fwm"], + ["bmm1","rcm0"], + ["bmm1","fwm"], + + //Doors + {"beamDeform":6500}, + + //Roof + {"beamDeform":8500}, + + ["bmm2","rcm1"], + ["bmm3","rcm4"], + ["bmm4","rccl0"], + ["bmm4","rccr0"], + + //Quarter panels + {"beamDeform":60000}, + ["bl47","rucl"], + ["bl44","rucl"], + ["bl45","rrcl2"], + ["bl46","rrcl2"], + + ["br47","rucr"], + ["br44","rucr"], + ["br44","rucm"], + ["br45","rrcr2"], + ["br46","rrcr2"] + + ], + "triangles" : [ + ["id1:", "id2:", "id3:"], + ["id1:", "id2:", "id3:"], + ["br0", "br2", "br1"], + ["br2", "br3", "br1"], + ["bl0", "bl1", "bl2"], + ["bl1", "bl3", "bl2"] + ] + } +} \ No newline at end of file diff --git a/examples/regression_jbeam/front-bumper-columns-repro.jbeam b/examples/regression_jbeam/front-bumper-columns-repro.jbeam new file mode 100644 index 00000000..d572521c --- /dev/null +++ b/examples/regression_jbeam/front-bumper-columns-repro.jbeam @@ -0,0 +1,276 @@ +{"nascar_fb_generation4_montecarlo": { + "information":{ + "authors":"Solarpower07", + "name":"Front Bumper Grand Prix", + }, + "slotType" : "nascar_fb_generation4", + "nodes": [ + ["id", "posX", "posY", "posZ"], + {"nodeWeight":1.0}, + {"frictionCoef":0.7}, + {"nodeMaterial":"|NM_PLASTIC"}, + {"collision":true}, + {"selfCollision":true}, + {"group":"gp_fb"}, + ["fbm0",0.038,-2.481,0.118], + ["fbl5",0.436,-2.423,0.118], + ["fbr5",-0.361,-2.423,0.118], + ["fbm1",0.038,-2.4,0.424], + ["fbl6",0.421,-2.339,0.424], + ["fbr6",-0.345,-2.339,0.424], + ["fbl3",0.756,-2.256,0.118], + ["fbr3",-0.681,-2.256,0.118], + ["fbl4",0.696,-2.212,0.446], + ["fbr4",-0.621,-2.212,0.446], + ["fbm2",0.038,-2.204,0.615], + ["fbl7",0.356,-2.18,0.609], + ["fbr7",-0.28,-2.18,0.609], + ["fbl0",0.932,-2.005,0.12], + ["fbr0",-0.857,-2.005,0.12], + ["fbl1",0.893,-1.987,0.469], + ["fbr1",-0.817,-1.987,0.469], + ["fbl2",0.753,-1.872,0.7], + ["fbr2",-0.678,-1.872,0.7], + + {"selfCollision":false, "collision":false}, + ["nl17",0.038,-1.875,0.339], + + ], + "beams": [ + ["id1:", "id2:"], + //Structural beams + {"beamType":"|NORMAL"}, + {"beamSpring":610000,"beamDamp":60}, + {"beamDeform":15000,"beamStrength":"FLT_MAX","disableMeshBreaking":true}, + {"deformLimitExpansion":1.1}, + ["fbr6","fbr7"], + ["fbm2","fbl7"], + ["fbl7","fbl2"], + ["fbm2","fbr7"], + ["fbr7","fbr2"], + ["fbl4","fbl3"], + ["fbm0","fbm1"], + ["fbl6","fbl5"], + ["fbl6","fbl4"], + ["fbl4","fbl1"], + ["fbl1","fbl0"], + ["fbr4","fbr2"], + ["fbm0","fbr5"], + ["fbl4","fbl7"], + ["fbr6","fbr4"], + ["fbl3","fbl0"], + ["fbr4","fbr3"], + ["fbr1","fbm0"], + ["fbl4","fbl2"], + ["fbm1","fbr6"], + ["fbl1","fbl2"], + ["fbr4","fbr7"], + ["fbr1","fbr2"], + ["fbr4","fbr1"], + ["fbm0","fbl5"], + ["fbr3","fbr0"], + ["fbr6","fbr5"], + ["fbm1","fbm2"], + ["fbl6","fbl7"], + ["fbl5","fbl3"], + ["fbm1","fbl6"], + ["fbr5","fbr3"], + + //Crossing beams + {"deformLimitExpansion":""}, + {"beamDeform":10000,"beamStrength":"FLT_MAX","disableMeshBreaking":false}, + ["fbm2","fbl6"], + ["fbr5","fbm1"], + ["fbm2","fbr6"], + ["fbr5","fbr4"], + ["fbr1","fbr3"], + ["fbr4","fbr0"], + ["fbl5","fbm1"], + ["fbl6","fbl3"], + ["fbl5","fbl4"], + ["fbl1","fbl3"], + ["fbl4","fbl0"], + ["fbm1","fbl7"], + ["fbr6","fbr3"], + ["fbm1","fbr7"], + ["fbm0","fbl6"], + ["fbm0","fbr6"], + + //Support beams + {"beamSpring":150000,"beamDamp":140}, + {"beamDeform":6000,"beamStrength":"FLT_MAX","disableMeshBreaking":true}, + ["fbr1","nl17"], + ["nl17","fbm0"], + ["fbm1","nl17"], + ["fbl6","nl17"], + ["fbl4","nl17"], + ["fbr6","nl17"], + ["fbr4","nl17"], + ["nl17","fbr0"], + ["fbr7","nl17"], + ["fbl1","nl17"], + ["fbl7","nl17"], + ["nl17","fbl3"], + ["nl17","fbl0"], + ["nl17","fbr3"], + ["nl17","fbl2"], + ["fbm2","nl17"], + ["nl17","fbr2"], + ["nl17","fbr5"], + ["nl17","fbl5"], + + {"beamDeform":4000}, //Sides + + + //Main connecting beams + {"beamSpring":760000,"beamDamp":95,"disableMeshBreaking":false}, + {"beamDeform":4500,"beamStrength":50000,"beamLongBound":1,"beamShortBound":1}, + {"deformLimitExpansion":1.35}, + + //Left + {"breakGroup":"fb_attach_l","breakGroupType":0}, + + ["fbl0","fsl0"], + ["fbl1","fsl0"], + ["fbl2","fsl1"], + ["fbl3","fsl2"], + ["fbl4","fsl3"], + ["fbl5","fsl2"], + ["fbl6","fsl3"], + + ["fbl0","rll2"], + ["fbl1","rll1"], + ["fbl2","ftl0"], + ["fbl3","rll0"], + ["fbl4","rll1"], + ["fbl5","rll0"], + ["fbl6","rll1"], + ["fbl7","ftl0"], + + ["fbl2","bl4"], + ["fbl1","bl2"], + ["fbl0","bl0"], + + //Right + {"breakGroup":"fb_attach_r"}, + + ["fbr0","fsr0"], + ["fbr1","fsr0"], + ["fbr2","fsr1"], + ["fbr3","fsr2"], + ["fbr4","fsr3"], + ["fbr5","fsr2"], + ["fbr6","fsr3"], + + ["fbr0","rlr2"], + ["fbr1","rlr1"], + ["fbr2","ftr0"], + ["fbr3","rlr0"], + ["fbr4","rlr1"], + ["fbr5","rlr"], + ["fbr6","rlr1"], + ["fbr7","ftr0"], + + ["fbr2","br4"], + ["fbr1","br2"], + ["fbr0","br0"], + + //Middle + {"breakGroup":"fb_attach_m","breakGroupType":0}, + {"beamDeform":5000}, + {"deformLimitExpansion":1.05}, + + ["fbm0","fsm0"], + ["fbm1","fsm0"], + ["fbm1","fsm1"], + ["fbm2","fsm1"], + + //Rigid Node Damping beams + {"beamType":"|SUPPORT"}, + {"beamSpring":501000,"beamDamp":155}, + {"beamDeform":6000,"beamStrength":150000}, + {"beamPrecompression":0.97}, + {"breakGroup":""}, + + ["fb15","fr8"], + ["fb15","fr10"], + ["fb15","fr11"], + ["fb15","fr14"], + ["fb15","fr15"], + ["fb15","fr16"], + ["fb15","fr17"], + + + //Supporting connecting beams + {"beamType":"|SUPPORT"}, + {"beamSpring":1100000,"beamDamp":125}, + {"beamDeform":3000,"beamStrength":250000}, + {"deformLimitExpansion":"","deformLimit":""}, + {"beamPrecompression":1}, + + ["fb0","fs1"], + ["fb0","fs2"], + ["fb1","fs5"], + ["fb1","fs6"], + ["fb1","fs7"], + ["fb1","fs8"], + ["fb2","fs9"], + ["fb3","fs10"], + ["fb5","fs11"], + ["fb5","fs13"], + ["fb6","fs12"], + ["fb6","fs14"], + ["fb17","fs11"], + ["fb17","fs13"], + ["fb22","fs12"], + ["fb22","fs14"], + + ["fb4","fr3"], + ["fb4","fr6"], + ["fb7","fr6"], + ["fb8","fr3"], + + ["fb0","spl0"], + ["fb2","spl1"], + ["fb3","spl2"], + + ["fb9","spl9"], + ["fb9","spl7"], + + ["fb12","spl10"], + ["fb12","spl8"], + + ], + "triangles":[ + ["id1:", "id2:", "id3:"], + {"dragCoef":3.5,"liftCoef":50}, + ["fbr7","fbr2","fbr4"], + ["fbr4","fbr6","fbr7"], + ["fbr1","fbr4","fbr2"], + ["fbr0","fbr3","fbr1"], + ["fbr1","fbr3","fbr4"], + ["fbr3","fbr5","fbr4"], + ["fbr5","fbr6","fbr4"], + ["fbr6","fbr5","fbm1"], + ["fbm1","fbr5","fbm0"], + ["fbm1","fbm0","fbl5"], + ["fbl6","fbm1","fbl5"], + ["fbr6","fbm1","fbr7"], + ["fbm1","fbm2","fbr7"], + ["fbm2","fbm1","fbl7"], + ["fbl6","fbl7","fbm1"], + ["fbl4","fbl7","fbl6"], + ["fbl7","fbl4","fbl2"], + ["fbl1","fbl2","fbl4"], + ["fbl1","fbl4","fbl3"], + ["fbl3","fbl4","fbl5"], + ["fbl5","fbl4","fbl6"], + ["fbl0","fbl1","fbl3"], + ], + "flexbodies": [ + ["mesh", "[group]:", "nonFlexMaterials"], + ["gp_fb", ["gp_fb"]], + ["gp_fb_inter", ["gp_fb"]], + ], +}, +} diff --git a/examples/regression_jbeam/hood-columns-repro.jbeam b/examples/regression_jbeam/hood-columns-repro.jbeam new file mode 100644 index 00000000..48e9022c --- /dev/null +++ b/examples/regression_jbeam/hood-columns-repro.jbeam @@ -0,0 +1,226 @@ +{"nascar_hood_generation4": { + "information":{ + "authors":"Solarpower07", + "name":"Hood Monte Carlo", + }, + "slots": [ + ["type", "default", "description"], + ["nascar_flaps_hood","nascar_flaps_hood", "Hood Flaps"], + ], + "slotType" : "nascar_hood_generation4", + "nodes": [ + ["id", "posX", "posY", "posZ"], + {"nodeWeight":0.9}, + {"frictionCoef":0.7}, + {"nodeMaterial":"|NM_PLASTIC"}, + {"collision":true}, + {"selfCollision":true}, + {"group":"gen4_hood"}, + ["hdl3",0.344,-2.156,0.623], + ["hdr3",-0.268,-2.156,0.623], + ["hdl0",0.74,-1.866,0.71], + ["hdr0",-0.664,-1.866,0.71], + ["hdl4",0.365,-1.826,0.78], + ["hdr4",-0.289,-1.826,0.78], + ["hdl5",0.432,-1.383,0.886], + ["hdr5",-0.356,-1.383,0.886], + ["hdl1",0.789,-1.359,0.852], + ["hdr1",-0.713,-1.359,0.852], + ["hdl6",0.439,-1.022,0.951], + ["hdr6",-0.363,-1.022,0.951], + ["hdl2",0.799,-0.924,0.939], + ["hdr2",-0.723,-0.924,0.939], + + {"selfCollision":"false","nodeWeight":2.2}, + {"group":""}, + ["hd_supp",0.038,-1.42,0.481], + ], + "beams": [ + ["id1:", "id2:"], + //Structural beams + {"beamType":"|NORMAL"}, + {"beamSpring":810000,"beamDamp":55}, + {"beamDeform":7000,"beamStrength":"FLT_MAX"}, + {"deformLimitExpansion":1.1}, + ["hdl5","hdl6"], + ["hdl3","hdl0"], + ["hdl5","hdr5"], + ["hdr5","hdr6"], + ["hdl6","hdl2"], + ["hdl4","hdl5"], + ["hdl2","hdl1"], + ["hdr2","hdr1"], + ["hdr3","hdr4"], + ["hdr4","hdr0"], + ["hdl6","hdr6"], + ["hdr3","hdr0"], + ["hdl5","hdl1"], + ["hdr1","hdr0"], + ["hdl3","hdl4"], + ["hdl4","hdl0"], + ["hdl1","hdl0"], + ["hdl3","hdr3"], + ["hdr4","hdr5"], + ["hdr6","hdr2"], + ["hdr5","hdr1"], + ["hdl4","hdr4"], + + //Crossing beams + {"beamDeform":5000}, + {"deformLimitExpansion":""}, + ["hdl5","hdl0"], + ["hdl6","hdr5"], + ["hdl5","hdr6"], + ["hdl5","hdl2"], + ["hdl4","hdr5"], + ["hdl4","hdl1"], + ["hdr5","hdr2"], + ["hdr4","hdr1"], + ["hdr5","hdr0"], + ["hdl3","hdr4"], + ["hdr6","hdr1"], + ["hdl6","hdl1"], + ["hdl4","hdr3"], + ["hdl5","hdr4"], + + //Support beams + {"beamSpring":65000,"beamDamp":145}, + {"beamDeform":3200}, + ["hdl3","hd_supp"], + ["hdl6","hd_supp"], + ["hd_supp","hdr2"], + ["hd_supp","hdl1"], + ["hd_supp","hdl0"], + ["hd_supp","hdr1"], + ["hdl4","hd_supp"], + ["hdr4","hd_supp"], + ["hdr3","hd_supp"], + ["hd_supp","hdr0"], + ["hd_supp","hdl2"], + ["hdr5","hd_supp"], + ["hdl5","hd_supp"], + ["hdr6","hd_supp"], + + {"beamSpring":250000,"beamDamp":125}, + {"beamDeform":2500}, + + + + //Front Impact Rigid + {"beamSpring":450000,"beamDamp":125}, + {"beamDeform":1500}, + ["hdl3","hdl2"], + ["hdl3","hdl6"], + + ["hdr3","hdr2"], + ["hdr3","hdr6"], + + + //Hinge beams + {"beamType":"|NORMAL"}, + {"beamSpring":1001000,"beamDamp":85}, + {"beamDeform":200000,"beamStrength":31500}, + + //Left + {"breakGroup":"hood_hinge_l","breakGroupType":0}, + ["hdl2","bl12"], + ["hdl2","fwl0"], + ["hdl2","bl17"], + + //Right + {"breakGroup":"hood_hinge_r"}, + ["hdr2","br12"], + ["hdr2","fwr0"], + ["hdr2","br17"], + + + + //Latch beams + {"beamType":"|NORMAL","deformGroup":"","deformTriggerRatio":""}, + {"beamSpring":750000,"beamDamp":65}, + {"beamDeform":4000,"beamStrength":110000}, + + {"breakGroup":"hood_latch"}, + + ["hdl3","rll1"], + ["hdl3","ftl0"], + + ["hdr3","rlr1"], + ["hdr3","ftr0"], + + + //Latch Support beams + {"beamType":"|SUPPORT"}, + {"beamSpring":1150000,"beamDamp":65}, + {"beamDeform":5000,"beamStrength":450000}, + + {"breakGroup":""}, + + ["hd4","fb22"], + ["hd2","fb6"], + ["hd0","fb1"], + ["hd1","fb5"], + ["hd3","fb13"], + ["hd","fb"], + + ["hdr0","bl4"], + ["hdr1","bl7"], + + ["hdr0","br4"], + ["hdr1","br7"], + + ["hdl0","rll1"], + ["hdl3","rll5"], + ["hdl3","ftl0"], + + ["hdr0","rlr1"], + ["hdr3","rlr5"], + ["hdr3","ftr0"], + + //Bummper attach + + + + {"breakGroup":""}, + + //Support beams + {"beamType":"|SUPPORT"}, + {"beamSpring":850000,"beamDamp":135}, + {"beamDeform":3000,"beamStrength":350000}, + + ["hdl4","ftl0"], + ["hdl5","ftl1"], + ["hdl6","fwl3"], + + ["hdr4","ftr0"], + ["hdr5","ftr1"], + ["hdr6","fwr3"], + + ], + "triangles":[ + ["id1:", "id2:", "id3:"], + {"dragCoef":6,"liftCoef":70}, + ["hdl4","hdl3","hdl0"], + ["hdr4","hdr0","hdr3"], + ["hdr6","hdr1","hdr5"], + ["hdr2","hdr1","hdr6"], + ["hdl6","hdl5","hdl1"], + ["hdl2","hdl6","hdl1"], + ["hdr4","hdl4","hdr5"], + ["hdl4","hdl5","hdr5"], + ["hdl6","hdr5","hdl5"], + ["hdr5","hdl6","hdr6"], + ["hdl1","hdl4","hdl0"], + ["hdl5","hdl4","hdl1"], + ["hdl4","hdr3","hdl3"], + ["hdr3","hdl4","hdr4"], + ["hdr5","hdr1","hdr4"], + ["hdr1","hdr0","hdr4"], + ], + "flexbodies": [ + ["mesh", "[group]:", "nonFlexMaterials"], + ["gp_hood", ["gen4_hood"]], + ["gp_hood_inter", ["gen4_hood"]], + ], +}, +} diff --git a/examples/regression_jbeam/middle-group-sign-repro.jbeam b/examples/regression_jbeam/middle-group-sign-repro.jbeam new file mode 100644 index 00000000..87397dee --- /dev/null +++ b/examples/regression_jbeam/middle-group-sign-repro.jbeam @@ -0,0 +1,20 @@ +{ +"testpart":{ + "nodes":[ + ["id", "posX", "posY", "posZ"], + // Synthetic regression-test fixture, not vetted by the jbeam + // maintainer and not intended as a demo/example. + // + // The middle group holds two nodes on opposite sides of the centre + // line at one Y and one Z, so X alone decides which of them is read + // first. Read as written nm0 comes first. Read as a distance from the + // centre line nm1 does, since 0.02 is nearer than 0.05. The outer two + // nodes are here so the spec also says that the middle group is the + // one being measured. + ["nl0", 0.4, 0.0, 0.1], + ["nm0", -0.05, 0.0, 0.1], + ["nm1", 0.02, 0.0, 0.1], + ["nr0", -0.4, 0.0, 0.1], + ], +}, +} diff --git a/jbeam-edit.cabal b/jbeam-edit.cabal index ff3fa40b..d8e15a3e 100644 --- a/jbeam-edit.cabal +++ b/jbeam-edit.cabal @@ -317,6 +317,7 @@ test-suite jbeam-edit-transformation-test main-is: Spec.hs hs-source-dirs: test-extra/transformation other-modules: + Spec.ColumnOrder Spec.Config Spec.Helpers Spec.Regression diff --git a/test-extra/transformation/Spec.hs b/test-extra/transformation/Spec.hs index ae8ad56b..38349e1f 100644 --- a/test-extra/transformation/Spec.hs +++ b/test-extra/transformation/Spec.hs @@ -20,6 +20,7 @@ import JbeamEdit.Transformation.BeamExtraction (beamInKnownSet) import JbeamEdit.Transformation.BeamValidation import JbeamEdit.Transformation.Config import JbeamEdit.Transformation.Types (Beam) +import Spec.ColumnOrder (columnOrderSpec, partFixedPointSpec) import Spec.Config (configParsingSpec) import Spec.Helpers (parseJbeamFile) import Spec.Regression @@ -157,6 +158,8 @@ main = hspec $ do mapM_ (testFixedPoint "cfg-example" tfConfig) inputFiles configParsingSpec sortAxesSpec + columnOrderSpec + partFixedPointSpec beamValidationSpec supportRenameIdempotencySpec letterEndingNodesSpec diff --git a/test-extra/transformation/Spec/ColumnOrder.hs b/test-extra/transformation/Spec/ColumnOrder.hs new file mode 100644 index 00000000..48e5432c --- /dev/null +++ b/test-extra/transformation/Spec/ColumnOrder.hs @@ -0,0 +1,211 @@ +module Spec.ColumnOrder ( + columnOrderSpec, + partFixedPointSpec, +) where + +import Data.Map qualified as M +import Data.Scientific (Scientific) +import Data.Text (Text) +import Data.Text qualified as T +import JbeamEdit.Transformation (transform) +import JbeamEdit.Transformation.Config ( + TransformationConfig (..), + defaultSortAxes, + newTransformationConfig, + ) +import JbeamEdit.Transformation.Types (Axis (..), SortAxes (..)) +import Spec.Helpers ( + originalNamesInOutputOrder, + parseJbeamFile, + vertexNamesInOrder, + ) +import Test.Hspec + +hoodFixture :: FilePath +hoodFixture = "examples/regression_jbeam/hood-columns-repro.jbeam" + +frontBumperFixture :: FilePath +frontBumperFixture = "examples/regression_jbeam/front-bumper-columns-repro.jbeam" + +bodyFixture :: FilePath +bodyFixture = "examples/regression_jbeam/body-columns-repro.jbeam" + +middleGroupFixture :: FilePath +middleGroupFixture = "examples/regression_jbeam/middle-group-sign-repro.jbeam" + +noBeamsFixture :: FilePath +noBeamsFixture = "examples/regression_jbeam/no-beams-repro.jbeam" + +bodyFrontEdgeOrder :: Text -> [Text] +bodyFrontEdgeOrder = bodyOrder [0, 2, 4, 1, 3] + +bodyRearColumnOrder :: Text -> [Text] +bodyRearColumnOrder = bodyOrder [47, 44, 45, 46] + +-- Nothing sits between the two columns in the fixture. +bodySideOrder :: Text -> [Text] +bodySideOrder prefix = bodyFrontEdgeOrder prefix <> bodyRearColumnOrder prefix + +bodyOrder :: [Int] -> Text -> [Text] +bodyOrder indices prefix = map named indices + where + named index = prefix <> T.pack (show index) + +orderWith :: SortAxes -> Scientific -> FilePath -> IO [Text] +orderWith axes threshold path = do + topNode <- parseJbeamFile path + let config = + newTransformationConfig + { sortAxes = axes + , ySortingThreshold = threshold + } + case transform M.empty config topNode of + Left err -> fail ("transform failed: " ++ T.unpack err) + Right (_, _, _, resultNode) -> + pure (originalNamesInOutputOrder topNode resultNode) + +group :: Text -> [Text] -> [Text] +group prefix = filter (T.isPrefixOf prefix) + +columnOrderSpec :: Spec +columnOrderSpec = describe "the reading order each part asks for" $ do + it "reads the hood one column at a time, the outer column first" $ do + order <- orderWith (SortAxes AxisX AxisY AxisZ) 0.23 hoodFixture + order + `shouldBe` [ "hdl0" + , "hdl1" + , "hdl2" + , "hdl3" + , "hdl4" + , "hdl5" + , "hdl6" + , "hdr0" + , "hdr1" + , "hdr2" + , "hdr3" + , "hdr4" + , "hdr5" + , "hdr6" + , "hd_supp" + ] + + it "reads each column of the front bumper from the bottom up" $ do + pendingWith + "The bumper's columns are upright stacks, so X groups them and Z walks \ + \them, and two things are missing. X is compared as written, so the \ + \innermost column comes first and the order is fbl5 fbl6 fbl7 fbl3 \ + \fbl4 fbl1 fbl2 fbl0. Comparing the distance from the centre line, \ + \negated where X groups, gets the columns the right way round but \ + \still gives fbl3 fbl0 fbl1 fbl2 at the outer end, because at 0.23 \ + \fbl0, fbl1 and fbl3 share a band and Z reads fbl3 first as the \ + \lowest of them. The rest needs the columns taken from the beams, \ + \where fbl0, fbl1 and fbl2 form one chain and fbl3, fbl4 another." + order <- orderWith (SortAxes AxisX AxisZ AxisY) 0.23 frontBumperFixture + group "fbl" order + `shouldBe` ["fbl0", "fbl1", "fbl2", "fbl3", "fbl4", "fbl5", "fbl6", "fbl7"] + group "fbm" order `shouldBe` ["fbm0", "fbm1", "fbm2"] + + it "reads the bumper's right side as the mirror of its left" $ do + pendingWith + "No change to the tool turns this one green. The right side holds a \ + \beam between fbr1 and fbm0 where the left side has one between fbl1 \ + \and fbl0, and of the 31 beams here whose two nodes both have a mirror \ + \node, that is the only pair with no mirror image. The order asserted \ + \here needs that beam to have one, so it waits on the file and not on \ + \the code, and the fixture is left exactly as it was sent. What the \ + \tool is missing meanwhile is only the columns from the beams: the \ + \side reads fbr3 fbr0 fbr1 fbr2 fbr4 fbr5 fbr6 fbr7 today, and its X \ + \values are negative, so comparing X as written already puts the outer \ + \column first here." + order <- orderWith (SortAxes AxisX AxisZ AxisY) 0.23 frontBumperFixture + group "fbr" order + `shouldBe` ["fbr0", "fbr1", "fbr2", "fbr3", "fbr4", "fbr5", "fbr6", "fbr7"] + + it "reads the body's front edge as two columns" $ do + pendingWith + "The front edge leans: its first column runs 0.152 along Y as it rises \ + \and the next one starts only 0.008 behind where that column ends. All \ + \five nodes share one band at 0.23 and Z then reads them bl0 bl1 bl2 \ + \bl3 bl4, which is the order the file already has and still not the \ + \order asked for. A threshold of 0.16 does separate the two columns, \ + \and it leaves the rear column below reading bl45 bl46 bl47 bl44, so \ + \no one threshold serves both. Taking the columns from the beams gives \ + \bl0 bl2 bl4 bl1 bl3. The rear column of the same side is the spec \ + \below, which asks for the hold-back instead." + order <- orderWith (SortAxes AxisY AxisZ AxisX) 0.23 bodyFixture + take 5 (group "bl" order) `shouldBe` bodyFrontEdgeOrder "bl" + + it "reads the body's right side the same way as its left" $ do + pendingWith + "Today the right side reads br0 br1 br2 br3 br4 and ends br45 br46 \ + \br47 br44, so it needs the columns from the beams and the hold-back \ + \for its rear column, as the left side does. X is only the tie break \ + \with these axes, and this side comes out the same whether it is read \ + \as written or as a distance from the centre line, so nothing here \ + \rests on the two sides being compared the same way." + order <- orderWith (SortAxes AxisY AxisZ AxisX) 0.23 bodyFixture + group "br" order `shouldBe` bodySideOrder "br" + + it "never reads the rear column before its lowest node" $ do + pendingWith + "The rear column climbs 0.461 in Z and travels 0.299 towards the front \ + \while it does, which is more than the threshold, so banding Y alone \ + \puts bl45 and bl46 one band ahead of bl47 and bl44 and reads them \ + \bl45 bl46 bl47 bl44. Holding a column back to the band its lowest \ + \node landed in gives bl47 bl44 bl45 bl46, which is what was decided." + order <- orderWith (SortAxes AxisY AxisZ AxisX) 0.23 bodyFixture + let names = group "bl" order + drop (length names - 4) names `shouldBe` bodyRearColumnOrder "bl" + + it "leaves the body's middle nodes alone" $ do + order <- orderWith (SortAxes AxisY AxisZ AxisX) 0.23 bodyFixture + group "bmm" order `shouldBe` ["bmm0", "bmm1", "bmm2", "bmm3", "bmm4"] + + it "reads the middle group by X as written, not by its distance from the centre" $ do + order <- orderWith (SortAxes AxisY AxisZ AxisX) 0.05 middleGroupFixture + order `shouldBe` ["nl0", "nm0", "nm1", "nr0"] + + it "reads a part with no beams in bands, since it has no chains to read" $ do + order <- orderWith defaultSortAxes 0.05 noBeamsFixture + order `shouldBe` ["nl0", "nl1", "nr2", "nr3"] + +namesTwice + :: SortAxes + -> Scientific + -> FilePath + -> IO + ( [(Text, (Double, Double, Double))] + , [(Text, (Double, Double, Double))] + ) +namesTwice axes threshold path = do + topNode <- parseJbeamFile path + let config = + newTransformationConfig + { sortAxes = axes + , ySortingThreshold = threshold + } + run node = + case transform M.empty config node of + Left err -> fail ("transform failed: " ++ T.unpack err) + Right (_, _, _, resultNode) -> pure resultNode + onceNode <- run topNode + twiceNode <- run onceNode + pure (vertexNamesInOrder onceNode, vertexNamesInOrder twiceNode) + +-- Uses the axes and thresholds of columnOrderSpec. +partFixedPointSpec :: Spec +partFixedPointSpec = describe "transforming a part a second time" $ do + it "writes the hood out exactly as the first transform left it" $ do + (once, twice) <- namesTwice (SortAxes AxisX AxisY AxisZ) 0.23 hoodFixture + once `shouldNotBe` [] + twice `shouldBe` once + + it "writes the front bumper out exactly as the first transform left it" $ do + (once, twice) <- namesTwice (SortAxes AxisX AxisZ AxisY) 0.23 frontBumperFixture + once `shouldNotBe` [] + twice `shouldBe` once + + it "writes the body out exactly as the first transform left it" $ do + (once, twice) <- namesTwice (SortAxes AxisY AxisZ AxisX) 0.23 bodyFixture + once `shouldNotBe` [] + twice `shouldBe` once diff --git a/test-extra/transformation/Spec/Config.hs b/test-extra/transformation/Spec/Config.hs index fc824f84..246d8e44 100644 --- a/test-extra/transformation/Spec/Config.hs +++ b/test-extra/transformation/Spec/Config.hs @@ -75,3 +75,11 @@ configParsingSpec = describe "the transformation config parser" $ do parseField ySortingThreshold "" `shouldBe` Right defaultSortingThreshold parseField supportThreshold "" `shouldBe` Right defaultSupportThreshold parseField xSortingThreshold "" `shouldBe` Right Nothing + + it "rejects an x-sorting-threshold on a part that groups by X already" $ + pendingWith + "No config key names the sort axes yet, so no source can say that a \ + \part groups by X, and a spec that wrote one would be inventing the \ + \syntax. x-sorting-threshold keeps its own axis, so the two together \ + \ask for the same axis twice, which is what has to be rejected. The \ + \assertion goes in once the key has a name (question D)." diff --git a/test-extra/transformation/Spec/Helpers.hs b/test-extra/transformation/Spec/Helpers.hs index da7c90b0..8e7c7637 100644 --- a/test-extra/transformation/Spec/Helpers.hs +++ b/test-extra/transformation/Spec/Helpers.hs @@ -2,6 +2,8 @@ module Spec.Helpers ( parseJbeamFile, vertexPositionsInOrder, + vertexNamesInOrder, + originalNamesInOutputOrder, vertexCoordinatesInOrder, vertexTextsInOrder, vertexCoordinates, @@ -55,6 +57,30 @@ vertexPositionsInOrder topNode = , Just (Number yNum) <- [inner V.!? 2] ] +vertexNamesInOrder :: Node -> [(Text, (Double, Double, Double))] +vertexNamesInOrder topNode = + case NP.queryNodes nodesQuery topNode >>= NP.expectArray nodesQuery of + Left _ -> [] + Right rows -> + [ (name, (realToFrac (nvValue x), realToFrac (nvValue y), realToFrac (nvValue z))) + | row <- V.toList rows + , Just inner <- [maybeArray row] + , Just (String name) <- [inner V.!? 0] + , name /= "id" + , Just (Number x) <- [inner V.!? 1] + , Just (Number y) <- [inner V.!? 2] + , Just (Number z) <- [inner V.!? 3] + ] + +-- transform renames while it reorders, so the vertices are matched by position, which survives. +originalNamesInOutputOrder :: Node -> Node -> [Text] +originalNamesInOutputOrder inputNode outputNode = + map (named . snd) (vertexNamesInOrder outputNode) + where + before = + M.fromList [(position, name) | (name, position) <- vertexNamesInOrder inputNode] + named position = M.findWithDefault "" position before + {- | Every vertex coordinate in a top node's "nodes" section, in the order `transform` wrote them out. Use this where the defect is about which vertex ended up where, rather than about which vertices survived.