linny-r 2.0.6 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ for the Linny-R Monitor dialog.
11
11
  */
12
12
 
13
13
  /*
14
- Copyright (c) 2017-2024 Delft University of Technology
14
+ Copyright (c) 2017-2025 Delft University of Technology
15
15
 
16
16
  Permission is hereby granted, free of charge, to any person obtaining a copy
17
17
  of this software and associated documentation files (the "Software"), to deal
@@ -11,7 +11,7 @@ functionality for the Linny-R model editor.
11
11
  */
12
12
 
13
13
  /*
14
- Copyright (c) 2017-2024 Delft University of Technology
14
+ Copyright (c) 2017-2025 Delft University of Technology
15
15
 
16
16
  Permission is hereby granted, free of charge, to any person obtaining a copy
17
17
  of this software and associated documentation files (the "Software"), to deal
@@ -909,16 +909,17 @@ class Paper {
909
909
  }
910
910
 
911
911
  //
912
- // Diagram-drawing method draws the diagram for the focal cluster
912
+ // Diagram-drawing method draws the diagram for the focal cluster.
913
913
  //
914
914
 
915
915
  drawModel(mdl) {
916
- // Draw the diagram for the focal cluster
916
+ // Draw the diagram for the focal cluster.
917
917
  this.clear();
918
- // Prepare to draw all elements in the focal cluster
918
+ // Prepare to draw all elements in the focal cluster.
919
919
  const fc = mdl.focal_cluster;
920
920
  fc.categorizeEntities();
921
- // NOTE: product positions must be updated before links are drawn
921
+ // NOTE: Product positions must be updated before links are drawn, so
922
+ // that links arrows will be drawn over their shapes.
922
923
  fc.positionProducts();
923
924
  for(let i = 0; i < fc.processes.length; i++) {
924
925
  fc.processes[i].clearHiddenIO();
@@ -926,10 +927,10 @@ class Paper {
926
927
  for(let i = 0; i < fc.sub_clusters.length; i++) {
927
928
  fc.sub_clusters[i].clearHiddenIO();
928
929
  }
929
- // NOTE: also ensure that notes will update their fields
930
+ // NOTE: Also ensure that notes will update their fields.
930
931
  fc.resetNoteFields();
931
932
  // Draw link arrows and constraints first, as all other entities are
932
- // slightly transparent so they cannot completely hide these lines
933
+ // slightly transparent so they cannot completely hide these lines.
933
934
  for(let i = 0; i < fc.arrows.length; i++) {
934
935
  this.drawArrow(fc.arrows[i]);
935
936
  }
@@ -945,25 +946,25 @@ class Paper {
945
946
  for(let i = 0; i < fc.sub_clusters.length; i++) {
946
947
  this.drawCluster(fc.sub_clusters[i]);
947
948
  }
948
- // Draw notes last, as they are semi-transparent (and can be quite small)
949
+ // Draw notes last, as they are semi-transparent (and can be quite small).
949
950
  for(let i = 0; i < fc.notes.length; i++) {
950
951
  this.drawNote(fc.notes[i]);
951
952
  }
952
- // Resize paper if necessary
953
+ // Resize paper if necessary.
953
954
  this.extend();
954
- // Display model name in browser
955
+ // Display model name in browser.
955
956
  document.title = mdl.name || 'Linny-R';
956
957
  }
957
958
 
958
959
  drawSelection(mdl, dx=0, dy=0) {
959
960
  // NOTE: Clear this global, as Bezier curves move from under the cursor
960
- // without a mouseout event
961
+ // without a mouseout event.
961
962
  this.constraint_under_cursor = null;
962
- // Draw the selected entities and associated links, and also constraints
963
+ // Draw the selected entities and associated links, and also constraints.
963
964
  for(let i = 0; i < mdl.selection.length; i++) {
964
965
  const obj = mdl.selection[i];
965
966
  // Links and constraints are drawn separately, so do not draw those
966
- // contained in the selection
967
+ // contained in the selection.
967
968
  if(!(obj instanceof Link || obj instanceof Constraint)) {
968
969
  if(obj instanceof Note) obj.parsed = false;
969
970
  UI.drawObject(obj, dx, dy);
@@ -972,12 +973,12 @@ class Paper {
972
973
  if(mdl.selection_related_arrows.length === 0) {
973
974
  mdl.selection_related_arrows = mdl.focal_cluster.selectedArrows();
974
975
  }
975
- // Only draw the arrows that relate to the selection
976
+ // Only draw the arrows that relate to the selection.
976
977
  for(let i = 0; i < mdl.selection_related_arrows.length; i++) {
977
978
  this.drawArrow(mdl.selection_related_arrows[i]);
978
979
  }
979
980
  // As they typically are few, simply redraw all constraints that relate to
980
- // the focal cluster
981
+ // the focal cluster.
981
982
  for(let i = 0; i < mdl.focal_cluster.related_constraints.length; i++) {
982
983
  this.drawConstraint(mdl.focal_cluster.related_constraints[i]);
983
984
  }
@@ -990,23 +991,23 @@ class Paper {
990
991
  //
991
992
 
992
993
  drawArrow(arrw, dx=0, dy=0) {
993
- // Draws an arrow from FROM nodebox to TO nodebox
994
- // NOTE: first erase previously drawn arrow
994
+ // Draw an arrow from FROM nodebox to TO nodebox.
995
+ // NOTE: First erase previously drawn arrow.
995
996
  arrw.shape.clear();
996
997
  arrw.hidden_nodes.length = 0;
997
- // Use local variables so as not to change any "real" attribute values
998
+ // Use local variables so as not to change any "real" attribute values.
998
999
  let cnb, proc, prod, fnx, fny, fnw, fnh, tnx, tny, tnw, tnh,
999
1000
  cp, rr, aa, bb, dd, nn, af, l, s, w, tw, th, bpx, bpy, epx, epy,
1000
1001
  sda, stroke_color, stroke_width, arrow_start, arrow_end,
1001
1002
  font_color, font_weight, luc = null, grid = null;
1002
- // Get the main arrow attributes
1003
+ // Get the main arrow attributes.
1003
1004
  const
1004
1005
  from_nb = arrw.from_node,
1005
1006
  to_nb = arrw.to_node;
1006
- // Use "let" because `ignored` may also be set later on (for single link)
1007
+ // Use "let" because `ignored` may also be set later on (for single link).
1007
1008
  let ignored = (from_nb && MODEL.ignored_entities[from_nb.identifier]) ||
1008
1009
  (to_nb && MODEL.ignored_entities[to_nb.identifier]);
1009
- // First check if this is a block arrow (ONE node being null)
1010
+ // First check if this is a block arrow (ONE node being null).
1010
1011
  if(!from_nb) {
1011
1012
  cnb = to_nb;
1012
1013
  } else if(!to_nb) {
@@ -1014,22 +1015,22 @@ class Paper {
1014
1015
  } else {
1015
1016
  cnb = null;
1016
1017
  }
1017
- // If not NULL `cnb` is the cluster or node box (product or process) having
1018
+ // If not NULL, `cnb` is the cluster or node box (product or process) having
1018
1019
  // links to entities outside the focal cluster. Such links are summarized
1019
1020
  // by "block arrows": on the left edge of the box to indicate inflows,
1020
1021
  // on the right edge to indicate outflows, and two-headed on the top edge
1021
1022
  // to indicate two-way flows. When the cursor is moved over a block arrow,
1022
1023
  // the Documentation dialog will display the list of associated nodes
1023
- // (with their actual flows if non-zero)
1024
+ // (with their actual flows if non-zero).
1024
1025
  if(cnb) {
1025
- // Distinguish between input, output and io products
1026
+ // Distinguish between input, output and io products.
1026
1027
  let ip = [], op = [], iop = [];
1027
1028
  if(cnb instanceof Cluster) {
1028
1029
  for(let i = 0; i < arrw.links.length; i++) {
1029
1030
  const lnk = arrw.links[i];
1030
- // determine which product is involved
1031
+ // Determine which product is involved.
1031
1032
  prod = (lnk.from_node instanceof Product ? lnk.from_node : lnk.to_node);
1032
- // NOTE: clusters "know" their input/output products
1033
+ // NOTE: Clusters "know" their input/output products.
1033
1034
  if(cnb.io_products.indexOf(prod) >= 0) {
1034
1035
  addDistinct(prod, iop);
1035
1036
  } else if(cnb.consumed_products.indexOf(prod) >= 0) {
@@ -1039,7 +1040,7 @@ class Paper {
1039
1040
  }
1040
1041
  }
1041
1042
  } else {
1042
- // cnb is process or product => knows its inputs and outputs
1043
+ // `cnb` is process or product => knows its inputs and outputs.
1043
1044
  for(let i = 0; i < arrw.links.length; i++) {
1044
1045
  const lnk = arrw.links[i];
1045
1046
  if(lnk.from_node === cnb) {
@@ -1047,7 +1048,7 @@ class Paper {
1047
1048
  } else {
1048
1049
  addDistinct(lnk.from_node, ip);
1049
1050
  }
1050
- // NOTE: for processes, products cannot be BOTH input and output
1051
+ // NOTE: For processes, products cannot be BOTH input and output.
1051
1052
  }
1052
1053
  }
1053
1054
  cnb.hidden_inputs = ip;
@@ -10,7 +10,7 @@ the Linny-R project.
10
10
  */
