/*ScianVisMesh.c June 14, 1991 Eric Pepke Routines for mesh visualization object The mesh object takes a scalar field defined over a data form with topological dimension 2. The spatial dimension of the dataset may be either 2 or 3. If the spatial dimension is 2, the Z is assumed to be zero. The mesh visualization object works for regular data forms, curvilinear data forms, and nonstructured data forms. */ #include "Scian.h" #include "ScianTypes.h" #include "ScianArrays.h" #include "ScianWindows.h" #include "ScianTextBoxes.h" #include "ScianObjWindows.h" #include "ScianEvents.h" #include "ScianIcons.h" #include "ScianColors.h" #include "ScianControls.h" #include "ScianLists.h" #include "ScianSpaces.h" #include "ScianSliders.h" #include "ScianIDs.h" #include "ScianDatasets.h" #include "ScianErrors.h" #include "ScianVisObjects.h" #include "ScianVisMesh.h" #include "ScianStyle.h" #include "ScianPictures.h" #include "ScianTitleBoxes.h" #include "ScianButtons.h" #include "ScianMethods.h" #include "ScianDraw.h" ObjPtr meshClass; /*Values for color cells. Don't change these values.*/ #define COLORCELLSNONE 0 #define COLORCELLSGRID 1 #define COLORCELLSDATA 2 static ObjPtr MeshInit(object) ObjPtr object; /*Initializes a mesh object*/ { ObjPtr minMax; real bounds[6]; real xySize, zSize; ObjPtr colorObj, deformObj, mainDataset; int nComponents; MakeVar(object, MAINDATASET); mainDataset = GetVar(object, MAINDATASET); SetVar(object, COLOROBJ, colorObj = mainDataset); if (colorObj) { SetVar(object, COLORS, ObjTrue); } SetVar(object, COLORCELLS, NewInt(1)); SetVar(object, DEFORMOBJ, deformObj = mainDataset); /*Heuristically determine whether to displace Z*/ GetBounds(object, bounds); xySize = bounds[1] - bounds[0]; xySize = MAX(xySize, bounds[3] - bounds[2]); xySize = MAX(xySize, bounds[5] - bounds[4]); SetCurForm(FORMFIELD, deformObj); nComponents = GetNComponents(FORMFIELD); if (nComponents < 3) { MakeVar(deformObj, MINMAX); minMax = GetVar(deformObj, MINMAX); zSize = ((real *) ELEMENTS(minMax))[1] - ((real *) ELEMENTS(minMax))[0]; if (zSize < xySize * 0.5) { SetVar(object, DEFORMSWITCH, NewInt(1)); } else { SetVar(object, DEFORMSWITCH, NewInt(0)); } } else { SetVar(object, DEFORMSWITCH, NewInt(0)); } return ObjTrue; } static void DrawMajorMark(x, yt, yb, selected) int x, yt, yb; Bool selected; /*Draws a major mark at x from yt to yb. Selected if selected.*/ { DrawUILine(x, yt - CCMARKBUTLEN, x, yb, UIBLACK); FillUIRect(x - CCMARKBUTWIDTH / 2, x + CCMARKBUTWIDTH / 2, yt - CCMARKBUTLEN, yt, UIWHITE); if (selected) { FrameUIRect(x - CCMARKBUTWIDTH / 2, x + CCMARKBUTWIDTH / 2 + 1, yt - CCMARKBUTLEN - 1, yt, UIBLACK); FrameUIRect(x - CCMARKBUTWIDTH / 2 - 1, x + CCMARKBUTWIDTH / 2 - 1, yt - CCMARKBUTLEN + 1, yt + 1, UIYELLOW); FrameUIRect(x - CCMARKBUTWIDTH / 2 - 2, x + CCMARKBUTWIDTH / 2, yt - CCMARKBUTLEN, yt + 2, UIYELLOW); } else { FrameUIRect(x - CCMARKBUTWIDTH / 2, x + CCMARKBUTWIDTH / 2, yt - CCMARKBUTLEN, yt, UIBLACK); } } static ObjPtr SetMeshMainDataset(visObj, dataSet) ObjPtr visObj, dataSet; /*Sets the main data set of visObj to dataSet*/ { SetVar(visObj, MAINDATASET, dataSet); return ObjTrue; } static ObjPtr MakeMeshColored(visObject) ObjPtr visObject; /*Makes the mesh colored*/ { SetVar(visObject, PICCOLORED, ObjTrue); if (GetPredicate(visObject, COLORS)) { ObjPtr meshField; ObjPtr colorField; ObjPtr var; ObjPtr surface; ObjPtr palette; /*Get the mesh field and its form*/ meshField = GetObjectVar("MakeMeshColored", visObject, MAINDATASET); if (!meshField) return ObjFalse; /*Get the color field and its form*/ colorField = GetObjectVar("MakeMeshColored", visObject, COLOROBJ); if (!colorField) return ObjFalse; /*Get the surface to color*/ MakeVar(visObject, SURFACE); surface = GetPictureVar("MakeMeshColored", visObject, SURFACE); if (!surface) return ObjFalse; /*See if the mesh form is the same as the color form*/ SetCurForm(FORMFIELD, meshField); SetCurForm(FIELD2, colorField); MakeVar(visObject, CPALETTE); palette = GetPaletteVar("MakeMeshColored", visObject, CPALETTE); if (!palette) { return ObjFalse; } SetPalette(palette); if (IdenticalFields(FORMFIELD, FIELD2)) { /*Yes! It's easy and fast to traverse*/ long info; info = GetDatasetInfo(colorField); if (info & DS_UNSTRUCTURED) { /*It's an unstructured data form*/ ObjPtr faces; /*Faces of the dataform*/ PolysPtr polys; /*The polygons in the surface*/ PolyPtr polyRunner; /*The runner through the polys*/ ThingListPtr cellRunner; /*The runner through the cells*/ ObjPtr field; /*The field itself*/ faces = GetDatasetKEdges(meshField, 2); if (!faces) { return ObjFalse; } /*Look in the polys of the picture*/ polys = (PolysPtr) ((PicPtr) surface) -> items; if (!polys) { return ObjTrue; } if (polys -> item . type != POLYGONS) { ReportError("MakeMeshColored", "Malformed picture"); return ObjFalse; } /*Get the data*/ if (!SetCurField(FIELD1, colorField)) { return ObjFalse; } cellRunner = LISTOF(faces); polyRunner = polys -> polygons; while (cellRunner) { ObjPtr array; int k, d, i, index; real *a; if (!polyRunner) { ReportError("MakeMeshColored", "Picture too short"); break; } array = cellRunner -> thing; d = DIMS(array)[0]; a = ArrayMeat(array); if (d == polyRunner -> nVertices) { for (k = 0; k < d; ++k) { /*Color the vertices of the polygon*/ long indices[1]; i = *a; indices[0] = i; index = GetRealColorIndex( SelectFieldScalar(FIELD1, indices)); polyRunner -> vertices[k] -> colorIndex = index; ++a; } } else { ReportError("MakeMeshColored", "Malformed polygon"); } cellRunner = cellRunner -> next; polyRunner = (PolyPtr) polyRunner -> item . next; } if (cellRunner) { ReportError("MakeMeshColored", "Picture too long"); } } else { /*Same structured dataset*/ int topDim; long curDim[2], iDims[2]; ObjPtr dims; RectMeshPtr rectMesh; real temp[3]; ObjPtr var; int colorCells; Bool reverse; /*It must have dimension 2*/ topDim = GetTopDim(meshField); if (topDim != 2) { ReportError("MakeMeshColored", "Topological dimension must be 2."); return ObjFalse; } /*Get the actual topological dimensions dimension*/ dims = GetDatasetFormDims(meshField); if (!dims || !IsRealArray(dims) || RANK(dims) != 1 || DIMS(dims)[0] != 2) { return ObjFalse; } iDims[0] = ((real *) ELEMENTS(dims))[0]; iDims[1] = ((real *) ELEMENTS(dims))[1]; /*Register the mesh field*/ if (!SetCurField(FIELD1, colorField)) { return ObjFalse; } /*See what kind of color cells it has*/ var = GetIntVar("MakeMeshColored", visObject, COLORCELLS); if (var) { colorCells = GetInt(var); } else { colorCells = 1; } if (colorCells) { real sample; VertexPtr v; int iDim, jDim; reverse = (GetPredicate(visObject, REVERSESENSE) ^ GetPredicate(meshField, ISLEFTHANDED)); if (reverse) { iDim = 1; jDim = 0; } else { iDim = 0; jDim = 1; } /*Get the rectangular mesh*/ rectMesh = (RectMeshPtr) ((PicPtr) surface) -> items; if (!rectMesh) { return ObjFalse; } if (colorCells == COLORCELLSGRID) { /*Color cells between the grid lines*/ for (curDim[iDim] = 0; curDim[iDim] < iDims[iDim]; ++curDim[iDim]) { for (curDim[jDim] = 0; curDim[jDim] < iDims[jDim]; ++curDim[jDim]) { sample = SelectFieldScalar(FIELD1, curDim); v = RectMeshVertex(rectMesh, curDim[iDim], curDim[jDim]); v -> colorIndex = GetRealColorIndex(sample); } } InterpRectCenters(rectMesh); } else if (colorCells == COLORCELLSDATA) { /*Color cells around the data points*/ for (curDim[iDim] = 0; curDim[iDim] < iDims[iDim]; ++curDim[iDim]) { for (curDim[jDim] = 0; curDim[jDim] < iDims[jDim]; ++curDim[jDim]) { sample = SelectFieldScalar(FIELD1, curDim); v = RectMeshCenter(rectMesh, curDim[iDim], curDim[jDim]); v -> colorIndex = GetRealColorIndex(sample); } } InterpRectVertices(rectMesh); } } } } else { ColorPictureByObject(surface, colorField, GetPredicate(visObject, INTERPCOLORS)); } } SetVar(visObject, PICCOLORED, ObjTrue); return ObjTrue; } static ObjPtr MakeMeshSurface(visObject) ObjPtr visObject; /*Makes the surface in a mesh object.*/ { ObjPtr dataset; /*The dataset the vis object represents*/ long datasetFlags; /*Flags of the dataset*/ ObjPtr var; /*Random variable*/ int colorCells; /*What kind of color cells*/ ObjPtr picture; /*The picture to be made*/ dataset = GetObjectVar("MakeMeshSurface", visObject, MAINDATASET); if (!dataset) { return ObjFalse; } datasetFlags = GetDatasetInfo(dataset); if (0 == datasetFlags & DS_HASFORM) { ReportError("MakeMeshSurface", "No data form"); return ObjFalse; } /*See what kind of color cells it has*/ var = GetIntVar("MakeMeshSurface", visObject, COLORCELLS); if (var) { colorCells = GetInt(var); } else { colorCells = 1; } /*Make the new picture*/ picture = NewPicture(); if (!picture) return ObjFalse; if (datasetFlags & DS_UNSTRUCTURED) { /*It's an unstructured dataset.*/ ObjPtr edges; /*Edges of the dataform*/ ObjPtr faces; /*Faces of the dataform*/ TwoReals *edgesArray; /*Array of edges*/ PolysPtr polys; /*The polygons in the surface*/ ThingListPtr runner; /*The runner through the cells*/ VertexPtr *vertices; /*Temporary holding place for vertices*/ long allocVertices; /*Number of vertices allocated*/ long nVertices; /*Total number of vertices*/ VertexPtr *indVertices; /*Indirect vertices*/ long k; /*Index into form*/ ObjPtr dims; /*Dims of the dataset*/ SetCurForm(FORMFIELD, dataset); dims = GetDatasetFormDims(dataset); if (!dims) { ReportError("MakeMeshSurface", "No form dimensions"); return ObjFalse; } nVertices = *((real *) ELEMENTS(dims)); edges = GetDatasetKEdges(dataset, 1); if (!edges) { return ObjFalse; } edgesArray = (TwoReals *) ArrayMeat(edges); faces = GetDatasetKEdges(dataset, 2); if (!faces) { return ObjFalse; } if (colorCells) { /*Make the picture and set it up with one polys*/ polys = AppendPolysToPicture(picture); if (!polys) return ObjFalse; indVertices = (VertexPtr *) Alloc(nVertices * sizeof(VertexPtr)); for (k = 0; k < nVertices; ++k) { indVertices[k] = NewVertex(picture, 0); indVertices[k] -> position[0] = SelectFieldComponent(FORMFIELD, 0, &k); indVertices[k] -> position[1] = SelectFieldComponent(FORMFIELD, 1, &k); indVertices[k] -> position[2] = SelectFieldComponent(FORMFIELD, 2, &k); } allocVertices = 20; vertices = (VertexPtr *) Alloc(20 * sizeof(VertexPtr)); if (!vertices) { return ObjFalse; } runner = LISTOF(faces); while (runner) { ObjPtr array; int k, d, i; real *a; array = runner -> thing; d = DIMS(array)[0]; a = ArrayMeat(array); for (k = 0; k < d; ++k) { i = *a; if (k >= allocVertices) { allocVertices += 20; vertices = (VertexPtr *) Realloc(vertices, allocVertices * sizeof(VertexPtr)); if (!vertices) { return ObjFalse; } } vertices[k] = indVertices[i]; ++a; } /*DIKEO fix to do better vertex allocation*/ AppendSPolyToPolys(polys, d, vertices); runner = runner -> next; } Free(vertices); Free(indVertices); } } else { /*It's a structured dataset.*/ int topDim, nComponents; long curDim[2], iDims[2]; ObjPtr dims; RectMeshPtr rectMesh; real temp[3]; int iDim, jDim; Bool reverse; /*It must have dimension 2*/ topDim = GetTopDim(dataset); if (topDim != 2) { ReportError("MakeMeshSurface", "Topological dimension must be 2."); return ObjFalse; } /*Get the actual topological dimensions*/ dims = GetDatasetFormDims(dataset); if (!dims || !IsRealArray(dims) || RANK(dims) != 1 || DIMS(dims)[0] != 2) { ReportError("MakeMeshSurface", "No topological dimensions"); return ObjFalse; } iDims[0] = ((real *) ELEMENTS(dims))[0]; iDims[1] = ((real *) ELEMENTS(dims))[1]; /*Register the dataset and its dataform*/ if (!SetCurField(FIELD1, dataset)) { return ObjFalse; } if (!SetCurForm(FIELD2, dataset)) { return ObjFalse; } reverse = (GetPredicate(visObject, REVERSESENSE) ^ GetPredicate(dataset, ISLEFTHANDED)); if (reverse) { iDim = 1; jDim = 0; } else { iDim = 0; jDim = 1; } if (colorCells) { /*Create the color cells*/ VertexPtr v; if (colorCells == COLORCELLSGRID) { /*Color cells between the grid lines*/ /*Create a new rectangular mesh*/ rectMesh = AppendRectMeshToPicture(picture, iDims[iDim], iDims[jDim], false); if (!rectMesh) { return ObjFalse; } nComponents = GetNComponents(FIELD2); for (curDim[iDim] = 0; curDim[iDim] < iDims[iDim]; ++curDim[iDim]) { for (curDim[jDim] = 0; curDim[jDim] < iDims[jDim]; ++curDim[jDim]) { v = RectMeshVertex(rectMesh, curDim[iDim], curDim[jDim]); v -> position[0] = SelectFieldComponent(FIELD2, 0, curDim); v -> position[1] = SelectFieldComponent(FIELD2, 1, curDim); v -> position[2] = nComponents >= 3 ? SelectFieldComponent(FIELD2, 2, curDim) : 0.0; } } InterpRectCenters(rectMesh); } else if (colorCells == COLORCELLSDATA) { /*Color cells around the data points*/ /*Create a new rectangular mesh*/ rectMesh = AppendRectMeshToPicture(picture, iDims[iDim] + 1, iDims[jDim] + 1, true); if (!rectMesh) { return ObjFalse; } nComponents = GetNComponents(FIELD2); for (curDim[iDim] = 0; curDim[iDim] < iDims[iDim]; ++curDim[iDim]) { for (curDim[jDim] = 0; curDim[jDim] < iDims[jDim]; ++curDim[jDim]) { v = RectMeshCenter(rectMesh, curDim[iDim], curDim[jDim]); v -> position[0] = SelectFieldComponent(FIELD2, 0, curDim); v -> position[1] = SelectFieldComponent(FIELD2, 1, curDim); v -> position[2] = nComponents >= 3 ? SelectFieldComponent(FIELD2, 2, curDim) : 0.0; } } InterpRectVertices(rectMesh); } } } CalcPictureNormals(picture); SetVar(visObject, SURFACE, picture); SetVar(picture, REPOBJ, visObject); return ObjTrue; } static ObjPtr AddMeshControls(mesh, panelContents) ObjPtr mesh, panelContents; /*Adds controls appropriate to a mesh object to panelContents*/ { ObjPtr titleBox, button, radio, var, corral, icon, name, meshField, mainDataset; ObjPtr textBox, defaultIcon; int width, left, top, bottom, right; ObjPtr control; ObjPtr minMax; real min, max, *elements; width = CWINWIDTH - 2 * CORRALBORDER - CWINCORRALWIDTH; /*Get the mesh field*/ meshField = GetObjectVar("AddMeshControls", mesh, MAINDATASET); if (!meshField) return ObjFalse; while (mainDataset = GetVar(meshField, MAINDATASET)) { meshField = mainDataset; } /*Find its min and max*/ MakeVar(meshField, MINMAX); minMax = GetVar(meshField, MINMAX); if (minMax) { elements = ELEMENTS(minMax); min = elements[0]; max = elements[1]; } else { min = 0.0; max = 1.0; } /*Create the color cells title box*/ right = width; left = right - (CCBUTTONLENGTH + 2 * MINORBORDER); top = CWINHEIGHT - MINORBORDER; bottom = top - (2 * MINORBORDER + 3 * CHECKBOXHEIGHT + 2 * CHECKBOXSPACING + TITLEBOXTOP); titleBox = NewTitleBox(left, right, bottom, top, "Color Cells"); PrefixList(panelContents, titleBox); SetVar(titleBox, PARENT, panelContents); /*Create the radio button group*/ radio = NewRadioButtonGroup("Color Cells Radio"); PrefixList(panelContents, radio); SetVar(radio, PARENT, panelContents); /*Create the buttons*/ left += MINORBORDER; right -= MINORBORDER; top -= TITLEBOXTOP + MINORBORDER; bottom = top - CHECKBOXHEIGHT; button = NewRadioButton(left, right, bottom, top, "None"); AddRadioButton(radio, button); top = bottom - CHECKBOXSPACING; bottom = top - CHECKBOXHEIGHT; button = NewRadioButton(left, right, bottom, top, "Between Grid Lines"); AddRadioButton(radio, button); top = bottom - CHECKBOXSPACING; bottom = top - CHECKBOXHEIGHT; button = NewRadioButton(left, right, bottom, top, "Around Data Points"); AddRadioButton(radio, button); /*Set the radio according to COLORCELLS*/ var = GetIntVar("AddMeshControls", mesh, COLORCELLS); if (!var) { SetVar(mesh, COLORCELLS, NewInt(1)); } AssocDirectControlWithVar(radio, mesh, COLORCELLS); /*Put in the mesh corral at the top left*/ left = MAJORBORDER; top = MAJORBORDER; corral = NewIconCorral(NULLOBJ, left, left + ONECORRALWIDTH, CWINHEIGHT - MAJORBORDER - ONECORRALHEIGHT, CWINHEIGHT - MAJORBORDER, 0); SetVar(corral, SINGLECORRAL, ObjTrue); SetVar(corral, TOPDOWN, ObjTrue); SetVar(corral, NAME, NewString("Mesh Field")); SetVar(corral, HELPSTRING, NewString("This corral shows the dataset that is being used to make \ the mesh display. The locations of color cells and meshs are calculated \ using this field. The color of color cells is calculated using the Color Field, \ available in the Color control set.")); PrefixList(panelContents, corral); SetVar(corral, PARENT, panelContents); SetVar(corral, REPOBJ, mesh); SetMethod(corral, DROPINCONTENTS, DropInMainDatasetCorral); /*Create the mesh source text box*/ textBox = NewTextBox(left, left + ONECORRALWIDTH, CWINHEIGHT - MAJORBORDER - ONECORRALHEIGHT - TEXTBOXSEP - TEXTBOXHEIGHT, CWINHEIGHT - MAJORBORDER - ONECORRALHEIGHT - TEXTBOXSEP, 0, "Mesh Field Text", "Mesh Field"); PrefixList(panelContents, textBox); SetVar(textBox, PARENT, panelContents); SetTextAlign(textBox, CENTERALIGN); /*Put in an icon that represents the field*/ name = GetVar(meshField, NAME); defaultIcon = GetVar(meshField, DEFAULTICON); if (defaultIcon) { icon = NewObject(defaultIcon, 0); SetVar(icon, NAME, name); } else { icon = NewIcon(0, 0, ICONQUESTION, GetString(name)); } SetVar(icon, ICONLOC, NULLOBJ); SetVar(icon, REPOBJ, meshField); DropIconInCorral(corral, icon); return ObjTrue; } void InitMeshes() /*Initializes the mesh object*/ { ObjPtr icon, color; /*Class for a mesh object*/ meshClass = NewObject(visDeformed, 0); AddToReferenceList(meshClass); SetVar(meshClass, NAME, NewString("Mesh")); SetMethod(meshClass, INITIALIZE, MeshInit); SetVar(meshClass, SHINVAL, NewReal(80.0)); SetVar(meshClass, SPECVAL, NewReal(0.2)); SetVar(meshClass, DEFAULTICON, icon = NewObject(visIcon, 0)); SetVar(icon, WHICHICON, NewInt(ICONMESH)); SetVar(icon, NAME, NewString("Mesh")); SetVar(icon, HELPSTRING, NewString("This icon represents a mesh visualization object. \ The mesh object shows shaded mesh surfaces of 2-dimensional \ scalar fields defined over structured or nonstructured grids.")); DeclareIndirectDependency(meshClass, SURFACE, MAINDATASET, CHANGED); DeclareDependency(meshClass, SURFACE, COLORCELLS); SetMethod(meshClass, SURFACE, MakeMeshSurface); DeclareDependency(meshClass, SURFACE, REVERSESENSE); SetMethod(meshClass, PICCOLORED, MakeMeshColored); SetMethod(meshClass, SETMAINDATASET, SetMeshMainDataset); SetMethod(meshClass, ADDCONTROLS, AddMeshControls); icon = NewIcon(0, 0, ICONMESH, "Mesh"); SetVar(meshClass, CONTROLICON, icon); DefineVisMapping(DS_HASFORM | DS_HASFIELD, 2, 3, 1, meshClass); DefineVisMapping(DS_HASFORM | DS_HASFIELD, 2, 2, 1, meshClass); DefineVisMapping(DS_HASFORM | DS_HASFIELD | DS_UNSTRUCTURED, 2, 3, 1, meshClass); DefineVisMapping(DS_HASFORM | DS_HASFIELD | DS_UNSTRUCTURED, 2, 2, 1, meshClass); } void KillMeshes() /*Kills the mesh*/ { DeleteThing(meshClass); }