Verovio
Source code documentation
editortoolkit_neume.h
1 // Name: editortoolkit_neume.h
3 // Author: Juliette Regimbal
4 // Created: 04/06/2019
5 // Copyright (c) Authors and others. All rights reserved.
7 
8 #ifndef __VRV_EDITOR_TOOLKIT_NEUME_H__
9 #define __VRV_EDITOR_TOOLKIT_NEUME_H__
10 
11 #include <cmath>
12 #include <string>
13 #include <utility>
14 
15 //--------------------------------------------------------------------------------
16 
17 #include "doc.h"
18 #include "editortoolkit.h"
19 #include "measure.h"
20 #include "staff.h"
21 #include "view.h"
22 #include "vrv.h"
23 #include "zone.h"
24 
25 #include "jsonxx.h"
26 
27 namespace vrv {
28 
29 //--------------------------------------------------------------------------------
30 // EditorToolkitNeume
31 //--------------------------------------------------------------------------------
32 
34 public:
35  EditorToolkitNeume(Doc *doc, View *view) : EditorToolkit(doc, view) {}
36  bool ParseEditorAction(const std::string &json_editorAction) override;
37 
42  bool AddSyl(std::string elementId, std::string sylText);
43  bool Chain(jsonxx::Array actions);
44  bool DisplaceClefOctave(std::string elementId, std::string direction);
45  bool Drag(std::string elementId, int x, int y, bool topLevel = true);
46  bool Insert(std::string elementType, std::string staffId, int ulx, int uly, int lrx, int lry,
47  std::vector<std::pair<std::string, std::string>> attributes);
48  bool InsertToSyllable(std::string elementId);
49  bool MatchHeight(std::string elementId);
50  bool Merge(std::vector<std::string> elementIds);
51  bool MoveOutsideSyllable(std::string elementId);
52  bool Set(std::string elementId, std::string attrType, std::string attrValue);
53  bool SetText(std::string elementId, const std::string &text);
54  bool SetClef(std::string elementId, std::string shape);
55  bool SetLiquescent(std::string elementId, std::string shape);
56  bool SetAquitanianElement(std::string elementId, std::string shape);
57  bool SortStaves();
58  bool Split(std::string elementId, int x);
59  bool SplitNeume(std::string elementId, std::string ncId);
60  bool Remove(std::string elementId);
61  bool Resize(std::string elementId, int ulx, int uly, int lrx, int lry, float resize = NAN);
62  bool Group(std::string groupType, std::vector<std::string> elementIds);
63  void UnlinkSyllable(Syllable *syllable);
64  bool Ungroup(std::string groupType, std::vector<std::string> elementIds);
65  bool ChangeGroup(std::string elementId, std::string contour);
66  bool ToggleLigature(std::vector<std::string> elementIds);
67  bool ToggleNeumeConnection(std::vector<std::string> elementIds);
68  bool ChangeStaff(std::string elementId);
69  bool ChangeStaffTo(std::string elementId, std::string staffId);
70  bool ClefMovementHandler(Clef *clef, int x, int y);
72 protected:
77  bool ParseAddSylAction(jsonxx::Object param, std::string *elementId, std::string *sylText);
78  bool ParseDisplaceClefAction(jsonxx::Object param, std::string *elementId, std::string *direction);
79  bool ParseDragAction(jsonxx::Object param, std::string *elementId, int *x, int *y);
80  bool ParseInsertAction(jsonxx::Object param, std::string *elementType, std::string *startId, std::string *endId);
81  bool ParseInsertAction(jsonxx::Object param, std::string *elementType, std::string *staffId, int *ulx, int *uly,
82  int *lrx, int *lry, std::vector<std::pair<std::string, std::string>> *attributes);
83  bool ParseInsertToSyllableAction(jsonxx::Object param, std::string *elementId);
84  bool ParseMatchHeightAction(jsonxx::Object param, std::string *elementId);
85  bool ParseMergeAction(jsonxx::Object param, std::vector<std::string> *elementIds);
86  bool ParseMoveOutsideSyllableAction(jsonxx::Object param, std::string *elementId);
87  bool ParseSetAction(jsonxx::Object param, std::string *elementId, std::string *attrType, std::string *attrValue);
88  bool ParseSetTextAction(jsonxx::Object param, std::string *elementId, std::string *text);
89  bool ParseSetClefAction(jsonxx::Object param, std::string *elementId, std::string *shape);
90  bool ParseSetLiquescentAction(jsonxx::Object param, std::string *elementId, std::string *shape);
91  bool ParseSetAquitanianElementAction(jsonxx::Object param, std::string *elementId, std::string *shape);
92  bool ParseSplitAction(jsonxx::Object param, std::string *elementId, int *x);
93  bool ParseSplitNeumeAction(jsonxx::Object param, std::string *elementId, std::string *ncId);
94  bool ParseRemoveAction(jsonxx::Object param, std::string *elementId);
95  bool ParseResizeAction(jsonxx::Object param, std::string *elementId, int *ulx, int *uly, int *lrx, int *lry);
96  bool ParseResizeRotateAction(
97  jsonxx::Object param, std::string *elementId, int *ulx, int *uly, int *lrx, int *lry, float *rotate);
98  bool ParseGroupAction(jsonxx::Object param, std::string *groupType, std::vector<std::string> *elementIds);
99  bool ParseUngroupAction(jsonxx::Object param, std::string *groupType, std::vector<std::string> *elementIds);
100  bool ParseChangeGroupAction(jsonxx::Object param, std::string *elementId, std::string *contour);
101  bool ParseToggleLigatureAction(jsonxx::Object param, std::vector<std::string> *elementIds);
102  bool ParseToggleNeumeConnectionAction(jsonxx::Object param, std::vector<std::string> *elementIds);
103  bool ParseChangeStaffAction(jsonxx::Object param, std::string *elementId);
104  bool ParseChangeStaffToAction(jsonxx::Object param, std::string *elementId, std::string *staffId);
106 
111  bool AdjustPitchAfterDrag(Object *obj, int y = 0);
112  bool AdjustPitchFromPosition(Object *obj);
113  bool AdjustClefLineFromPosition(Clef *clef, Staff *staff = NULL);
115 };
116 
117 //--------------------------------------------------------------------------------
118 // Comparator structs
119 //--------------------------------------------------------------------------------
120 // To be used with std::sort to find the object with a closest bounding
121 // box to a point defined by the x and y parameters of ClosestBB
122 
123 struct ClosestBB {
124  int x;
125  int y;
126 
127  int distanceToBB(int ulx, int uly, int lrx, int lry, double rotate = 0)
128  {
129  int offset = (x - ulx) * tan(rotate * M_PI / 180.0);
130  uly = uly + offset;
131  lry = lry + offset;
132  int xDiff = std::max((ulx > x ? ulx - x : 0), (x > lrx ? x - lrx : 0));
133  int yDiff = std::max((uly > y ? uly - y : 0), (y > lry ? y - lry : 0));
134 
135  return sqrt(xDiff * xDiff + yDiff * yDiff);
136  }
137 
138  bool operator()(Object *a, Object *b)
139  {
140  if (!a->GetFacsimileInterface() || !b->GetFacsimileInterface()) return true;
141  Zone *zoneA = a->GetFacsimileInterface()->GetZone();
142  Zone *zoneB = b->GetFacsimileInterface()->GetZone();
143 
144  int distA = distanceToBB(zoneA->GetUlx(), zoneA->GetUly(), zoneA->GetLrx(), zoneA->GetLry(),
145  zoneA->HasRotate() ? zoneA->GetRotate() : 0);
146  int distB = distanceToBB(zoneB->GetUlx(), zoneB->GetUly(), zoneB->GetLrx(), zoneB->GetLry(),
147  zoneB->HasRotate() ? zoneB->GetRotate() : 0);
148  return (distA < distB);
149  }
150 };
151 
152 // To be used with std::stable_sort to find the position to insert a new accid / divLine
153 // TODO: use closesBB instead
154 struct ClosestNeume {
155  int x;
156  int y;
157 
158  bool operator()(Object *a, Object *b)
159  {
160  // check if neume has neume components
161  if (!a->GetFirst(NC)) {
162  LogError("Neume %s doesn't have neume components.", a->GetID().c_str());
163  return true;
164  }
165  if (!b->GetFirst(NC)) {
166  LogError("Neume %s doesn't have neume components.", b->GetID().c_str());
167  return true;
168  }
169  if (!a->GetFirst(NC)->GetFacsimileInterface()) {
170  LogError("Neume component %s doesn't have facsimile.", a->GetFirst(NC)->GetID().c_str());
171  return true;
172  }
173  if (!b->GetFirst(NC)->GetFacsimileInterface()) {
174  LogError("Neume component %s doesn't have facsimile.", b->GetFirst(NC)->GetID().c_str());
175  return true;
176  }
177  Zone *zoneA = a->GetFirst(NC)->GetFacsimileInterface()->GetZone();
178  Zone *zoneB = b->GetFirst(NC)->GetFacsimileInterface()->GetZone();
179 
180  int distA = std::abs(x - zoneA->GetUlx());
181  int distB = std::abs(x - zoneB->GetUlx());
182 
183  return (distA < distB);
184  }
185 };
186 
187 // To be used with std::stable_sort to find the position to insert a new staff
188 
189 struct StaffSort {
190  // Update 2024-04:
191  // Used only in neume lines,
192  // System->(Measure->Staff)
193  // Need to sort Measure to sort staff
194 
195  // Sort staves by:
196  // 1. Column number derived from staff@type (column1, column2, etc)
197  // 2. Within each column: left-to-right and top-to-bottom
198  bool operator()(Object *a, Object *b)
199  {
200  if (!a->Is(SYSTEM) || !b->Is(SYSTEM)) return false;
201  if (!a->FindDescendantByType(MEASURE) || !b->FindDescendantByType(MEASURE)) return false;
202 
203  Measure *measureA = dynamic_cast<Measure *>(a->FindDescendantByType(MEASURE));
204  Measure *measureB = dynamic_cast<Measure *>(b->FindDescendantByType(MEASURE));
205  if (!measureA->IsNeumeLine() || !measureB->IsNeumeLine()) return true;
206 
207  Staff *staffA = dynamic_cast<Staff *>(a->FindDescendantByType(STAFF));
208  Staff *staffB = dynamic_cast<Staff *>(b->FindDescendantByType(STAFF));
209  assert(staffA);
210  assert(staffB);
211 
212  if (staffA->HasType() && staffB->HasType()) {
213  // First compare column numbers from staff@type
214  std::string typeA = staffA->GetType();
215  std::string typeB = staffB->GetType();
216 
217  if (staffA->GetType().find("column") == 0 && staffB->GetType().find("column") == 0) {
218  int columnA = std::stoi(typeA.substr(6));
219  int columnB = std::stoi(typeB.substr(6));
220  if (columnA != columnB) {
221  return columnA < columnB;
222  }
223  }
224  }
225 
226  // If in same column, use position-based sorting logic
227  Zone *zoneA = staffA->GetFacsimileInterface()->GetZone();
228  Zone *zoneB = staffB->GetFacsimileInterface()->GetZone();
229  assert(zoneA);
230  assert(zoneB);
231 
232  int aLowest, bLowest, aHighest, bHighest;
233 
234  aLowest = zoneA->GetRotate() < 0
235  ? zoneA->GetLry()
236  : zoneA->GetLry() + (zoneA->GetLrx() - zoneA->GetUlx()) * tan(zoneA->GetRotate() * M_PI / 180.0);
237 
238  aHighest = zoneA->GetRotate() < 0
239  ? zoneA->GetUly()
240  : zoneA->GetUly() - (zoneA->GetLrx() - zoneA->GetUlx()) * tan(zoneA->GetRotate() * M_PI / 180.0);
241 
242  bLowest = zoneB->GetRotate() < 0
243  ? zoneB->GetLry()
244  : zoneB->GetLry() + (zoneB->GetLrx() - zoneB->GetUlx()) * tan(zoneB->GetRotate() * M_PI / 180.0);
245 
246  bHighest = zoneB->GetRotate() < 0
247  ? zoneB->GetUly()
248  : zoneB->GetUly() - (zoneB->GetLrx() - zoneB->GetUlx()) * tan(zoneB->GetRotate() * M_PI / 180.0);
249 
250  // Check for y intersection
251  // if the x intersection part is smaller than half of length of staffA
252  // sort by x coordinate
253  if (((aLowest <= bLowest && aLowest >= bHighest) || (aHighest <= bLowest && aHighest >= bHighest)
254  || (bLowest <= aLowest && bLowest >= aHighest) || (bHighest <= aLowest && bHighest >= aHighest))
255  && (zoneA->GetLrx() - zoneB->GetUlx() <= 0.5 * (zoneA->GetLrx() - zoneA->GetUlx()))) {
256  // sort by x center
257  return (zoneA->GetUlx() < zoneB->GetUlx());
258  }
259  else { // no intersection
260  return (zoneA->GetUly() < zoneB->GetUly());
261  }
262  }
263 };
264 
265 } // namespace vrv
266 
267 #endif
This class models the MEI <clef> element.
Definition: clef.h:41
This class is a hold the data and corresponds to the model of a MVC design pattern.
Definition: doc.h:41
Definition: editortoolkit.h:30
Definition: editortoolkit_neume.h:33
bool ParseEditorAction(const std::string &json_editorAction) override
In child classes, this parses the provided editor action and then performs the correct action.
bool AddSyl(std::string elementId, std::string sylText)
Experimental editor functions.
bool ParseAddSylAction(jsonxx::Object param, std::string *elementId, std::string *sylText)
Parse JSON instructions for experimental editor functions.
bool AdjustPitchAfterDrag(Object *obj, int y=0)
Helper functions for editor actions.
Zone * GetZone()
Get the zone.
Definition: facsimileinterface.h:58
This class represents a measure in a page-based score (Doc).
Definition: measure.h:46
bool IsNeumeLine() const
Return true if the measure represents a neume (section) line.
Definition: measure.h:87
This class represents a basic object.
Definition: object.h:66
Object * FindDescendantByType(ClassId classId, int deepness=UNLIMITED_DEPTH, bool direction=FORWARD)
Look for a descendant with the specified type (returns NULL if not found) This method is a wrapper fo...
This class represents a staff in a laid-out score (Doc).
Definition: staff.h:110
This class models the MEI <mensur> element.
Definition: syllable.h:25
This class is a drawing context and corresponds to the view of a MVC design pattern.
Definition: view.h:109
Implements the zone element in MEI.
Definition: zone.h:30
Definition: editortoolkit_neume.h:123
Definition: editortoolkit_neume.h:154
Definition: editortoolkit_neume.h:189