11
11
 
12
12
  /*
13
- Copyright (c) 2017-2024 Delft University of Technology
13
+ Copyright (c) 2017-2025 Delft University of Technology
14
14
 
15
15
  Permission is hereby granted, free of charge, to any person obtaining a copy
16
16
  of this software and associated documentation files (the "Software"), to deal
@@ -5801,7 +5801,7 @@ class NodeBox extends ObjectWithXYWH {
5801
5801
  }
5802
5802
 
5803
5803
  resize() {
5804
- // Resizes this node; returns TRUE iff size has changed.
5804
+ // Resize this node; returns TRUE iff size has changed.
5805
5805
  // Therefore, keep track of original width and height.
5806
5806
  const
5807
5807
  ow = this.width,
@@ -5814,7 +5814,7 @@ class NodeBox extends ObjectWithXYWH {
5814
5814
  w = Math.max(w, UI.textSize(`[${this.scale_unit}]`).width);
5815
5815
  }
5816
5816
  this.frame_width = w + 7;
5817
- // Add 17 pixels height for actor name
5817
+ // Add 17 pixels height for actor name.
5818
5818
  this.height = Math.max(50, this.bbox.height + 17);
5819
5819
  if(this instanceof Process) {
5820
5820
  this.width = Math.max(90, this.frame_width + 20);
@@ -5822,11 +5822,11 @@ class NodeBox extends ObjectWithXYWH {
5822
5822
  } else if(this instanceof Cluster) {
5823
5823
  this.width = Math.max(
5824
5824
  CONFIGURATION.min_cluster_size, this.frame_width + 20);
5825
- // Clusters have a square shape
5825
+ // Clusters have a square shape.
5826
5826
  this.height = Math.max(this.width, this.height);
5827
5827
  } else {
5828
5828
  this.height += 8;
5829
- // Reserve some extra space for UB/LB if defined
5829
+ // Reserve some extra space for UB/LB if defined.
5830
5830
  if(this.lower_bound.defined || this.upper_bound.defined) {
5831
5831
  this.frame_width += 16;
5832
5832
  }
@@ -8282,20 +8282,20 @@ class Product extends Node {
8282
8282
  // By default, processes have the letter p, products the letter q.
8283
8283
  this.TEX_id = 'p';
8284
8284
  // For products, the default bounds are [0, 0], and modeler-defined bounds
8285
- // typically are equal
8285
+ // typically are equal.
8286
8286
  this.equal_bounds = true;
8287
- // In addition to LB, UB and IL, products has 1 input attribute: P
8287
+ // In addition to LB, UB and IL, products has 1 input attribute: P.
8288
8288
  this.price = new Expression(this, 'P', '');
8289
- // Products have a highest cost price, and may have a stock price (if storage)
8289
+ // Products have a highest cost price, and may have a stock price (if storage).
8290
8290
  this.highest_cost_price = [];
8291
8291
  this.stock_price = [];
8292
8292
  // Stock level changing from 0 to positive counts as "start up", while
8293
- // changing from positive to 0 counts as a "shut-down"
8294
- // NOTE: being relatively rare, start_ups and shut_downs are not vectors,
8295
- // but store the numbers of the time steps in which they occurred
8293
+ // changing from positive to 0 counts as a "shut-down".
8294
+ // NOTE: Being relatively rare, start_ups and shut_downs are not vectors,
8295
+ // but store the numbers of the time steps in which they occurred.
8296
8296
  this.start_ups = [];
8297
8297
  this.shut_downs = [];
8298
- // Modeler may set explicit properties
8298
+ // Modeler may set explicit properties.
8299
8299
  this.is_source = false;
8300
8300
  this.is_sink = false;
8301
8301
  this.is_buffer = false;
@@ -8650,13 +8650,13 @@ class Product extends Node {
8650
8650
  }
8651
8651
 
8652
8652
  get defaultAttribute() {
8653
- // Products have their level as default attribute
8653
+ // Products have their level as default attribute.
8654
8654
  return 'L';
8655
8655
  }
8656
8656
 
8657
8657
  attributeValue(a) {
8658
- // Returns the computed result for attribute `a`
8659
- // (for products, this is always a vector except IL)
8658
+ // Return the computed result for attribute `a`.
8659
+ // NOTE: For products, this is always a vector except IL.
8660
8660
  if(a === 'L') return this.level;
8661
8661
  if(a === 'CP') return this.cost_price;
8662
8662
  if(a === 'HCP') return this.highest_cost_price;
@@ -8664,7 +8664,7 @@ class Product extends Node {
8664
8664
  }
8665
8665
 
8666
8666
  attributeExpression(a) {
8667
- // Products have four expression attributes
8667
+ // Products have four expression attributes.
8668
8668
  if(a === 'LB') return this.lower_bound;
8669
8669
  if(a === 'UB') {
8670
8670
  return (this.equal_bounds ? this.lower_bound : this.upper_bound);
@@ -8748,7 +8748,7 @@ class Product extends Node {
8748
8748
  }
8749
8749
 
8750
8750
  copyPropertiesFrom(p) {
8751
- // Set properties to be identical to those of product `p`
8751
+ // Set properties to be identical to those of product `p`.
8752
8752
  this.x = p.x;
8753
8753
  this.y = p.y;
8754
8754
  this.comments = p.comments;
@@ -8765,11 +8765,11 @@ class Product extends Node {
8765
8765
  this.initial_level.text = p.initial_level.text;
8766
8766
  this.integer_level = p.integer_level;
8767
8767
  this.TEX_id = p.TEX_id;
8768
- // NOTE: do not copy the `no_links` property, nor the import/export status
8768
+ // NOTE: Do not copy the `no_links` property, nor the import/export status.
8769
8769
  }
8770
8770
 
8771
8771
  differences(p) {
8772
- // Return "dictionary" of differences, or NULL if none
8772
+ // Return "dictionary" of differences, or NULL if none.
8773
8773
  const d = differences(this, p, UI.MC.PRODUCT_PROPS);
8774
8774
  if(Object.keys(d).length > 0) return d;
8775
8775
  return null;
@@ -12699,6 +12699,10 @@ class BoundLine {
12699
12699
  VM.constraint_codes[this.type] + '] bound line #' +
12700
12700
  this.constraint.bound_lines.indexOf(this);
12701
12701
  }
12702
+
12703
+ get name() {
12704
+ return this.displayName;
12705
+ }
12702
12706
 
12703
12707
  get copy() {
12704
12708
  // Return a "clone" of this bound line.
@@ -9,7 +9,7 @@ This JavaScript file (linny-r-utils.js) defines a variety of "helper" functions
9
9
  that are used in other Linny-R modules.
10
10
  */
