diff --git a/examples/ast/jbeam/fender.hs b/examples/ast/jbeam/fender.hs index e3d1fe49..3d6d6dd5 100644 --- a/examples/ast/jbeam/fender.hs +++ b/examples/ast/jbeam/fender.hs @@ -94,7 +94,7 @@ Object ( ArrayValue { avElements = [ - ( String "bfl_f", True ), + ( String "bfl0", True ), ( Number ( NumberValue { nvText = "0.959", nvValue = 0.959 } ), True ), @@ -257,7 +257,7 @@ Object ( ArrayValue { avElements = [ - ( String "bfr_f", True ), + ( String "bfr0", True ), ( Number ( NumberValue { nvText = "-0.906", nvValue = -0.906 } ), True ), diff --git a/examples/formatted_jbeam/fender-complex-jbfl.jbeam b/examples/formatted_jbeam/fender-complex-jbfl.jbeam index ff26f356..76ed8b98 100644 --- a/examples/formatted_jbeam/fender-complex-jbfl.jbeam +++ b/examples/formatted_jbeam/fender-complex-jbfl.jbeam @@ -23,7 +23,7 @@ // Left side {"group" : "cot_fender_l"}, - ["bfl_f", 0.959, -1.762, 0.576], + ["bfl0", 0.959, -1.762, 0.576], ["bfl1", 0.855, -1.788, 0.707], ["bfl2", 0.739, -1.845, 0.716], ["bfl3", 0.948, -1.435, 0.730], @@ -37,7 +37,7 @@ // Right side {"group" : "cot_fender_r"}, - ["bfr_f", -0.906, -1.737, 0.578], + ["bfr0", -0.906, -1.737, 0.578], ["bfr1", -0.807, -1.769, 0.707], ["bfr2", -0.691, -1.829, 0.716], ["bfr3", -0.890, -1.409, 0.729], diff --git a/examples/formatted_jbeam/fender-minimal-jbfl.jbeam b/examples/formatted_jbeam/fender-minimal-jbfl.jbeam index 480487b8..95ffe41f 100644 --- a/examples/formatted_jbeam/fender-minimal-jbfl.jbeam +++ b/examples/formatted_jbeam/fender-minimal-jbfl.jbeam @@ -23,7 +23,7 @@ // Left side {"group" : "cot_fender_l"}, - ["bfl_f", 0.959, -1.762, 0.576], + ["bfl0", 0.959, -1.762, 0.576], ["bfl1", 0.855, -1.788, 0.707], ["bfl2", 0.739, -1.845, 0.716], ["bfl3", 0.948, -1.435, 0.730], @@ -37,7 +37,7 @@ // Right side {"group" : "cot_fender_r"}, - ["bfr_f", -0.906, -1.737, 0.578], + ["bfr0", -0.906, -1.737, 0.578], ["bfr1", -0.807, -1.769, 0.707], ["bfr2", -0.691, -1.829, 0.716], ["bfr3", -0.890, -1.409, 0.729], diff --git a/examples/jbeam/fender.jbeam b/examples/jbeam/fender.jbeam index 10c4efca..43a08944 100644 --- a/examples/jbeam/fender.jbeam +++ b/examples/jbeam/fender.jbeam @@ -25,7 +25,7 @@ //Left side {"group":"cot_fender_l"}, - ["bfl_f",0.959,-1.762,0.576], + ["bfl0",0.959,-1.762,0.576], ["bfl1",0.855,-1.788,0.707], ["bfl2",0.739,-1.845,0.716], ["bfl3",0.948,-1.435,0.730], @@ -39,7 +39,7 @@ //Right side {"group":"cot_fender_r"}, - ["bfr_f",-0.906,-1.737,0.578], + ["bfr0",-0.906,-1.737,0.578], ["bfr1",-0.807,-1.769,0.707], ["bfr2",-0.691,-1.829,0.716], ["bfr3",-0.890,-1.409,0.729], diff --git a/examples/regression_jbeam/README.md b/examples/regression_jbeam/README.md new file mode 100644 index 00000000..ed93e21f --- /dev/null +++ b/examples/regression_jbeam/README.md @@ -0,0 +1,9 @@ +# Regression jbeam fixtures + +Small `.jbeam` files that exist purely to reproduce a specific bug for a +regression test. Unlike `examples/jbeam/`, these are **not** written or +vetted by the jbeam maintainer, not curated demo material, and +not picked up by `jbeam-edit-dump-ast` (which only scans `examples/jbeam/`). +Don't treat them as examples of good jbeam, and don't add to this +directory unless a test genuinely needs a fixture that can't be built +from what's already in the project. diff --git a/examples/regression_jbeam/letter-ending-nodes-repro.jbeam b/examples/regression_jbeam/letter-ending-nodes-repro.jbeam new file mode 100644 index 00000000..7188fd2a --- /dev/null +++ b/examples/regression_jbeam/letter-ending-nodes-repro.jbeam @@ -0,0 +1,34 @@ +{ +"testpart":{ + "nodes":[ + ["id", "posX", "posY", "posZ"], + // Names ending in a letter rather than a digit, in several prefix + // groups. These used to be dropped silently: every such group got + // the same SupportKey, and the insert replaced instead of merged, + // so only the last group survived. + ["nl_f", 1.0, -1.0, 0.0], + ["nr_f", -1.0, -1.0, 0.0], + ["nll", 1.0, 0.0, 0.0], + ["nrr", -1.0, 0.0, 0.0], + ["nl_r", 1.0, 1.0, 0.0], + ["nr_r", -1.0, 1.0, 0.0], + // Digit-ending names alongside them, so both paths are exercised. + ["nl0", 1.0, 2.0, 0.0], + ["nr0", -1.0, 2.0, 0.0], + ], + "beams":[ + ["id1:", "id2:"], + ["nl_f", "nll"], + ["nll", "nl_r"], + ["nl_r", "nl0"], + ["nr_f", "nrr"], + ["nrr", "nr_r"], + ["nr_r", "nr0"], + ], + "triangles":[ + ["id1:", "id2:", "id3:"], + ["nl_f", "nll", "nl_r"], + ["nr_f", "nrr", "nr_r"], + ], +}, +} diff --git a/examples/regression_jbeam/support-rename-idempotency-repro.jbeam b/examples/regression_jbeam/support-rename-idempotency-repro.jbeam new file mode 100644 index 00000000..b258fe3d --- /dev/null +++ b/examples/regression_jbeam/support-rename-idempotency-repro.jbeam @@ -0,0 +1,34 @@ +{ +"testpart":{ + "nodes":[ + ["id", "posX", "posY", "posZ"], + ["nl0", 1.0, -3.0, 0.0], + ["nl1", 1.0, -3.3, 0.0], + ["nl2", 1.0, -3.6, 0.0], + ["nl3", 1.0, -3.9, 0.5], + ["nl10", 1.0, 0.0, 0.0], + ["nl11", 1.0, 0.3, 0.0], + ["nl12", 1.0, 0.6, 0.0], + ["nl13", 1.0, 0.9, 0.5], + ["nl20", 1.0, 3.0, 0.0], + ["nl21", 1.0, 3.3, 0.0], + ["nl22", 1.0, 3.6, 0.0], + ["nl23", 1.0, 3.9, 0.5], + ], + "beams":[ + ["id1:", "id2:"], + ["nl0", "nl1"], + ["nl0", "nl2"], + ["nl0", "nl3"], + ["nl10", "nl11"], + ["nl10", "nl12"], + ["nl10", "nl13"], + ["nl20", "nl21"], + ["nl20", "nl22"], + ["nl20", "nl23"], + ], + "triangles":[ + ["id1:", "id2:", "id3:"], + ], +}, +} diff --git a/examples/transformed_jbeam/fender-after-frame-cfg-default.jbeam b/examples/transformed_jbeam/fender-after-frame-cfg-default.jbeam index 48fdcb37..6eb07612 100644 --- a/examples/transformed_jbeam/fender-after-frame-cfg-default.jbeam +++ b/examples/transformed_jbeam/fender-after-frame-cfg-default.jbeam @@ -23,7 +23,7 @@ // Left side {"group" : "cot_fender_l"}, - ["bfl_f", 0.959, -1.762, 0.576], + ["bfl0", 0.959, -1.762, 0.576], ["bfl1", 0.855, -1.788, 0.707], ["bfl2", 0.739, -1.845, 0.716], ["bfl3", 0.948, -1.435, 0.730], @@ -37,7 +37,7 @@ // Right side {"group" : "cot_fender_r"}, - ["bfr_f", -0.906, -1.737, 0.578], + ["bfr0", -0.906, -1.737, 0.578], ["bfr1", -0.807, -1.769, 0.707], ["bfr2", -0.691, -1.829, 0.716], ["bfr3", -0.890, -1.409, 0.729], diff --git a/examples/transformed_jbeam/fender-after-frame-cfg-example.jbeam b/examples/transformed_jbeam/fender-after-frame-cfg-example.jbeam index 48fdcb37..6eb07612 100644 --- a/examples/transformed_jbeam/fender-after-frame-cfg-example.jbeam +++ b/examples/transformed_jbeam/fender-after-frame-cfg-example.jbeam @@ -23,7 +23,7 @@ // Left side {"group" : "cot_fender_l"}, - ["bfl_f", 0.959, -1.762, 0.576], + ["bfl0", 0.959, -1.762, 0.576], ["bfl1", 0.855, -1.788, 0.707], ["bfl2", 0.739, -1.845, 0.716], ["bfl3", 0.948, -1.435, 0.730], @@ -37,7 +37,7 @@ // Right side {"group" : "cot_fender_r"}, - ["bfr_f", -0.906, -1.737, 0.578], + ["bfr0", -0.906, -1.737, 0.578], ["bfr1", -0.807, -1.769, 0.707], ["bfr2", -0.691, -1.829, 0.716], ["bfr3", -0.890, -1.409, 0.729], diff --git a/examples/transformed_jbeam/fender-cfg-default.jbeam b/examples/transformed_jbeam/fender-cfg-default.jbeam index 57e71067..42840269 100644 --- a/examples/transformed_jbeam/fender-cfg-default.jbeam +++ b/examples/transformed_jbeam/fender-cfg-default.jbeam @@ -14,53 +14,51 @@ }, "slotType" : "cot_fender", "nodes" : [ - ["id", "posX", "posY", "posZ"], + ["id", "posX", "posY", "posZ"], {"frictionCoef" : 0.7}, {"nodeMaterial" : "|NM_METAL"}, + + // Left side {"collision" : true}, {"group" : "cot_fender_l"}, {"nodeWeight" : 0.65}, {"selfCollision" : true}, - ["bfl0", 0.739, -1.845, 0.716], - ["bfl1", 0.855, -1.788, 0.707], - ["bfl2", 0.948, -1.435, 0.730], - ["bfl3", 0.756, -1.413, 0.843], - ["bfl4", 0.963, -1.024, 0.112], - ["bfl5", 0.964, -1.072, 0.507], - ["bfl6", 0.778, -1.008, 0.873], - ["bfl7", 0.987, -0.743, 0.109], - ["bfl8", 0.987, -0.744, 0.494], - ["bfl9", 0.812, -0.759, 0.896], - ["bfr0", -0.691, -1.829, 0.716], - ["bfr1", -0.807, -1.769, 0.707], - ["bfr2", -0.890, -1.409, 0.729], - ["bfr3", -0.700, -1.397, 0.843], - ["bfr4", -0.899, -1.005, 0.112], - ["bfr5", -0.900, -1.053, 0.508], - ["bfr6", -0.715, -0.991, 0.873], - ["bfr7", -0.916, -0.742, 0.112], - ["bfr8", -0.917, -0.746, 0.494], - ["bfr9", -0.734, -0.746, 0.888], + ["bfl0", 0.739, -1.845, 0.716], + ["bfl1", 0.959, -1.762, 0.576], + ["bfl2", 0.855, -1.788, 0.707], + ["bfl3", 0.948, -1.435, 0.730], + ["bfl4", 0.756, -1.413, 0.843], + ["bfl5", 0.963, -1.024, 0.112], + ["bfl6", 0.964, -1.072, 0.507], + ["bfl7", 0.778, -1.008, 0.873], + ["bfl8", 0.987, -0.743, 0.109], + ["bfl9", 0.987, -0.744, 0.494], + ["bfl10", 0.812, -0.759, 0.896], + + // Right side + {"group" : "cot_fender_r"}, + ["bfr0", -0.691, -1.829, 0.716], + ["bfr1", -0.906, -1.737, 0.578], + ["bfr2", -0.807, -1.769, 0.707], + ["bfr3", -0.890, -1.409, 0.729], + ["bfr4", -0.700, -1.397, 0.843], + ["bfr5", -0.899, -1.005, 0.112], + ["bfr6", -0.900, -1.053, 0.508], + ["bfr7", -0.715, -0.991, 0.873], + ["bfr8", -0.916, -0.742, 0.112], + ["bfr9", -0.917, -0.746, 0.494], + ["bfr10", -0.734, -0.746, 0.888], // Support nodes {"collision" : false}, {"group" : ""}, {"nodeWeight" : 1.2}, {"selfCollision" : false}, - ["bfsl", 0.684, -1.079, 0.507], - ["bfsr", -0.623, -1.064, 0.507], - - // Support nodes - {"group" : "cot_fender_r"}, - ["bfr_sr", -0.906, -1.737, 0.578], - {"collision" : true}, - {"group" : "cot_fender_l"}, - {"nodeWeight" : 0.65}, - {"selfCollision" : true}, - ["bfl_sl", 0.959, -1.762, 0.576], + ["bfsl", 0.684, -1.079, 0.507], + ["bfsr", -0.623, -1.064, 0.507], ], "beams" : [ - ["id1:", "id2:"], + ["id1:", "id2:"], // Structural beams // beamSpring: 451000, beamDamp: 50 @@ -72,100 +70,100 @@ {"beamDeform" : 6000}, // Front - ["bfr0", "bfr3"], - ["bfr0", "bfr1"], - ["bfr1", "bfr3"], - ["bfl3", "bfl0"], - ["bfl0", "bfl1"], - ["bfl0", "bfl1"], - ["bfl3", "bfl2"], - ["bfl0", "bfl2"], - ["bfr1", "bfr0"], - ["bfr3", "bfr2"], - ["bfr0", "bfr2"], - ["bfl3", "bfl1"], + ["bfr0", "bfr4"], + ["bfr0", "bfr2"], + ["bfr2", "bfr4"], + ["bfl4", "bfl0"], + ["bfl0", "bfl2"], + ["bfl1", "bfl2"], + ["bfl4", "bfl3"], + ["bfl1", "bfl3"], + ["bfr2", "bfr1"], + ["bfr4", "bfr3"], + ["bfr1", "bfr3"], + ["bfl4", "bfl2"], // Middle {"beamDeform" : 12000}, - ["bfl5", "bfl2"], - ["bfr5", "bfr2"], - ["bfl3", "bfl6"], - ["bfr3", "bfr6"], + ["bfl6", "bfl3"], + ["bfr6", "bfr3"], + ["bfl4", "bfl7"], + ["bfr4", "bfr7"], // Rear - ["bfl6", "bfl9"], - ["bfl4", "bfl5"], - ["bfl6", "bfl5"], - ["bfl4", "bfl7"], - ["bfl5", "bfl8"], - ["bfl7", "bfl8"], - ["bfl9", "bfl8"], - ["bfr6", "bfr9"], - ["bfr5", "bfr4"], - ["bfr5", "bfr6"], - ["bfr4", "bfr7"], - ["bfr5", "bfr8"], - ["bfr7", "bfr8"], - ["bfr9", "bfr8"], + ["bfl7", "bfl10"], + ["bfl5", "bfl6"], + ["bfl7", "bfl6"], + ["bfl5", "bfl8"], + ["bfl6", "bfl9"], + ["bfl8", "bfl9"], + ["bfl10", "bfl9"], + ["bfr7", "bfr10"], + ["bfr6", "bfr5"], + ["bfr6", "bfr7"], + ["bfr5", "bfr8"], + ["bfr6", "bfr9"], + ["bfr8", "bfr9"], + ["bfr10", "bfr9"], // Crossing beams {"deformLimitExpansion" : ""}, // Front - ["bfl0", "bfl3"], - ["bfl3", "bfl5"], - ["bfl6", "bfl2"], - ["bfr3", "bfr5"], - ["bfr6", "bfr2"], - ["bfl1", "bfl2"], - ["bfr2", "bfr1"], - ["bfr3", "bfr0"], + ["bfl1", "bfl4"], + ["bfl4", "bfl6"], + ["bfl7", "bfl3"], + ["bfr4", "bfr6"], + ["bfr7", "bfr3"], + ["bfl2", "bfl3"], + ["bfr3", "bfr2"], + ["bfr4", "bfr1"], // Rear - ["bfl5", "bfl9"], - ["bfl6", "bfl8"], - ["bfl4", "bfl8"], - ["bfl5", "bfl7"], - ["bfr5", "bfr9"], - ["bfr6", "bfr8"], - ["bfr4", "bfr8"], - ["bfr5", "bfr7"], + ["bfl6", "bfl10"], + ["bfl7", "bfl9"], + ["bfl5", "bfl9"], + ["bfl6", "bfl8"], + ["bfr6", "bfr10"], + ["bfr7", "bfr9"], + ["bfr5", "bfr9"], + ["bfr6", "bfr8"], // Support beams - ["bfl0", "bfsl"], - ["bfl5", "bfsl"], - ["bfl4", "bfsl"], - ["bfl2", "bfsl"], - ["bfl0", "bfsl"], - ["bfl1", "bfsl"], - ["bfl3", "bfsl"], - ["bfl6", "bfsl"], - ["bfl7", "bfsl"], - ["bfl8", "bfsl"], - ["bfl9", "bfsl"], - ["bfr0", "bfsr"], - ["bfr5", "bfsr"], - ["bfr4", "bfsr"], - ["bfr2", "bfsr"], - ["bfr0", "bfsr"], - ["bfr1", "bfsr"], - ["bfr3", "bfsr"], - ["bfr6", "bfsr"], - ["bfr7", "bfsr"], - ["bfr8", "bfsr"], - ["bfr9", "bfsr"], + ["bfl0", "bfsl"], + ["bfl6", "bfsl"], + ["bfl5", "bfsl"], + ["bfl3", "bfsl"], + ["bfl1", "bfsl"], + ["bfl2", "bfsl"], + ["bfl4", "bfsl"], + ["bfl7", "bfsl"], + ["bfl8", "bfsl"], + ["bfl9", "bfsl"], + ["bfl10", "bfsl"], + ["bfr0", "bfsr"], + ["bfr6", "bfsr"], + ["bfr5", "bfsr"], + ["bfr3", "bfsr"], + ["bfr1", "bfsr"], + ["bfr2", "bfsr"], + ["bfr4", "bfsr"], + ["bfr7", "bfsr"], + ["bfr8", "bfsr"], + ["bfr9", "bfsr"], + ["bfr10", "bfsr"], // Front rigid {"beamSpring" : 350000, "beamDamp" : 115}, {"beamDeform" : 900}, // Left side - ["bfl2", "bfl8"], - ["bfl3", "bfl9"], + ["bfl3", "bfl9"], + ["bfl4", "bfl10"], // Right side - ["bfr2", "bfr8"], - ["bfr3", "bfr9"], + ["bfr3", "bfr9"], + ["bfr4", "bfr10"], // Attachment beams {"beamType" : "|NORMAL"}, @@ -179,36 +177,36 @@ // Front {"breakGroup" : "fender_l"}, {"breakGroupType" : 1}, - ["bfl0", "fr17"], - ["bfl0", "fr9"], - ["bfl1", "fr17"], - ["bfl1", "fr9"], - ["bfl0", "fr17"], - ["bfl0", "fr9"], + ["bfl1", "fr17"], + ["bfl1", "fr9"], + ["bfl2", "fr17"], + ["bfl2", "fr9"], + ["bfl0", "fr17"], + ["bfl0", "fr9"], // Middle {"beamDeform" : 18000}, {"beamStrength" : 4000}, {"breakGroupType" : 1}, - ["bfl2", "fr17"], - ["bfl3", "fr17"], - ["bfl3", "fr9"], + ["bfl3", "fr17"], + ["bfl4", "fr17"], + ["bfl4", "fr9"], {"breakGroupType" : 0}, // Rear {"beamStrength" : 16000, "beamDeform" : 12000}, - ["bfl4", "fr18"], - ["bfl4", "fr27"], - ["bfl5", "fr17"], - ["bfl5", "fr21"], - ["bfl6", "fr17"], - ["bfl6", "fr29"], - ["bfl7", "fr18"], - ["bfl7", "fr27"], - ["bfl8", "fr28"], - ["bfl8", "fr21"], - ["bfl9", "fr17"], - ["bfl9", "fr29"], + ["bfl5", "fr18"], + ["bfl5", "fr27"], + ["bfl6", "fr17"], + ["bfl6", "fr21"], + ["bfl7", "fr17"], + ["bfl7", "fr29"], + ["bfl8", "fr18"], + ["bfl8", "fr27"], + ["bfl9", "fr28"], + ["bfl9", "fr21"], + ["bfl10", "fr17"], + ["bfl10", "fr29"], // Right side @@ -216,86 +214,86 @@ {"beamStrength" : 20000, "beamDeform" : 1000}, {"breakGroup" : "fender_r"}, {"breakGroupType" : 1}, - ["bfl0", "rl_f8"], - ["bfl0", "rl_f6"], - ["bfl1", "rl_f6"], - ["bfl0", "rl_f6"], - ["bfr0", "rl_f9"], - ["bfr0", "rl_f7"], - ["bfr1", "rl_f7"], - ["bfr0", "rl_f7"], + ["bfl1", "rl_f8"], + ["bfl1", "rl_f6"], + ["bfl2", "rl_f6"], + ["bfl0", "rl_f6"], + ["bfr1", "rl_f9"], + ["bfr1", "rl_f7"], + ["bfr2", "rl_f7"], + ["bfr0", "rl_f7"], // Middle {"beamDeform" : 18000}, {"beamStrength" : 4000}, {"breakGroupType" : 1}, - ["bfl2", "rl_f10"], - ["bfl2", "rl_f11"], - ["bfl3", "rl_f11"], - ["bfr2", "rl_f12"], - ["bfr2", "rl_f13"], - ["bfr3", "rl_f13"], + ["bfl3", "rl_f10"], + ["bfl3", "rl_f11"], + ["bfl4", "rl_f11"], + ["bfr3", "rl_f12"], + ["bfr3", "rl_f13"], + ["bfr4", "rl_f13"], {"breakGroupType" : 0}, // Rear {"beamStrength" : 16000, "beamDeform" : 12000}, - ["bfl4", "rl_f15"], - ["bfl4", "rl_f16"], - ["bfl5", "rl_f15"], - ["bfl5", "rl_f16"], - ["bfl7", "rl_f15"], - ["bfl7", "rl_f16"], - ["bfl8", "rl_f15"], - ["bfl8", "rl_f16"], - ["bfl6", "rl_f16"], - ["bfl9", "rl_f16"], - ["bfr4", "rl_f17"], - ["bfr4", "rl_f18"], - ["bfr5", "rl_f17"], - ["bfr5", "rl_f18"], - ["bfr7", "rl_f17"], - ["bfr7", "rl_f18"], - ["bfr8", "rl_f17"], - ["bfr8", "rl_f18"], - ["bfr6", "rl_f18"], - ["bfr9", "rl_f18"], + ["bfl5", "rl_f15"], + ["bfl5", "rl_f16"], + ["bfl6", "rl_f15"], + ["bfl6", "rl_f16"], + ["bfl8", "rl_f15"], + ["bfl8", "rl_f16"], + ["bfl9", "rl_f15"], + ["bfl9", "rl_f16"], + ["bfl7", "rl_f16"], + ["bfl10", "rl_f16"], + ["bfr5", "rl_f17"], + ["bfr5", "rl_f18"], + ["bfr6", "rl_f17"], + ["bfr6", "rl_f18"], + ["bfr8", "rl_f17"], + ["bfr8", "rl_f18"], + ["bfr9", "rl_f17"], + ["bfr9", "rl_f18"], + ["bfr7", "rl_f18"], + ["bfr10", "rl_f18"], {"beamStrength" : 16000, "beamDeform" : "FLT_MAX"}, // Body // Left side {"breakGroup" : "fender_l"}, - ["bfl7", "mbl0"], - ["bfl8", "mbl1"], - ["bfl9", "mbl2"], + ["bfl8", "mbl0"], + ["bfl9", "mbl1"], + ["bfl10", "mbl2"], // Right side {"breakGroup" : "fender_r"}, - ["bfr7", "mbr0"], - ["bfr8", "mbr1"], - ["bfr9", "mbr2"], + ["bfr8", "mbr0"], + ["bfr9", "mbr1"], + ["bfr10", "mbr2"], {"breakGroup" : "", "breakGroupType" : "", "beamType" : "|NORMAL"}, ], "triangles" : [ - ["id1:", "id2:", "id3:"], - ["bfl2", "bfl3", "bfl1"], - ["bfl0", "bfl2", "bfl1"], - ["bfl3", "bfl0", "bfl1"], - ["bfl3", "bfl2", "bfl6"], - ["bfl5", "bfl6", "bfl2"], - ["bfl9", "bfl6", "bfl5"], - ["bfl8", "bfl9", "bfl5"], - ["bfl4", "bfl7", "bfl5"], - ["bfl8", "bfl5", "bfl7"], - ["bfr3", "bfr1", "bfr0"], - ["bfr2", "bfr1", "bfr3"], - ["bfr0", "bfr1", "bfr2"], - ["bfr5", "bfr2", "bfr6"], - ["bfr3", "bfr6", "bfr2"], - ["bfr9", "bfr5", "bfr6"], - ["bfr8", "bfr5", "bfr9"], - ["bfr8", "bfr7", "bfr5"], - ["bfr4", "bfr5", "bfr7"], + ["id1:", "id2:", "id3:"], + ["bfl3", "bfl4", "bfl2"], + ["bfl1", "bfl3", "bfl2"], + ["bfl4", "bfl0", "bfl2"], + ["bfl4", "bfl3", "bfl7"], + ["bfl6", "bfl7", "bfl3"], + ["bfl10", "bfl7", "bfl6"], + ["bfl9", "bfl10", "bfl6"], + ["bfl5", "bfl8", "bfl6"], + ["bfl9", "bfl6", "bfl8"], + ["bfr4", "bfr2", "bfr0"], + ["bfr3", "bfr2", "bfr4"], + ["bfr1", "bfr2", "bfr3"], + ["bfr6", "bfr3", "bfr7"], + ["bfr4", "bfr7", "bfr3"], + ["bfr10", "bfr6", "bfr7"], + ["bfr9", "bfr6", "bfr10"], + ["bfr9", "bfr8", "bfr6"], + ["bfr5", "bfr6", "bfr8"], ], "flexbodies" : [ ["mesh", "[group]:", "nonFlexMaterials"], diff --git a/examples/transformed_jbeam/fender-cfg-example.jbeam b/examples/transformed_jbeam/fender-cfg-example.jbeam index 57e71067..42840269 100644 --- a/examples/transformed_jbeam/fender-cfg-example.jbeam +++ b/examples/transformed_jbeam/fender-cfg-example.jbeam @@ -14,53 +14,51 @@ }, "slotType" : "cot_fender", "nodes" : [ - ["id", "posX", "posY", "posZ"], + ["id", "posX", "posY", "posZ"], {"frictionCoef" : 0.7}, {"nodeMaterial" : "|NM_METAL"}, + + // Left side {"collision" : true}, {"group" : "cot_fender_l"}, {"nodeWeight" : 0.65}, {"selfCollision" : true}, - ["bfl0", 0.739, -1.845, 0.716], - ["bfl1", 0.855, -1.788, 0.707], - ["bfl2", 0.948, -1.435, 0.730], - ["bfl3", 0.756, -1.413, 0.843], - ["bfl4", 0.963, -1.024, 0.112], - ["bfl5", 0.964, -1.072, 0.507], - ["bfl6", 0.778, -1.008, 0.873], - ["bfl7", 0.987, -0.743, 0.109], - ["bfl8", 0.987, -0.744, 0.494], - ["bfl9", 0.812, -0.759, 0.896], - ["bfr0", -0.691, -1.829, 0.716], - ["bfr1", -0.807, -1.769, 0.707], - ["bfr2", -0.890, -1.409, 0.729], - ["bfr3", -0.700, -1.397, 0.843], - ["bfr4", -0.899, -1.005, 0.112], - ["bfr5", -0.900, -1.053, 0.508], - ["bfr6", -0.715, -0.991, 0.873], - ["bfr7", -0.916, -0.742, 0.112], - ["bfr8", -0.917, -0.746, 0.494], - ["bfr9", -0.734, -0.746, 0.888], + ["bfl0", 0.739, -1.845, 0.716], + ["bfl1", 0.959, -1.762, 0.576], + ["bfl2", 0.855, -1.788, 0.707], + ["bfl3", 0.948, -1.435, 0.730], + ["bfl4", 0.756, -1.413, 0.843], + ["bfl5", 0.963, -1.024, 0.112], + ["bfl6", 0.964, -1.072, 0.507], + ["bfl7", 0.778, -1.008, 0.873], + ["bfl8", 0.987, -0.743, 0.109], + ["bfl9", 0.987, -0.744, 0.494], + ["bfl10", 0.812, -0.759, 0.896], + + // Right side + {"group" : "cot_fender_r"}, + ["bfr0", -0.691, -1.829, 0.716], + ["bfr1", -0.906, -1.737, 0.578], + ["bfr2", -0.807, -1.769, 0.707], + ["bfr3", -0.890, -1.409, 0.729], + ["bfr4", -0.700, -1.397, 0.843], + ["bfr5", -0.899, -1.005, 0.112], + ["bfr6", -0.900, -1.053, 0.508], + ["bfr7", -0.715, -0.991, 0.873], + ["bfr8", -0.916, -0.742, 0.112], + ["bfr9", -0.917, -0.746, 0.494], + ["bfr10", -0.734, -0.746, 0.888], // Support nodes {"collision" : false}, {"group" : ""}, {"nodeWeight" : 1.2}, {"selfCollision" : false}, - ["bfsl", 0.684, -1.079, 0.507], - ["bfsr", -0.623, -1.064, 0.507], - - // Support nodes - {"group" : "cot_fender_r"}, - ["bfr_sr", -0.906, -1.737, 0.578], - {"collision" : true}, - {"group" : "cot_fender_l"}, - {"nodeWeight" : 0.65}, - {"selfCollision" : true}, - ["bfl_sl", 0.959, -1.762, 0.576], + ["bfsl", 0.684, -1.079, 0.507], + ["bfsr", -0.623, -1.064, 0.507], ], "beams" : [ - ["id1:", "id2:"], + ["id1:", "id2:"], // Structural beams // beamSpring: 451000, beamDamp: 50 @@ -72,100 +70,100 @@ {"beamDeform" : 6000}, // Front - ["bfr0", "bfr3"], - ["bfr0", "bfr1"], - ["bfr1", "bfr3"], - ["bfl3", "bfl0"], - ["bfl0", "bfl1"], - ["bfl0", "bfl1"], - ["bfl3", "bfl2"], - ["bfl0", "bfl2"], - ["bfr1", "bfr0"], - ["bfr3", "bfr2"], - ["bfr0", "bfr2"], - ["bfl3", "bfl1"], + ["bfr0", "bfr4"], + ["bfr0", "bfr2"], + ["bfr2", "bfr4"], + ["bfl4", "bfl0"], + ["bfl0", "bfl2"], + ["bfl1", "bfl2"], + ["bfl4", "bfl3"], + ["bfl1", "bfl3"], + ["bfr2", "bfr1"], + ["bfr4", "bfr3"], + ["bfr1", "bfr3"], + ["bfl4", "bfl2"], // Middle {"beamDeform" : 12000}, - ["bfl5", "bfl2"], - ["bfr5", "bfr2"], - ["bfl3", "bfl6"], - ["bfr3", "bfr6"], + ["bfl6", "bfl3"], + ["bfr6", "bfr3"], + ["bfl4", "bfl7"], + ["bfr4", "bfr7"], // Rear - ["bfl6", "bfl9"], - ["bfl4", "bfl5"], - ["bfl6", "bfl5"], - ["bfl4", "bfl7"], - ["bfl5", "bfl8"], - ["bfl7", "bfl8"], - ["bfl9", "bfl8"], - ["bfr6", "bfr9"], - ["bfr5", "bfr4"], - ["bfr5", "bfr6"], - ["bfr4", "bfr7"], - ["bfr5", "bfr8"], - ["bfr7", "bfr8"], - ["bfr9", "bfr8"], + ["bfl7", "bfl10"], + ["bfl5", "bfl6"], + ["bfl7", "bfl6"], + ["bfl5", "bfl8"], + ["bfl6", "bfl9"], + ["bfl8", "bfl9"], + ["bfl10", "bfl9"], + ["bfr7", "bfr10"], + ["bfr6", "bfr5"], + ["bfr6", "bfr7"], + ["bfr5", "bfr8"], + ["bfr6", "bfr9"], + ["bfr8", "bfr9"], + ["bfr10", "bfr9"], // Crossing beams {"deformLimitExpansion" : ""}, // Front - ["bfl0", "bfl3"], - ["bfl3", "bfl5"], - ["bfl6", "bfl2"], - ["bfr3", "bfr5"], - ["bfr6", "bfr2"], - ["bfl1", "bfl2"], - ["bfr2", "bfr1"], - ["bfr3", "bfr0"], + ["bfl1", "bfl4"], + ["bfl4", "bfl6"], + ["bfl7", "bfl3"], + ["bfr4", "bfr6"], + ["bfr7", "bfr3"], + ["bfl2", "bfl3"], + ["bfr3", "bfr2"], + ["bfr4", "bfr1"], // Rear - ["bfl5", "bfl9"], - ["bfl6", "bfl8"], - ["bfl4", "bfl8"], - ["bfl5", "bfl7"], - ["bfr5", "bfr9"], - ["bfr6", "bfr8"], - ["bfr4", "bfr8"], - ["bfr5", "bfr7"], + ["bfl6", "bfl10"], + ["bfl7", "bfl9"], + ["bfl5", "bfl9"], + ["bfl6", "bfl8"], + ["bfr6", "bfr10"], + ["bfr7", "bfr9"], + ["bfr5", "bfr9"], + ["bfr6", "bfr8"], // Support beams - ["bfl0", "bfsl"], - ["bfl5", "bfsl"], - ["bfl4", "bfsl"], - ["bfl2", "bfsl"], - ["bfl0", "bfsl"], - ["bfl1", "bfsl"], - ["bfl3", "bfsl"], - ["bfl6", "bfsl"], - ["bfl7", "bfsl"], - ["bfl8", "bfsl"], - ["bfl9", "bfsl"], - ["bfr0", "bfsr"], - ["bfr5", "bfsr"], - ["bfr4", "bfsr"], - ["bfr2", "bfsr"], - ["bfr0", "bfsr"], - ["bfr1", "bfsr"], - ["bfr3", "bfsr"], - ["bfr6", "bfsr"], - ["bfr7", "bfsr"], - ["bfr8", "bfsr"], - ["bfr9", "bfsr"], + ["bfl0", "bfsl"], + ["bfl6", "bfsl"], + ["bfl5", "bfsl"], + ["bfl3", "bfsl"], + ["bfl1", "bfsl"], + ["bfl2", "bfsl"], + ["bfl4", "bfsl"], + ["bfl7", "bfsl"], + ["bfl8", "bfsl"], + ["bfl9", "bfsl"], + ["bfl10", "bfsl"], + ["bfr0", "bfsr"], + ["bfr6", "bfsr"], + ["bfr5", "bfsr"], + ["bfr3", "bfsr"], + ["bfr1", "bfsr"], + ["bfr2", "bfsr"], + ["bfr4", "bfsr"], + ["bfr7", "bfsr"], + ["bfr8", "bfsr"], + ["bfr9", "bfsr"], + ["bfr10", "bfsr"], // Front rigid {"beamSpring" : 350000, "beamDamp" : 115}, {"beamDeform" : 900}, // Left side - ["bfl2", "bfl8"], - ["bfl3", "bfl9"], + ["bfl3", "bfl9"], + ["bfl4", "bfl10"], // Right side - ["bfr2", "bfr8"], - ["bfr3", "bfr9"], + ["bfr3", "bfr9"], + ["bfr4", "bfr10"], // Attachment beams {"beamType" : "|NORMAL"}, @@ -179,36 +177,36 @@ // Front {"breakGroup" : "fender_l"}, {"breakGroupType" : 1}, - ["bfl0", "fr17"], - ["bfl0", "fr9"], - ["bfl1", "fr17"], - ["bfl1", "fr9"], - ["bfl0", "fr17"], - ["bfl0", "fr9"], + ["bfl1", "fr17"], + ["bfl1", "fr9"], + ["bfl2", "fr17"], + ["bfl2", "fr9"], + ["bfl0", "fr17"], + ["bfl0", "fr9"], // Middle {"beamDeform" : 18000}, {"beamStrength" : 4000}, {"breakGroupType" : 1}, - ["bfl2", "fr17"], - ["bfl3", "fr17"], - ["bfl3", "fr9"], + ["bfl3", "fr17"], + ["bfl4", "fr17"], + ["bfl4", "fr9"], {"breakGroupType" : 0}, // Rear {"beamStrength" : 16000, "beamDeform" : 12000}, - ["bfl4", "fr18"], - ["bfl4", "fr27"], - ["bfl5", "fr17"], - ["bfl5", "fr21"], - ["bfl6", "fr17"], - ["bfl6", "fr29"], - ["bfl7", "fr18"], - ["bfl7", "fr27"], - ["bfl8", "fr28"], - ["bfl8", "fr21"], - ["bfl9", "fr17"], - ["bfl9", "fr29"], + ["bfl5", "fr18"], + ["bfl5", "fr27"], + ["bfl6", "fr17"], + ["bfl6", "fr21"], + ["bfl7", "fr17"], + ["bfl7", "fr29"], + ["bfl8", "fr18"], + ["bfl8", "fr27"], + ["bfl9", "fr28"], + ["bfl9", "fr21"], + ["bfl10", "fr17"], + ["bfl10", "fr29"], // Right side @@ -216,86 +214,86 @@ {"beamStrength" : 20000, "beamDeform" : 1000}, {"breakGroup" : "fender_r"}, {"breakGroupType" : 1}, - ["bfl0", "rl_f8"], - ["bfl0", "rl_f6"], - ["bfl1", "rl_f6"], - ["bfl0", "rl_f6"], - ["bfr0", "rl_f9"], - ["bfr0", "rl_f7"], - ["bfr1", "rl_f7"], - ["bfr0", "rl_f7"], + ["bfl1", "rl_f8"], + ["bfl1", "rl_f6"], + ["bfl2", "rl_f6"], + ["bfl0", "rl_f6"], + ["bfr1", "rl_f9"], + ["bfr1", "rl_f7"], + ["bfr2", "rl_f7"], + ["bfr0", "rl_f7"], // Middle {"beamDeform" : 18000}, {"beamStrength" : 4000}, {"breakGroupType" : 1}, - ["bfl2", "rl_f10"], - ["bfl2", "rl_f11"], - ["bfl3", "rl_f11"], - ["bfr2", "rl_f12"], - ["bfr2", "rl_f13"], - ["bfr3", "rl_f13"], + ["bfl3", "rl_f10"], + ["bfl3", "rl_f11"], + ["bfl4", "rl_f11"], + ["bfr3", "rl_f12"], + ["bfr3", "rl_f13"], + ["bfr4", "rl_f13"], {"breakGroupType" : 0}, // Rear {"beamStrength" : 16000, "beamDeform" : 12000}, - ["bfl4", "rl_f15"], - ["bfl4", "rl_f16"], - ["bfl5", "rl_f15"], - ["bfl5", "rl_f16"], - ["bfl7", "rl_f15"], - ["bfl7", "rl_f16"], - ["bfl8", "rl_f15"], - ["bfl8", "rl_f16"], - ["bfl6", "rl_f16"], - ["bfl9", "rl_f16"], - ["bfr4", "rl_f17"], - ["bfr4", "rl_f18"], - ["bfr5", "rl_f17"], - ["bfr5", "rl_f18"], - ["bfr7", "rl_f17"], - ["bfr7", "rl_f18"], - ["bfr8", "rl_f17"], - ["bfr8", "rl_f18"], - ["bfr6", "rl_f18"], - ["bfr9", "rl_f18"], + ["bfl5", "rl_f15"], + ["bfl5", "rl_f16"], + ["bfl6", "rl_f15"], + ["bfl6", "rl_f16"], + ["bfl8", "rl_f15"], + ["bfl8", "rl_f16"], + ["bfl9", "rl_f15"], + ["bfl9", "rl_f16"], + ["bfl7", "rl_f16"], + ["bfl10", "rl_f16"], + ["bfr5", "rl_f17"], + ["bfr5", "rl_f18"], + ["bfr6", "rl_f17"], + ["bfr6", "rl_f18"], + ["bfr8", "rl_f17"], + ["bfr8", "rl_f18"], + ["bfr9", "rl_f17"], + ["bfr9", "rl_f18"], + ["bfr7", "rl_f18"], + ["bfr10", "rl_f18"], {"beamStrength" : 16000, "beamDeform" : "FLT_MAX"}, // Body // Left side {"breakGroup" : "fender_l"}, - ["bfl7", "mbl0"], - ["bfl8", "mbl1"], - ["bfl9", "mbl2"], + ["bfl8", "mbl0"], + ["bfl9", "mbl1"], + ["bfl10", "mbl2"], // Right side {"breakGroup" : "fender_r"}, - ["bfr7", "mbr0"], - ["bfr8", "mbr1"], - ["bfr9", "mbr2"], + ["bfr8", "mbr0"], + ["bfr9", "mbr1"], + ["bfr10", "mbr2"], {"breakGroup" : "", "breakGroupType" : "", "beamType" : "|NORMAL"}, ], "triangles" : [ - ["id1:", "id2:", "id3:"], - ["bfl2", "bfl3", "bfl1"], - ["bfl0", "bfl2", "bfl1"], - ["bfl3", "bfl0", "bfl1"], - ["bfl3", "bfl2", "bfl6"], - ["bfl5", "bfl6", "bfl2"], - ["bfl9", "bfl6", "bfl5"], - ["bfl8", "bfl9", "bfl5"], - ["bfl4", "bfl7", "bfl5"], - ["bfl8", "bfl5", "bfl7"], - ["bfr3", "bfr1", "bfr0"], - ["bfr2", "bfr1", "bfr3"], - ["bfr0", "bfr1", "bfr2"], - ["bfr5", "bfr2", "bfr6"], - ["bfr3", "bfr6", "bfr2"], - ["bfr9", "bfr5", "bfr6"], - ["bfr8", "bfr5", "bfr9"], - ["bfr8", "bfr7", "bfr5"], - ["bfr4", "bfr5", "bfr7"], + ["id1:", "id2:", "id3:"], + ["bfl3", "bfl4", "bfl2"], + ["bfl1", "bfl3", "bfl2"], + ["bfl4", "bfl0", "bfl2"], + ["bfl4", "bfl3", "bfl7"], + ["bfl6", "bfl7", "bfl3"], + ["bfl10", "bfl7", "bfl6"], + ["bfl9", "bfl10", "bfl6"], + ["bfl5", "bfl8", "bfl6"], + ["bfl9", "bfl6", "bfl8"], + ["bfr4", "bfr2", "bfr0"], + ["bfr3", "bfr2", "bfr4"], + ["bfr1", "bfr2", "bfr3"], + ["bfr6", "bfr3", "bfr7"], + ["bfr4", "bfr7", "bfr3"], + ["bfr10", "bfr6", "bfr7"], + ["bfr9", "bfr6", "bfr10"], + ["bfr9", "bfr8", "bfr6"], + ["bfr5", "bfr6", "bfr8"], ], "flexbodies" : [ ["mesh", "[group]:", "nonFlexMaterials"], diff --git a/src-extra/transformation/JbeamEdit/Transformation.hs b/src-extra/transformation/JbeamEdit/Transformation.hs index 34681f0e..c1357484 100644 --- a/src-extra/transformation/JbeamEdit/Transformation.hs +++ b/src-extra/transformation/JbeamEdit/Transformation.hs @@ -110,6 +110,19 @@ groupByPrefix origTree = . NE.map (prefixForVertexKey origTree) . NE.groupWith1 (dropIndex . vName . aVertex) +{- | Like 'groupByPrefix', but support vertices always share a single +SupportKey bucket instead of being split by name prefix. +-} +groupForType + :: VertexTreeType + -> Maybe (OMap1 VertexTreeKey VertexTree) + -> NonEmpty AnnotatedVertex + -> OMap1 VertexTreeKey VertexTree +groupForType SupportTree origTree vs = + let topComments = concatMap tComments (OMap1.lookup SupportKey =<< origTree) + in OMap1.singleton (SupportKey, VertexTree topComments vs) +groupForType _ origTree vs = groupByPrefix origTree vs + commentsExists :: Maybe (OMap1 VertexTreeKey VertexTree) -> Bool commentsExists = any (notNull . tComments . OMap1.head) @@ -129,7 +142,7 @@ addVertexTreeToForest newNames tf grouped forest forestAcc t = addSideComment t (commentsExists origTree) . addPrefixComments t . fmap (sortVertices t newNames tf) - $ groupByPrefix origTree groupsForT + $ groupForType t origTree groupsForT in Right (M.insertWith mergeOMap1Trees t tree forestAcc) Nothing -> Right forestAcc @@ -140,7 +153,8 @@ mergeOMap1Trees mergeOMap1Trees new existing = foldl' go existing (OMap1.assocs new) where - merge (VertexTree _ newVerts) (VertexTree ec ev) = VertexTree ec (ev <> newVerts) + merge (VertexTree nc newVerts) (VertexTree ec ev) = + VertexTree (bool nc ec (notNull ec)) (ev <> newVerts) go acc (k, v) = OMap1.insertWith merge k v acc groupAnnotatedVertices @@ -149,15 +163,6 @@ groupAnnotatedVertices -> Either Text (VertexTreeType, [AnnotatedVertex]) groupAnnotatedVertices brks g = (,[g]) <$> determineGroup' brks (aVertex g) -updateSupportVertexName - :: VertexTreeType - -> AnnotatedVertex - -> AnnotatedVertex -updateSupportVertexName vType (AnnotatedVertex c v m) = AnnotatedVertex c (v {vName = newName}) m - where - name = vName v - newName = dropIndex name <> prefixForType vType - moveSupportVertices :: UpdateNamesMap -> TransformationConfig @@ -165,10 +170,10 @@ moveSupportVertices -> M.Map VertexTreeType [AnnotatedVertex] -> (VertexForest, M.Map VertexTreeType [AnnotatedVertex]) moveSupportVertices newNames tfCfg connMap vsPerType = - let supportVertices :: [(VertexTreeType, AnnotatedVertex)] + let supportVertices :: [AnnotatedVertex] supportVertices = - [ (vType, av) - | (vType, vs) <- M.toList vsPerType + [ av + | vs <- M.elems vsPerType , av <- vs , let name = vName (aVertex av) , let vertexCount = length vs @@ -182,6 +187,7 @@ moveSupportVertices newNames tfCfg connMap vsPerType = thr = ySortingThreshold tfCfg assignSupportNames = assignNames newNames brks SupportTree + supportKey = vertexPrefix newNames brks SupportTree vertexForest :: VertexForest vertexForest = @@ -195,16 +201,13 @@ moveSupportVertices newNames tfCfg connMap vsPerType = , VertexTree [sideComment SupportTree] ( snd - . mapAccumL - assignSupportNames - M.empty - . NE.sortBy (compareAV thr SupportTree) - $ NE.map (uncurry updateSupportVertexName) vs + . mapAccumL assignSupportNames M.empty + $ NE.sortBy (compareAV supportKey thr SupportTree) vs ) ) ) - supportVertexNames = foldr (S.insert . anVertexName . snd) S.empty supportVertices + supportVertexNames = foldr (S.insert . anVertexName) S.empty supportVertices remainingVertices :: M.Map VertexTreeType [AnnotatedVertex] remainingVertices = @@ -331,12 +334,17 @@ treesOrder :: [VertexTreeType] treesOrder = [LeftTree, MiddleTree, RightTree, SupportTree] compareAV - :: Scientific -> VertexTreeType -> AnnotatedVertex -> AnnotatedVertex -> Ordering -compareAV thr treeType vertex1 vertex2 = + :: (Vertex -> Text) + -> Scientific + -> VertexTreeType + -> AnnotatedVertex + -> AnnotatedVertex + -> Ordering +compareAV supportKey thr treeType vertex1 vertex2 = let supportNameCompare = bool EQ - (on compare (dropIndex . vName . aVertex) vertex1 vertex2) + (on compare (supportKey . aVertex) vertex1 vertex2) (treeType == SupportTree) y1 = vY . aVertex $ vertex1 y2 = vY . aVertex $ vertex2 @@ -354,9 +362,55 @@ compareAV thr treeType vertex1 vertex2 = ] renameVertexId :: VertexTreeType -> Int -> Text -> Text -renameVertexId treeType idx vertexPrefix = +renameVertexId treeType idx prefix = let idx' = mwhen (treeType /= SupportTree || idx /= 0) (intToText idx) - in vertexPrefix <> idx' + in prefix <> idx' + +sideLetters :: String +sideLetters = ['l', 'm', 'r'] + +{- | Strip the @s@ and side letter a previous run appended. The side letter only +comes off when it is the one about to be put back, so @rl_r@ keeps its rear @r@ +while @bfl@ loses the @l@ that would otherwise be doubled. +-} +dropSupportSuffix :: Text -> Text -> Text +dropSupportSuffix sideLetter prefix = + bool + withoutSide + (T.init withoutSide) + (T.length withoutSide >= 2 && T.last withoutSide == 's') + where + withoutSide = + bool + prefix + (T.init prefix) + (T.length prefix > 2 && T.takeEnd 1 prefix == sideLetter) + +{- | The name a vertex gets, before the running index is appended. Support +prefixes are a fixed point of this, which is what lets it double as a sort +key that survives a transform. +-} +vertexPrefix + :: UpdateNamesMap -> XGroupBreakpoints -> VertexTreeType -> Vertex -> Text +vertexPrefix newNames brks treeType v + | treeType == SupportTree = + updatedPrefix (dropSupportSuffix typeSpecific prefix) + <> T.singleton 's' + <> typeSpecific + | T.length prefix >= 3 + && T.last prefix' == 's' = + updatedPrefix (T.init prefix') <> typeSpecific + | T.length prefix >= 3 + && isLmr = + updatedPrefix prefix' <> typeSpecific + | otherwise = + updatedPrefix prefix <> typeSpecific + where + updatedPrefix cleanPrefix = M.findWithDefault cleanPrefix cleanPrefix newNames + prefix = dropIndex (vName v) + typeSpecific = either (const "") prefixForType (determineGroup brks v) + (prefix', lastChar) = fromMaybe (error "unreachable") (T.unsnoc prefix) + isLmr = lastChar `elem` sideLetters assignNames :: UpdateNamesMap @@ -367,33 +421,9 @@ assignNames -> (Map Text Int, AnnotatedVertex) assignNames newNames brks treeType prefixMap av = let v = aVertex av - updatedPrefix cleanPrefix' = M.findWithDefault cleanPrefix' cleanPrefix' newNames - prefix = dropIndex (vName v) - typeSpecific = either (const "") prefixForType (determineGroup brks v) - (prefix', lastChar) = fromMaybe (error "unreachable") (T.unsnoc prefix) - isLmr = lastChar `elem` ['l', 'm', 'r'] - supportPrefixChar = T.singleton 's' <> bool typeSpecific (T.singleton lastChar) isLmr - cleanPrefix - | treeType /= SupportTree - && T.length prefix >= 3 - && T.last prefix' == 's' = - updatedPrefix (T.init prefix') <> typeSpecific - | treeType /= SupportTree - && T.length prefix >= 3 - && isLmr = - updatedPrefix prefix' <> typeSpecific - | treeType /= SupportTree = - updatedPrefix prefix <> typeSpecific - | T.length prefix' >= 3 - && T.last prefix' == 's' = - updatedPrefix (T.init prefix') <> T.singleton 's' <> typeSpecific - | T.length prefix' < 2 = - updatedPrefix prefix <> supportPrefixChar - | otherwise = - updatedPrefix prefix' <> supportPrefixChar + cleanPrefix = vertexPrefix newNames brks treeType v lastIdx = M.findWithDefault 0 cleanPrefix prefixMap - newName = renameVertexId treeType lastIdx cleanPrefix - newVertex = v {vName = newName} + newVertex = v {vName = renameVertexId treeType lastIdx cleanPrefix} prefixMap' = M.insert cleanPrefix (lastIdx + 1) prefixMap in (prefixMap', av {aVertex = newVertex}) @@ -406,7 +436,10 @@ sortVertices sortVertices treeType newNames tfCfg (VertexTree comments vertices) = let thr = ySortingThreshold tfCfg brks = xGroupBreakpoints tfCfg - sortedGroups = NE.sortBy (compareAV thr treeType) vertices + sortedGroups = + NE.sortBy + (compareAV (vertexPrefix newNames brks treeType) thr treeType) + vertices renamedGroups = snd $ mapAccumL (assignNames newNames brks treeType) M.empty sortedGroups in VertexTree comments renamedGroups diff --git a/src-extra/transformation/JbeamEdit/Transformation/VertexExtraction.hs b/src-extra/transformation/JbeamEdit/Transformation/VertexExtraction.hs index 38ae1dae..8b7e05de 100644 --- a/src-extra/transformation/JbeamEdit/Transformation/VertexExtraction.hs +++ b/src-extra/transformation/JbeamEdit/Transformation/VertexExtraction.hs @@ -117,13 +117,13 @@ breakVertices vertexPrefix allVertexNames ns = go [] ns allVertexNames Right ( vertexNames , reverse (maybeConsComment [node] assocPriorCmt) - , metaBefore ++ rest + , reverse metaBefore ++ rest ) else Right ( vertexNames , reverse (maybeConsComment currentTree assocPriorCmt) - , metaBefore ++ (node : rest) + , reverse metaBefore ++ (node : rest) ) | otherwise = go (node : acc) rest vertexNames where @@ -148,7 +148,8 @@ insertTreeInMap :: VertexTree -> OMap1 VertexTreeKey VertexTree -> OMap1 VertexTreeKey VertexTree insertTreeInMap (VertexTree newComments newVertexGroups) omap = let vType = getVertexTreePrefix newVertexGroups - merge _ (VertexTree ec ev) = VertexTree ec (ev <> newVertexGroups) + merge (VertexTree nc _) (VertexTree ec ev) = + VertexTree (if null ec then nc else ec) (ev <> newVertexGroups) in OMap1.insertWith merge vType (VertexTree newComments newVertexGroups) omap isSupportVertex :: Vertex -> Bool diff --git a/test-extra/transformation/Spec.hs b/test-extra/transformation/Spec.hs index c3463f55..5e351d6d 100644 --- a/test-extra/transformation/Spec.hs +++ b/test-extra/transformation/Spec.hs @@ -2,11 +2,18 @@ module Spec ( main, ) where -import Data.List (isPrefixOf, isSuffixOf) +import Data.ByteString.Lazy qualified as LBS +import Data.List (isInfixOf, isPrefixOf, isSuffixOf) import Data.Map qualified as M +import Data.Set (Set) import Data.Set qualified as S +import Data.Text (Text) import Data.Text qualified as T -import JbeamEdit.Core.Node (Node) +import Data.Text.Encoding (encodeUtf8) +import Data.Vector qualified as V +import GHC.IsList (fromList) +import JbeamEdit.Core.Node (Node (..), NumberValue (..), expectArray) +import JbeamEdit.Core.NodePath qualified as NP import JbeamEdit.Formatting import JbeamEdit.IOUtils (tryReadFile) import JbeamEdit.Parsing.Jbeam (parseNodes) @@ -45,6 +52,41 @@ topNodeSpec rs cfName tfConfig inFilename outFilename = do Right (formatNode rs node) describe desc . it "works" $ transformAndFormat `shouldBe` Right (T.pack output) +{- | Transforming an already transformed file must produce the same text +again. Unlike 'supportRenameIdempotencySpec' this compares the whole +formatted output, so it also covers comments, metadata and beam references. +-} +fixedPointSpec + :: RuleSet -> String -> TransformationConfig -> FilePath -> Spec +fixedPointSpec rs cfName tfConfig outFilename = do + output <- runIO $ readFile outFilename + let desc = + "with " + ++ cfName + ++ ": transforming " + ++ outFilename + ++ " again should leave it unchanged" + -- Parse the same text the result is compared against, rather than + -- reading the file a second time, and drop the carriage returns a + -- Windows checkout leaves behind. formatNode always emits LF, so + -- the line endings the file happens to carry are not part of what + -- this spec is asserting. + expected = T.replace "\r\n" "\n" (T.pack output) + check = + case parseNodes (LBS.fromStrict (encodeUtf8 expected)) of + Left err -> + expectationFailure + ("failed to parse " ++ outFilename ++ ": " ++ T.unpack err) + Right node -> + case transform M.empty tfConfig node of + Left err -> expectationFailure ("transform failed: " ++ T.unpack err) + Right (_, _, _, again) -> formatNode rs again `shouldBe` expected + describe desc . it "works" $ + if "suspension" `isInfixOf` outFilename + then + pendingWith "metadata is not carried across vertex tree boundaries, issue #221" + else check + parseJbeamFile :: FilePath -> IO Node parseJbeamFile path = do let osPath = unsafeEncodeUtf path @@ -85,6 +127,99 @@ beamValidationSpec = do it "has no duplicate beams" $ findDuplicateBeams internalBeams `shouldBe` [] +nodesQuery :: NP.NodePath +nodesQuery = fromList [NP.ObjectIndex 0, NP.ObjectKey "nodes"] + +{- | (name, Y position) for every vertex in a top node's "nodes" section, +in file order. +-} +vertexPositionsInOrder :: Node -> [(Text, Double)] +vertexPositionsInOrder topNode = + case NP.queryNodes nodesQuery topNode >>= NP.expectArray nodesQuery of + Left _ -> [] + Right rows -> + [ (name, realToFrac (nvValue yNum)) + | row <- V.toList rows + , Just inner <- [expectArray row] + , Just (String name) <- [inner V.!? 0] + , name /= "id" + , Just (Number yNum) <- [inner V.!? 2] + ] + +{- | Every vertex coordinate in a top node's "nodes" section. Positions +survive renaming, so they identify a vertex across a transform. +-} +vertexCoordinates :: Node -> Set (Double, Double, Double) +vertexCoordinates topNode = + case NP.queryNodes nodesQuery topNode >>= NP.expectArray nodesQuery of + Left _ -> S.empty + Right rows -> + S.fromList + [ (realToFrac (nvValue x), realToFrac (nvValue y), realToFrac (nvValue z)) + | row <- V.toList rows + , Just inner <- [expectArray 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] + ] + +{- | Names ending in a letter rather than a digit all map to the same +SupportKey, so an insert that replaced instead of merged used to drop +every group but the last. +-} +letterEndingNodesFixture :: FilePath +letterEndingNodesFixture = + "examples/regression_jbeam/letter-ending-nodes-repro.jbeam" + +letterEndingNodesSpec :: Spec +letterEndingNodesSpec = + describe "letter-ending node names" + . it "keeps every vertex through a transform" + $ do + topNode <- parseJbeamFile letterEndingNodesFixture + let expected = vertexCoordinates topNode + expected `shouldNotBe` S.empty + case transform M.empty newTransformationConfig topNode of + Left err -> expectationFailure ("transform failed: " ++ T.unpack err) + Right (_, _, _, resultNode) -> + vertexCoordinates resultNode `shouldBe` expected + +{- | Three small hubs (nl0, nl10, nl20; front/mid/rear), each beamed to +three of its own ordinary leaf nodes (see issue #215). At +support-threshold 20 with 12 nodes in the group, thrCount = +round(0.2*12) = 2: each hub (3 connections) clears it and becomes a +support vertex, each leaf (1 connection) doesn't. This mirrors the +support-hub shape seen in real body files (three support nodes sharing +one prefix group) at a size small enough to reason about by hand: after +the first transform, the mid/rear hubs get a trailing index (e.g. nlsl1, +nlsl2) while the front one doesn't (nlsl), and that index is exactly +what trips up the second pass. Y positions are spaced well outside the +(default 0.05) y-sorting-threshold so this test stays isolated from the +separate y-sorting-threshold banding bug (issue #214). +-} +supportRenameIdempotencyFixture :: FilePath +supportRenameIdempotencyFixture = + "examples/regression_jbeam/support-rename-idempotency-repro.jbeam" + +supportRenameIdempotencySpec :: Spec +supportRenameIdempotencySpec = + describe "support vertex renaming" + . it "is a fixed point: transforming the output again renames nothing further" + $ do + let cfg = newTransformationConfig {supportThreshold = 20} + topNode <- parseJbeamFile supportRenameIdempotencyFixture + case transform M.empty cfg topNode of + Left err -> expectationFailure ("first transform failed: " ++ T.unpack err) + Right (_, _, _, onceNode) -> + case transform M.empty cfg onceNode of + Left err -> expectationFailure ("second transform failed: " ++ T.unpack err) + Right (_, _, _, twiceNode) -> do + let once = vertexPositionsInOrder onceNode + once `shouldNotBe` [] + vertexPositionsInOrder twiceNode `shouldBe` once + main :: IO () main = hspec $ do let exampleConfigPath = unsafeEncodeUtf "examples/jbeam-edit.yaml" @@ -99,6 +234,12 @@ main = hspec $ do ++ cfName ++ ".jbeam" testInputFile cfName tfConfig' inFile = topNodeSpec (read rs) cfName tfConfig' inFile (outputFile cfName inFile) + testFixedPoint cfName tfConfig' inFile = + fixedPointSpec (read rs) cfName tfConfig' (outputFile cfName inFile) mapM_ (testInputFile "cfg-default" newTransformationConfig) inputFiles mapM_ (testInputFile "cfg-example" tfConfig) inputFiles + mapM_ (testFixedPoint "cfg-default" newTransformationConfig) inputFiles + mapM_ (testFixedPoint "cfg-example" tfConfig) inputFiles beamValidationSpec + supportRenameIdempotencySpec + letterEndingNodesSpec