diff --git a/d2layouts/d2sequence/constants.go b/d2layouts/d2sequence/constants.go index 78ff0ce16..07a8c2ff1 100644 --- a/d2layouts/d2sequence/constants.go +++ b/d2layouts/d2sequence/constants.go @@ -6,7 +6,7 @@ const HORIZONTAL_PAD = 50. // leaves at least 25 units of space on the top/bottom when computing the space required between messages const VERTICAL_PAD = 50. -const MIN_ACTOR_DISTANCE = 70. +const MIN_ACTOR_DISTANCE = 250. const MIN_ACTOR_WIDTH = 150. diff --git a/d2layouts/d2sequence/sequence_diagram.go b/d2layouts/d2sequence/sequence_diagram.go index fdf214eff..e0bd09f73 100644 --- a/d2layouts/d2sequence/sequence_diagram.go +++ b/d2layouts/d2sequence/sequence_diagram.go @@ -76,9 +76,7 @@ func newSequenceDiagram(objects []*d2graph.Object, messages []*d2graph.Edge) *se curr := queue[0] groups = append(groups, curr) queue = queue[1:] - for _, c := range curr.ChildrenArray { - queue = append(queue, c) - } + queue = append(queue, curr.ChildrenArray...) } } else { actors = append(actors, obj) @@ -138,7 +136,9 @@ func newSequenceDiagram(objects []*d2graph.Object, messages []*d2graph.Edge) *se } if rank != len(actors)-1 { - sd.actorXStep[rank] = math.Max(actor.Width/2., MIN_ACTOR_DISTANCE) + actorHW := actor.Width / 2. + nextActorHW := actors[rank+1].Width / 2. + sd.actorXStep[rank] = math.Max(actorHW+nextActorHW+HORIZONTAL_PAD, MIN_ACTOR_DISTANCE) } } diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index 3596355f2..31bb39b67 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -1414,14 +1414,17 @@ choo: { name: "sequence_diagram_actor_distance", script: `shape: sequence_diagram a: "an actor with a really long label that will break everything" -b: "a short one" c: "an\nactor\nwith\na\nreally\nlong\nlabel\nthat\nwill\nbreak\neverything" d: "simple" -a -> b -> c: "short" +e: "a short one" +b: "far away" +f: "what if there were no labels between this actor and the previous one" +a -> b: "short" a -> b: "long label for testing purposes and it must be really, really long" -b -> c: "long label for testing purposes and it must be really, really long" c -> d: "short" -a -> d: "this should span many actors lifelines so we know how it will look like when redering a long label over many actors"`, +a -> d: "this should span many actors lifelines so we know how it will look like when redering a long label over many actors" +d -> e: "long label for testing purposes and it must be really, really long" +a -> f`, }, } diff --git a/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/board.exp.json b/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/board.exp.json index bfd562063..3ce0ee4a5 100644 --- a/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/board.exp.json +++ b/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/board.exp.json @@ -40,50 +40,11 @@ "zIndex": 0, "level": 1 }, - { - "id": "b", - "type": "", - "pos": { - "x": 626, - "y": 210 - }, - "width": 184, - "height": 126, - "opacity": 1, - "strokeDash": 0, - "strokeWidth": 2, - "borderRadius": 0, - "fill": "#EDF0FD", - "stroke": "#0D32B2", - "shadow": false, - "3d": false, - "multiple": false, - "tooltip": "", - "link": "", - "icon": null, - "iconPosition": "", - "fields": null, - "methods": null, - "columns": null, - "label": "a short one", - "fontSize": 16, - "fontFamily": "DEFAULT", - "language": "", - "color": "#0A0F25", - "italic": false, - "bold": true, - "underline": false, - "labelWidth": 84, - "labelHeight": 26, - "labelPosition": "INSIDE_MIDDLE_CENTER", - "zIndex": 0, - "level": 1 - }, { "id": "c", "type": "", "pos": { - "x": 1088, + "x": 588, "y": 50 }, "width": 183, @@ -122,7 +83,7 @@ "id": "d", "type": "", "pos": { - "x": 1401, + "x": 1026, "y": 210 }, "width": 152, @@ -156,6 +117,123 @@ "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, "level": 1 + }, + { + "id": "e", + "type": "", + "pos": { + "x": 1471, + "y": 210 + }, + "width": 184, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "fields": null, + "methods": null, + "columns": null, + "label": "a short one", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 84, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "b", + "type": "", + "pos": { + "x": 1730, + "y": 210 + }, + "width": 167, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "fields": null, + "methods": null, + "columns": null, + "label": "far away", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 67, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "f", + "type": "", + "pos": { + "x": 1947, + "y": 210 + }, + "width": 587, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "fields": null, + "methods": null, + "columns": null, + "label": "what if there were no labels between this actor and the previous one", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 487, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 } ], "connections": [ @@ -189,7 +267,7 @@ "y": 466 }, { - "x": 718, + "x": 1813.5, "y": 466 } ], @@ -198,45 +276,6 @@ "icon": null, "zIndex": 4 }, - { - "id": "(b -> c)[0]", - "src": "b", - "srcArrow": "none", - "srcLabel": "", - "dst": "c", - "dstArrow": "triangle", - "dstLabel": "", - "opacity": 1, - "strokeDash": 0, - "strokeWidth": 2, - "stroke": "#0D32B2", - "label": "short", - "fontSize": 16, - "fontFamily": "DEFAULT", - "language": "", - "color": "#676C7E", - "italic": true, - "bold": false, - "underline": false, - "labelWidth": 36, - "labelHeight": 21, - "labelPosition": "INSIDE_MIDDLE_CENTER", - "labelPercentage": 0, - "route": [ - { - "x": 718, - "y": 596 - }, - { - "x": 1179.5, - "y": 596 - } - ], - "animated": false, - "tooltip": "", - "icon": null, - "zIndex": 4 - }, { "id": "(a -> b)[1]", "src": "a", @@ -264,50 +303,11 @@ "route": [ { "x": 257, - "y": 726 + "y": 596 }, { - "x": 718, - "y": 726 - } - ], - "animated": false, - "tooltip": "", - "icon": null, - "zIndex": 4 - }, - { - "id": "(b -> c)[1]", - "src": "b", - "srcArrow": "none", - "srcLabel": "", - "dst": "c", - "dstArrow": "triangle", - "dstLabel": "", - "opacity": 1, - "strokeDash": 0, - "strokeWidth": 2, - "stroke": "#0D32B2", - "label": "long label for testing purposes and it must be really, really long", - "fontSize": 16, - "fontFamily": "DEFAULT", - "language": "", - "color": "#676C7E", - "italic": true, - "bold": false, - "underline": false, - "labelWidth": 411, - "labelHeight": 21, - "labelPosition": "INSIDE_MIDDLE_CENTER", - "labelPercentage": 0, - "route": [ - { - "x": 718, - "y": 856 - }, - { - "x": 1179.5, - "y": 856 + "x": 1813.5, + "y": 596 } ], "animated": false, @@ -341,12 +341,12 @@ "labelPercentage": 0, "route": [ { - "x": 1179.5, - "y": 986 + "x": 679.5, + "y": 726 }, { - "x": 1477, - "y": 986 + "x": 1102, + "y": 726 } ], "animated": false, @@ -378,13 +378,91 @@ "labelHeight": 21, "labelPosition": "INSIDE_MIDDLE_CENTER", "labelPercentage": 0, + "route": [ + { + "x": 257, + "y": 856 + }, + { + "x": 1102, + "y": 856 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 4 + }, + { + "id": "(d -> e)[0]", + "src": "d", + "srcArrow": "none", + "srcLabel": "", + "dst": "e", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "long label for testing purposes and it must be really, really long", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 411, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPercentage": 0, + "route": [ + { + "x": 1102, + "y": 986 + }, + { + "x": 1563, + "y": 986 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 4 + }, + { + "id": "(a -> f)[0]", + "src": "a", + "srcArrow": "none", + "srcLabel": "", + "dst": "f", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, "route": [ { "x": 257, "y": 1116 }, { - "x": 1477, + "x": 2240.5, "y": 1116 } ], @@ -432,45 +510,6 @@ "icon": null, "zIndex": 2 }, - { - "id": "(b -- )[0]", - "src": "b", - "srcArrow": "none", - "srcLabel": "", - "dst": "b-lifeline-end-668380428", - "dstArrow": "none", - "dstLabel": "", - "opacity": 1, - "strokeDash": 6, - "strokeWidth": 2, - "stroke": "#0D32B2", - "label": "", - "fontSize": 16, - "fontFamily": "DEFAULT", - "language": "", - "color": "#676C7E", - "italic": true, - "bold": false, - "underline": false, - "labelWidth": 0, - "labelHeight": 0, - "labelPosition": "", - "labelPercentage": 0, - "route": [ - { - "x": 718, - "y": 336 - }, - { - "x": 718, - "y": 1246 - } - ], - "animated": false, - "tooltip": "", - "icon": null, - "zIndex": 2 - }, { "id": "(c -- )[0]", "src": "c", @@ -497,11 +536,11 @@ "labelPercentage": 0, "route": [ { - "x": 1179.5, + "x": 679.5, "y": 336 }, { - "x": 1179.5, + "x": 679.5, "y": 1246 } ], @@ -536,11 +575,128 @@ "labelPercentage": 0, "route": [ { - "x": 1477, + "x": 1102, "y": 336 }, { - "x": 1477, + "x": 1102, + "y": 1246 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 2 + }, + { + "id": "(e -- )[0]", + "src": "e", + "srcArrow": "none", + "srcLabel": "", + "dst": "e-lifeline-end-2214352275", + "dstArrow": "none", + "dstLabel": "", + "opacity": 1, + "strokeDash": 6, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 1563, + "y": 336 + }, + { + "x": 1563, + "y": 1246 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 2 + }, + { + "id": "(b -- )[0]", + "src": "b", + "srcArrow": "none", + "srcLabel": "", + "dst": "b-lifeline-end-668380428", + "dstArrow": "none", + "dstLabel": "", + "opacity": 1, + "strokeDash": 6, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 1813.5, + "y": 336 + }, + { + "x": 1813.5, + "y": 1246 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 2 + }, + { + "id": "(f -- )[0]", + "src": "f", + "srcArrow": "none", + "srcLabel": "", + "dst": "f-lifeline-end-865917984", + "dstArrow": "none", + "dstLabel": "", + "opacity": 1, + "strokeDash": 6, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 2240.5, + "y": 336 + }, + { + "x": 2240.5, "y": 1246 } ], diff --git a/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/sketch.exp.svg index d1f73d658..01f8a5980 100644 --- a/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/sketch.exp.svg @@ -2,7 +2,7 @@ an actor with a really long label that will break everythinganactorwithareallylonglabelthatwillbreakeverythingsimplea short onefar awaywhat if there were no labels between this actor and the previous one shortlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actorslong label for testing purposes and it must be really, really long + + + + + + an actor with a really long label that will break everythinga short oneanactorwithareallylonglabelthatwillbreakeverythingsimple shortshortlong label for testing purposes and it must be really, really longlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actors - - - - - - - +an actor with a really long label that will break everythinganactorwithareallylonglabelthatwillbreakeverythingsimplea short onefar awaywhat if there were no labels between this actor and the previous one shortlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actorslong label for testing purposes and it must be really, really long + + + + + + a labelblabelsa class+ -public() bool -void- -private() int -voidcloudyyyy:= 5 +a labelblabelsa class+ +public() bool +void- +private() int +voidcloudyyyy:= 5 := a + 7 -fmt.Printf("%d", b)cyldiadocssix cornersa random iconoverpackdocs pagetoohard o saysinglepersona queuea squarea step at a timedatausersid -int -name -varchar - result := callThisFunction(obj, 5) midthis sideother side - +fmt.Printf("%d", b)cyldiadocssix cornersa random iconoverpackdocs pagetoohard o saysinglepersona queuea squarea step at a timedatausersid +int +name +varchar + result := callThisFunction(obj, 5) midthis sideother side + - + a labelblabelsa class+ -public() bool -void- -private() int -voidcloudyyyy:= 5 +a labelblabelsa class+ +public() bool +void- +private() int +voidcloudyyyy:= 5 := a + 7 -fmt.Printf("%d", b)cyldiadocssix cornersa random iconoverpackdocs pagetoohard o saysinglepersona queuea squarea step at a timedatausersid -int -name -varchar - result := callThisFunction(obj, 5) midthis sideother side - +fmt.Printf("%d", b)cyldiadocssix cornersa random iconoverpackdocs pagetoohard o saysinglepersona queuea squarea step at a timedatausersid +int +name +varchar + result := callThisFunction(obj, 5) midthis sideother side + - + abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note - - - - - +abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note + + + + + abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note - - - - - +abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note + + + + + scoreritemResponseitemessayRubricconceptitemOutcome scoreritemResponseitemessayRubricconceptitemOutcome abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier - - +abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier + + abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier - - +abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier + + How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place - - - - - - - - - - - - - - +How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place + + + + + + + + + + + + + + How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place - - - - - - - - - - - - - - +How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place + + + + + + + + + + + + + + ab a self edge herebetween actorsto descendantto deeper descendantto parentactor - +ab a self edge herebetween actorsto descendantto deeper descendantto parentactor + - - - - - + + + + + ab a self edge herebetween actorsto descendantto deeper descendantto parentactor - +ab a self edge herebetween actorsto descendantto deeper descendantto parentactor + - - - - - + + + + + scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) - - - - - - - - - - - - - +scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + + + + + + + + + + + + + scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) - - - - - - - - - - - - - +scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + + + + + + + + + + + + + a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) - - - - - - - - - - - - - - - - - - - - - - - - - +a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + + + + + + + + + + + + + + + + + + + + + + + + + a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) - - - - - - - - - - - - - - - - - - - - - - - - - +a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + + + + + + + + + + + + + + + + + + + + + + + + +