11
11
  /*
12
- Copyright (c) 2017-2024 Delft University of Technology
12
+ Copyright (c) 2017-2025 Delft University of Technology
13
13
 
14
14
  Permission is hereby granted, free of charge, to any person obtaining a copy
15
15
  of this software and associated documentation files (the "Software"), to deal
@@ -74,16 +74,16 @@ function safeStrToFloat(str, val=0) {
74
74
  }
75
75
 
76
76
  function safeStrToInt(str, val=0) {
77
- // Returns numeric value of integer string, IGNORING decimals after
77
+ // Return numeric value of integer string, IGNORING decimals after
78
78
  // point or comma.
79
- // NOTE: returns default value `val` if `str` is empty, null or undefined
79
+ // NOTE: Return default value `val` if `str` is empty, null or undefined.
80
80
  const n = (str ? parseInt(str) : val);
81
81
  return (isNaN(n) ? val : n);
82
82
  }
83
83
 
84
84
  function rangeToList(str, max=0) {
85
- // Parses ranges "n-m/i" into a list of integers
86
- // Returns FALSE if range is not valid according to the convention below
85
+ // Parse ranges "n-m/i" into a list of integers
86
+ // Return FALSE if range is not valid according to the convention below
87
87
  // The part "/i" is optional and denotes the increment; by default, i = 1.
88
88
  // The returned list will contain all integers starting at n and up to
89
89
  // at most (!) m, with increments of i, so [n, n+i, n+2i, ...]
@@ -112,6 +112,33 @@ function rangeToList(str, max=0) {
112
112
  return list;
113
113
  }
114
114
 
115
+ function listToRange(list) {
116
+ // Return a string that represents the given list of integers as a series
117
+ // of subranges, e.g., [0,1,2,3,5,6,9,11] results in "0-3, 5-6, 9, 11".
118
+ const
119
+ n = list.length,
120
+ subs = [];
121
+ if(!n) return '';
122
+ let i = 0,
123
+ from = list[0],
124
+ to = from;
125
+ while(i < n) {
126
+ i++;
127
+ if(list[i] === to + 1) {
128
+ to++;
129
+ } else {
130
+ if(from === to) {
131
+ subs.push(from);
132
+ } else {
133
+ subs.push(`${from}-${to}`);
134
+ }
135
+ from = list[i];
136
+ to = from;
137
+ }
138
+ }
139
+ return subs.join(', ');
140
+ }
141
+
115
142
  function dateToString(d) {
116
143
  // Returns date-time `d` in UTC format, accounting for time zone
117
144
  const offset = d.getTimezoneOffset();
@@ -198,6 +225,13 @@ function ellipsedText(text, n=50, m=10) {
198
225
  return text.slice(0, n) + ' \u2026 ' + text.slice(text.length - m);
199
226
  }
200
227
 
228
+ function unquoteCSV(s) {
229
+ // Returns a double-quoted string `s` without its quotes, and with
230
+ // quote pairs "" replaced by single " quotes.
231
+ if(!s.startsWith('"') || !s.endsWith('"')) return s;
232
+ return s.slice(1, -1).replaceAll('""', '"');
233
+ }
234
+
201
235
  //
202
236
  // Functions used when comparing two Linny-R models
203
237
  //
@@ -1098,12 +1132,14 @@ if(NODE) module.exports = {
1098
1132
  safeStrToFloat: safeStrToFloat,
1099
1133
  safeStrToInt: safeStrToInt,
1100
1134
  rangeToList: rangeToList,
1135
+ listToRange: listToRange,
1101
1136
  dateToString: dateToString,
1102
1137
  msecToTime: msecToTime,
1103
1138
  compactClockTime: compactClockTime,
1104
1139
  uniformDecimals: uniformDecimals,
1105
1140
  capitalized: capitalized,
1106
1141
  ellipsedText: ellipsedText,
1142
+ unquoteCSV: unquoteCSV,
1107
1143
  earlierVersion: earlierVersion,
1108
1144
  differences: differences,
1109
1145
  markFirstDifference: markFirstDifference,