use of cbit.vcell.render.Vect3d in project vcell by virtualcell.
the class StlExporter method writeBinaryStl.
/**
* Insert the method's description here.
* Creation date: (7/19/2004 10:54:30 AM)
* @param geometrySurfaceDescription cbit.vcell.geometry.surface.GeometrySurfaceDescription
*/
public static void writeBinaryStl(GeometrySurfaceDescription geometrySurfaceDescription, RandomAccessFile raf) throws java.io.IOException {
GeometricRegion[] regions = geometrySurfaceDescription.getGeometricRegions();
SurfaceCollection surfaceCollection = geometrySurfaceDescription.getSurfaceCollection();
//
// vertices should be all in positive quadrant (no negative coordinates) so have to add offset based on origin
//
double ox = geometrySurfaceDescription.getGeometry().getOrigin().getX();
double oy = geometrySurfaceDescription.getGeometry().getOrigin().getY();
double oz = geometrySurfaceDescription.getGeometry().getOrigin().getZ();
//
if (regions == null) {
throw new RuntimeException("Geometric Regions not defined");
}
if (surfaceCollection == null) {
throw new RuntimeException("Surfaces not defined");
}
int triangleCount = 0;
for (int i = 0; i < surfaceCollection.getSurfaceCount(); i++) {
Surface surface = surfaceCollection.getSurfaces(i);
triangleCount += surface.getPolygonCount() * 2;
}
int bytesPerTriangle = 4 * 3 + 4 * 3 + 4 * 3 + 4 * 3 + 2;
int fileLength = 80 + 4 + bytesPerTriangle * triangleCount;
ByteBuffer buf = raf.getChannel().map(MapMode.READ_WRITE, 0L, fileLength);
// stl should be little endian (ref: "http://en.wikipedia.org/wiki/STL_(file_format)" )
buf.order(ByteOrder.LITTLE_ENDIAN);
// write 80 byte ASCII header (no text for now).
buf.put(new byte[80]);
//
// write number of triangles (num facets)
//
buf.putInt(triangleCount);
Triangle[] triangles = new Triangle[2];
Vect3d[] unitNormals = new Vect3d[] { new Vect3d(), new Vect3d() };
for (int j = 0; j < surfaceCollection.getSurfaceCount(); j++) {
Surface surface = surfaceCollection.getSurfaces(j);
for (int k = 0; k < surface.getPolygonCount(); k++) {
Polygon polygon = surface.getPolygons(k);
Vect3d unitNormal = new Vect3d();
if (polygon.getNodeCount() != 4) {
throw new RuntimeException("expecting quad mesh elements for STL export");
}
triangles[0] = new Triangle(polygon.getNodes()[0], polygon.getNodes()[1], polygon.getNodes()[2]);
triangles[1] = new Triangle(polygon.getNodes()[0], polygon.getNodes()[2], polygon.getNodes()[3]);
triangles[0].getUnitNormal(unitNormals[0]);
triangles[1].getUnitNormal(unitNormals[1]);
for (int t = 0; t < 2; t++) {
buf.putFloat((float) unitNormals[t].getX());
buf.putFloat((float) unitNormals[t].getY());
buf.putFloat((float) unitNormals[t].getZ());
Node n0 = triangles[t].getNodes()[0];
buf.putFloat((float) n0.getX());
buf.putFloat((float) n0.getY());
buf.putFloat((float) n0.getZ());
Node n1 = triangles[t].getNodes()[1];
buf.putFloat((float) n1.getX());
buf.putFloat((float) n1.getY());
buf.putFloat((float) n1.getZ());
Node n2 = triangles[t].getNodes()[2];
buf.putFloat((float) n2.getX());
buf.putFloat((float) n2.getY());
buf.putFloat((float) n2.getZ());
// store the surface index in the "attribute byte count".
buf.putShort((short) j);
}
}
}
}
use of cbit.vcell.render.Vect3d in project vcell by virtualcell.
the class SBMLImporter method getVCellCSGNode.
public static cbit.vcell.geometry.CSGNode getVCellCSGNode(org.sbml.jsbml.ext.spatial.CSGNode sbmlCSGNode) {
String csgNodeName = sbmlCSGNode.getId();
if (sbmlCSGNode instanceof org.sbml.jsbml.ext.spatial.CSGPrimitive) {
PrimitiveKind primitiveKind = ((org.sbml.jsbml.ext.spatial.CSGPrimitive) sbmlCSGNode).getPrimitiveType();
cbit.vcell.geometry.CSGPrimitive.PrimitiveType vcellCSGPrimitiveType = getVCellPrimitiveType(primitiveKind);
cbit.vcell.geometry.CSGPrimitive vcellPrimitive = new cbit.vcell.geometry.CSGPrimitive(csgNodeName, vcellCSGPrimitiveType);
return vcellPrimitive;
} else if (sbmlCSGNode instanceof CSGPseudoPrimitive) {
throw new RuntimeException("Pseudo primitives not yet supported in CSGeometry.");
} else if (sbmlCSGNode instanceof CSGSetOperator) {
org.sbml.jsbml.ext.spatial.CSGSetOperator sbmlSetOperator = (org.sbml.jsbml.ext.spatial.CSGSetOperator) sbmlCSGNode;
OperatorType opType = null;
switch(sbmlSetOperator.getOperationType()) {
case difference:
{
opType = OperatorType.DIFFERENCE;
break;
}
case intersection:
{
opType = OperatorType.INTERSECTION;
break;
}
case union:
{
opType = OperatorType.UNION;
break;
}
default:
{
throw new RuntimeException("sbml CSG geometry set operator " + sbmlSetOperator.getOperationType().name() + " not supported");
}
}
cbit.vcell.geometry.CSGSetOperator vcellSetOperator = new cbit.vcell.geometry.CSGSetOperator(csgNodeName, opType);
for (int c = 0; c < sbmlSetOperator.getListOfCSGNodes().size(); c++) {
vcellSetOperator.addChild(getVCellCSGNode(sbmlSetOperator.getListOfCSGNodes().get(c)));
}
return vcellSetOperator;
} else if (sbmlCSGNode instanceof CSGTransformation) {
org.sbml.jsbml.ext.spatial.CSGTransformation sbmlTransformation = (org.sbml.jsbml.ext.spatial.CSGTransformation) sbmlCSGNode;
cbit.vcell.geometry.CSGNode vcellCSGChild = getVCellCSGNode(sbmlTransformation.getCSGNode());
if (sbmlTransformation instanceof org.sbml.jsbml.ext.spatial.CSGTranslation) {
org.sbml.jsbml.ext.spatial.CSGTranslation sbmlTranslation = (org.sbml.jsbml.ext.spatial.CSGTranslation) sbmlTransformation;
Vect3d translation = new Vect3d(sbmlTranslation.getTranslateX(), sbmlTranslation.getTranslateY(), sbmlTranslation.getTranslateZ());
cbit.vcell.geometry.CSGTranslation vcellTranslation = new cbit.vcell.geometry.CSGTranslation(csgNodeName, translation);
vcellTranslation.setChild(vcellCSGChild);
return vcellTranslation;
} else if (sbmlTransformation instanceof CSGRotation) {
CSGRotation sbmlRotation = (CSGRotation) sbmlTransformation;
Vect3d axis = new Vect3d(sbmlRotation.getRotateAxisX(), sbmlRotation.getRotateAxisY(), sbmlRotation.getRotateAxisZ());
double rotationAngleRadians = sbmlRotation.getRotateAngleInRadians();
cbit.vcell.geometry.CSGRotation vcellRotation = new cbit.vcell.geometry.CSGRotation(csgNodeName, axis, rotationAngleRadians);
vcellRotation.setChild(vcellCSGChild);
return vcellRotation;
} else if (sbmlTransformation instanceof CSGScale) {
CSGScale sbmlScale = (CSGScale) sbmlTransformation;
Vect3d scale = new Vect3d(sbmlScale.getScaleX(), sbmlScale.getScaleY(), sbmlScale.getScaleZ());
cbit.vcell.geometry.CSGScale vcellScale = new cbit.vcell.geometry.CSGScale(csgNodeName, scale);
vcellScale.setChild(vcellCSGChild);
return vcellScale;
} else if (sbmlTransformation instanceof CSGHomogeneousTransformation) {
throw new SBMLImportException("homogeneous transformations not supported yet.");
} else {
throw new SBMLImportException("unsupported type of CSGTransformation");
}
} else {
throw new SBMLImportException("unsupported type of CSGNode");
}
}
use of cbit.vcell.render.Vect3d in project vcell by virtualcell.
the class XmlReader method getCSGTranslation.
private CSGTranslation getCSGTranslation(Element param) throws XmlParseException {
String name = unMangle(param.getAttributeValue(XMLTags.NameAttrTag));
String translateXStr = unMangle(param.getAttributeValue(XMLTags.CSGTranslationXTag));
String translateYStr = unMangle(param.getAttributeValue(XMLTags.CSGTranslationYTag));
String translateZStr = unMangle(param.getAttributeValue(XMLTags.CSGTranslationZTag));
Vect3d translateAxis = new Vect3d(Double.parseDouble(translateXStr), Double.parseDouble(translateYStr), Double.parseDouble(translateZStr));
CSGTranslation csgTranslation = new CSGTranslation(name, translateAxis);
// Retrieve CSGNode - CSGScale element should have one child
Object[] elements = param.getChildren().toArray();
if (elements.length > 1) {
throw new XmlParseException("CSGScale element cannot have more than one child element");
}
CSGNode csgChildNode = getCSGNode((Element) elements[0]);
csgTranslation.setChild(csgChildNode);
return csgTranslation;
}
use of cbit.vcell.render.Vect3d in project vcell by virtualcell.
the class ROIMultiPaintManager method showGUI.
public Geometry showGUI(final String okButtonText, final String sourceDataName, final Component parentComponent, String initalAnnotation, final VCPixelClass[] vcPixelClasses, UserPreferences userPreferences) {
originalAnnotation = initalAnnotation;
final Geometry[] finalGeometryHolder = new Geometry[1];
if (overlayEditorPanelJAI == null) {
overlayEditorPanelJAI = new OverlayEditorPanelJAI();
overlayEditorPanelJAI.setCalcCoords(new CalcCoords());
overlayEditorPanelJAI.setUserPreferences(userPreferences);
overlayEditorPanelJAI.setMinimumSize(new Dimension(700, 600));
overlayEditorPanelJAI.setPreferredSize(new Dimension(700, 600));
overlayEditorPanelJAI.addPropertyChangeListener(ROIMultiPaintManager.this);
}
// delete all names
overlayEditorPanelJAI.deleteROIName(null);
// when user selects image "from DB" or "from current geometry"
applyPixelClasses(vcPixelClasses, parentComponent);
if (getImageDataset().length > 1) {
String[] channelNames = new String[getImageDataset().length];
for (int i = 0; i < channelNames.length; i++) {
channelNames[i] = "channel " + i;
}
overlayEditorPanelJAI.setChannelNames(channelNames);
} else {
overlayEditorPanelJAI.setChannelNames(null);
}
updateUnderlayHistogramDisplay();
overlayEditorPanelJAI.setContrastToMinMax();
overlayEditorPanelJAI.setAllROICompositeImage(roiComposite, OverlayEditorPanelJAI.FRAP_DATA_INIT_PROPERTY);
final JDialog jDialog = new JDialog(JOptionPane.getFrameForComponent(parentComponent));
jDialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
jDialog.setTitle("Geometry Editor (" + sourceDataName + ")");
jDialog.setModal(true);
final JButton cancelJButton = new JButton("Cancel");
cancelJButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final String QUIT_ANSWER = "Quit Geometry Editor";
String result = DialogUtils.showWarningDialog(jDialog, "Confirm cancel", "Quit geometry editor and lose all changes?", new String[] { QUIT_ANSWER, "back" }, QUIT_ANSWER);
if (result != null && result.equals(QUIT_ANSWER)) {
jDialog.dispose();
}
}
});
jDialog.addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
super.windowOpened(e);
if (bHasOriginalData) {
calculateHistogram();
}
updateUndoAfter(false);
if (vcPixelClasses == null) {
askInitialize(false);
}
}
@Override
public void windowClosing(WindowEvent e) {
// TODO Auto-generated method stub
super.windowClosing(e);
cancelJButton.doClick();
}
});
final JPanel okCancelJPanel = new JPanel(new FlowLayout());
JButton okJButton = new JButton(okButtonText);
okJButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
VCImage finalImage = checkAll();
finalImage.setDescription((editedGeometryAttributes != null ? editedGeometryAttributes.annotation : originalAnnotation));
finalGeometryHolder[0] = new Geometry((String) null, finalImage);
finalGeometryHolder[0].getGeometrySpec().setOrigin((editedGeometryAttributes != null ? editedGeometryAttributes.origin : originalOrigin));
finalGeometryHolder[0].getGeometrySpec().setExtent((editedGeometryAttributes != null ? editedGeometryAttributes.extent : originalExtent));
finalGeometryHolder[0].setDescription((editedGeometryAttributes != null ? editedGeometryAttributes.annotation : originalAnnotation));
jDialog.dispose();
} catch (UserCancelException uce) {
} catch (Exception exc) {
DialogUtils.showErrorDialog(overlayEditorPanelJAI, "Error validating compartments.\n" + exc.getMessage(), exc);
}
}
});
JButton attributesJButton = new JButton("Attributes...");
attributesJButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
editedGeometryAttributes = showEditGeometryAttributes(jDialog, editedGeometryAttributes);
} catch (UserCancelException uce) {
// ignore
}
}
});
// JButton surfaceButton = new JButton("View Surfaces...");
// surfaceButton.addActionListener(new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// showDataValueSurfaceViewer(geomAttr);
// }
// });
JButton exportJButton = new JButton("Export...");
exportJButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
export();
}
});
JButton importJButton = new JButton("Import stl...");
importJButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser importJFC = new JFileChooser(ClientRequestManager.getPreferredPath(UserPreferences.getLastUserPreferences()));
importJFC.setDialogTitle("Choose .stl file(s) to import");
importJFC.setMultiSelectionEnabled(true);
int result = importJFC.showOpenDialog(overlayEditorPanelJAI);
if (result == JFileChooser.APPROVE_OPTION) {
File[] selectedFiles = importJFC.getSelectedFiles();
if (selectedFiles != null && selectedFiles.length > 0) {
ClientRequestManager.setPreferredPath(UserPreferences.getLastUserPreferences(), selectedFiles[0]);
Vect3d sampleSize = new Vect3d(getImageDataset()[0].getISize().getX(), getImageDataset()[0].getISize().getY(), getImageDataset()[0].getISize().getZ());
ArrayList<AsynchClientTask> stlImportTasks = getImportSTLtasks(selectedFiles, sampleSize, new Vect3d(0, 0, 0));
ClientTaskDispatcher.dispatch(overlayEditorPanelJAI, new Hashtable<>(), stlImportTasks.toArray(new AsynchClientTask[0]));
} else {
DialogUtils.showErrorDialog(overlayEditorPanelJAI, "Select at least 1 .stl file for import.");
}
}
}
});
okCancelJPanel.add(okJButton);
okCancelJPanel.add(attributesJButton);
okCancelJPanel.add(exportJButton);
okCancelJPanel.add(importJButton);
// okCancelJPanel.add(surfaceButton);
okCancelJPanel.add(cancelJButton);
jDialog.getContentPane().add(overlayEditorPanelJAI, BorderLayout.CENTER);
jDialog.getContentPane().add(okCancelJPanel, BorderLayout.SOUTH);
jDialog.setSize(700, 600);
DialogUtils.showModalJDialogOnTop(jDialog, parentComponent);
if (finalGeometryHolder[0] == null) {
throw UserCancelException.CANCEL_GENERIC;
}
return finalGeometryHolder[0];
}
use of cbit.vcell.render.Vect3d in project vcell by virtualcell.
the class ClientRequestManager method createNewDocument.
/**
* Insert the method's description here. Creation date: (5/10/2004 3:48:16 PM)
*/
public AsynchClientTask[] createNewDocument(final TopLevelWindowManager requester, final VCDocument.DocumentCreationInfo documentCreationInfo) {
// throws UserCancelException, Exception {
/* asynchronous and not blocking any window */
AsynchClientTask[] taskArray = null;
final int createOption = documentCreationInfo.getOption();
switch(documentCreationInfo.getDocumentType()) {
case BIOMODEL_DOC:
{
AsynchClientTask task1 = new AsynchClientTask("creating biomodel", AsynchClientTask.TASKTYPE_NONSWING_BLOCKING) {
@Override
public void run(Hashtable<String, Object> hashTable) throws Exception {
BioModel bioModel = createDefaultBioModelDocument(null);
hashTable.put("doc", bioModel);
}
};
taskArray = new AsynchClientTask[] { task1 };
break;
}
case MATHMODEL_DOC:
{
if ((createOption == VCDocument.MATH_OPTION_NONSPATIAL) || (createOption == VCDocument.MATH_OPTION_SPATIAL_EXISTS)) {
AsynchClientTask task2 = new AsynchClientTask("creating mathmodel", AsynchClientTask.TASKTYPE_NONSWING_BLOCKING) {
@Override
public void run(Hashtable<String, Object> hashTable) throws Exception {
Geometry geometry = null;
if (createOption == VCDocument.MATH_OPTION_NONSPATIAL) {
geometry = new Geometry("Untitled", 0);
} else {
geometry = (Geometry) hashTable.get(GEOMETRY_KEY);
}
MathModel mathModel = createMathModel("Untitled", geometry);
mathModel.setName("MathModel" + (getMdiManager().getNumCreatedDocumentWindows() + 1));
hashTable.put("doc", mathModel);
}
};
if (createOption == VCDocument.MATH_OPTION_SPATIAL_EXISTS) {
AsynchClientTask task1 = createSelectDocTask(requester);
AsynchClientTask task1b = createSelectLoadGeomTask(requester);
taskArray = new AsynchClientTask[] { task1, task1b, task2 };
} else {
taskArray = new AsynchClientTask[] { task2 };
}
break;
} else if (createOption == VCDocument.MATH_OPTION_FROMBIOMODELAPP) {
AsynchClientTask task1 = new AsynchClientTask("select biomodel application", AsynchClientTask.TASKTYPE_SWING_BLOCKING) {
@Override
public void run(Hashtable<String, Object> hashTable) throws Exception {
// spatial or non-spatial
BioModelInfo bioModelInfo = (BioModelInfo) DialogUtils.getDBTreePanelSelection(requester.getComponent(), getMdiManager().getDatabaseWindowManager().getBioModelDbTreePanel(), "Open", "Select BioModel");
if (bioModelInfo != null) {
// may throw UserCancelException
hashTable.put("bioModelInfo", bioModelInfo);
}
}
};
AsynchClientTask task2 = new AsynchClientTask("find sim contexts in biomodel application", AsynchClientTask.TASKTYPE_NONSWING_BLOCKING) {
@Override
public void run(Hashtable<String, Object> hashTable) throws Exception {
// spatial or non-spatial
// Get the simContexts in the corresponding BioModel
BioModelInfo bioModelInfo = (BioModelInfo) hashTable.get("bioModelInfo");
SimulationContext[] simContexts = getDocumentManager().getBioModel(bioModelInfo).getSimulationContexts();
if (simContexts != null) {
// may throw UserCancelException
hashTable.put("simContexts", simContexts);
}
}
};
AsynchClientTask task3 = new AsynchClientTask("create math model from biomodel application", AsynchClientTask.TASKTYPE_SWING_BLOCKING) {
@Override
public void run(Hashtable<String, Object> hashTable) throws Exception {
SimulationContext[] simContexts = (SimulationContext[]) hashTable.get("simContexts");
String[] simContextNames = new String[simContexts.length];
if (simContextNames.length == 0) {
throw new RuntimeException("no application is available");
} else {
for (int i = 0; i < simContexts.length; i++) {
simContextNames[i] = simContexts[i].getName();
}
Component component = requester.getComponent();
// Get the simContext names, so that user can choose which simContext math to
// import
String simContextChoice = (String) PopupGenerator.showListDialog(component, simContextNames, "Please select Application");
if (simContextChoice == null) {
throw UserCancelException.CANCEL_DB_SELECTION;
}
SimulationContext chosenSimContext = null;
for (int i = 0; i < simContexts.length; i++) {
if (simContexts[i].getName().equals(simContextChoice)) {
chosenSimContext = simContexts[i];
break;
}
}
Objects.requireNonNull(chosenSimContext);
BioModelInfo bioModelInfo = (BioModelInfo) hashTable.get("bioModelInfo");
// Get corresponding mathDesc to create new mathModel and return.
String newName = bioModelInfo.getVersion().getName() + "_" + chosenSimContext.getName();
MathDescription bioMathDesc = chosenSimContext.getMathDescription();
MathDescription newMathDesc = null;
newMathDesc = new MathDescription(newName + "_" + (new Random()).nextInt());
newMathDesc.setGeometry(bioMathDesc.getGeometry());
newMathDesc.read_database(new CommentStringTokenizer(bioMathDesc.getVCML_database()));
newMathDesc.isValid();
MathModel newMathModel = new MathModel(null);
newMathModel.setName(newName);
newMathModel.setMathDescription(newMathDesc);
hashTable.put("doc", newMathModel);
}
}
};
taskArray = new AsynchClientTask[] { task1, task2, task3 };
break;
} else {
throw new RuntimeException("Unknown MathModel Document creation option value=" + documentCreationInfo.getOption());
}
}
case GEOMETRY_DOC:
{
if (createOption == VCDocument.GEOM_OPTION_1D || createOption == VCDocument.GEOM_OPTION_2D || createOption == VCDocument.GEOM_OPTION_3D) {
// analytic
AsynchClientTask task1 = new AsynchClientTask("creating analytic geometry", AsynchClientTask.TASKTYPE_NONSWING_BLOCKING) {
@Override
public void run(Hashtable<String, Object> hashTable) throws Exception {
Geometry geometry = new Geometry("Geometry" + (getMdiManager().getNumCreatedDocumentWindows() + 1), documentCreationInfo.getOption());
geometry.getGeometrySpec().addSubVolume(new AnalyticSubVolume("subdomain0", new Expression(1.0)));
geometry.precomputeAll(new GeometryThumbnailImageFactoryAWT());
hashTable.put("doc", geometry);
}
};
taskArray = new AsynchClientTask[] { task1 };
break;
}
if (createOption == VCDocument.GEOM_OPTION_CSGEOMETRY_3D) {
// constructed solid geometry
AsynchClientTask task1 = new AsynchClientTask("creating constructed solid geometry", AsynchClientTask.TASKTYPE_NONSWING_BLOCKING) {
@Override
public void run(Hashtable<String, Object> hashTable) throws Exception {
Geometry geometry = new Geometry("Geometry" + (getMdiManager().getNumCreatedDocumentWindows() + 1), 3);
Extent extent = geometry.getExtent();
if (extent != null) {
// create a CSGPrimitive of type cube and scale it to the 'extent' components.
// Use this as the default or background CSGObject (subdomain).
// This can be considered as the equivalent of subdomain (with expression) 1.0
// for analyticSubvolume.
// basic cube
CSGPrimitive cube = new CSGPrimitive("cube", CSGPrimitive.PrimitiveType.CUBE);
// scaled cube
double x = extent.getX();
double y = extent.getY();
double z = extent.getZ();
CSGScale scaledCube = new CSGScale("scale", new Vect3d(x / 2.0, y / 2.0, z / 2.0));
scaledCube.setChild(cube);
// translated scaled cube
CSGTranslation translatedScaledCube = new CSGTranslation("translation", new Vect3d(x / 2, y / 2, z / 2));
translatedScaledCube.setChild(scaledCube);
CSGObject csgObject = new CSGObject(null, "subdomain0", 0);
csgObject.setRoot(translatedScaledCube);
geometry.getGeometrySpec().addSubVolume(csgObject, false);
geometry.precomputeAll(new GeometryThumbnailImageFactoryAWT());
hashTable.put("doc", geometry);
}
}
};
taskArray = new AsynchClientTask[] { task1 };
break;
} else {
throw new RuntimeException("Unknown Geometry Document creation option value=" + documentCreationInfo.getOption());
}
}
default:
{
throw new RuntimeException("Unknown default document type: " + documentCreationInfo.getDocumentType());
}
}
return taskArray;
}
Aggregations