public void SetColors(uint bubbleValueId, FieldDerivativeType dt, FEWorld world, IColorMap colorMap) { FieldValue fv = world.GetFieldValue(bubbleValueId); System.Diagnostics.Debug.Assert(fv.IsBubble == true); uint quantityId = fv.QuantityId; var mesh = world.Mesh; MeshType meshType; int[] vertexs; mesh.GetConnectivity(MeshId, out meshType, out vertexs); if (Type == ElementType.Tri) { Colors = new float[ElemCount * 3]; for (int iTri = 0; iTri < ElemCount; iTri++) { // Bubble uint feId = world.GetTriangleFEIdFromMesh(quantityId, MeshId, (uint)iTri); System.Diagnostics.Debug.Assert(feId != 0); double value = fv.GetShowValue((int)(feId - 1), 0, dt); var color = colorMap.GetColor(value); for (int iColor = 0; iColor < 3; iColor++) { Colors[iTri * 3 + iColor] = (float)color[iColor]; } } } else if (Type == ElementType.Quad) { // TRIと同じでよいが要素IDを取得するメソッドが現状ない throw new NotImplementedException(); } }
public EdgeFieldDrawer(uint valueId, FieldDerivativeType valueDt, bool isntDisplacementValue, bool isDrawInnerEdge, FEWorld world) { Set(valueId, valueDt, isntDisplacementValue, isDrawInnerEdge, world); }
public FaceFieldDrawer(uint valueId, FieldDerivativeType valueDt, bool isntDisplacementValue, FEWorld world, uint colorValueId, FieldDerivativeType colorValueDt, double min, double max) { ColorMap = new ColorMap(min, max); Set(valueId, valueDt, isntDisplacementValue, world, colorValueId, colorValueDt); }
public void UpdateBubbleFieldValueValuesFromNodeValues( uint valueId, FieldDerivativeType dt, double[] nodeValues) { System.Diagnostics.Debug.Assert(FieldValueArray.IsObjectId(valueId)); FieldValue fv = FieldValueArray.GetObject(valueId); uint quantityId = fv.QuantityId; uint dof = fv.Dof; System.Diagnostics.Debug.Assert(fv.Dof == GetDof(quantityId)); double[] values = fv.GetDoubleValues(dt); uint coCnt = GetCoordCount(quantityId); uint offsetNode = 0; for (uint qId = 0; qId < quantityId; qId++) { offsetNode += GetNodeCount(qId) * GetDof(qId); } IList <uint> feIds = GetTriangleFEIds(quantityId); foreach (uint feId in feIds) { TriangleFE triFE = GetTriangleFE(quantityId, feId); int[] coIds = triFE.NodeCoordIds; uint elemNodeCnt = triFE.NodeCount; double[] bubbleValue = new double[dof]; for (int iNode = 0; iNode < elemNodeCnt; iNode++) { int coId = coIds[iNode]; int nodeId = Coord2Node(quantityId, coId); if (nodeId == -1) { //for (int iDof = 0; iDof < dof; iDof++) //{ // bubbleValue[iDof] += 0; //} } else { for (int iDof = 0; iDof < dof; iDof++) { bubbleValue[iDof] += nodeValues[offsetNode + nodeId * dof + iDof]; } } } for (int iDof = 0; iDof < dof; iDof++) { bubbleValue[iDof] /= (double)elemNodeCnt; } for (int iDof = 0; iDof < dof; iDof++) { values[(feId - 1) * dof + iDof] = bubbleValue[iDof]; } } }
public double[] GetDoubleValue(int coId, FieldDerivativeType dt) { double[] values = GetDoubleValues(dt); double[] value = new double[Dof]; for (int iDof = 0; iDof < Dof; iDof++) { value[iDof] = values[coId * Dof + iDof]; } return(value); }
public System.Numerics.Complex[] GetComplexValue(int coId, FieldDerivativeType dt) { System.Numerics.Complex[] values = GetComplexValues(dt); System.Numerics.Complex[] value = new System.Numerics.Complex[Dof]; for (int iDof = 0; iDof < Dof; iDof++) { value[iDof] = values[coId * Dof + iDof]; } return(value); }
public FieldValue(uint quantityId, FieldValueType type, FieldDerivativeType dt, bool isBubble, FieldShowType showType, uint pointCnt) { QuantityId = quantityId; Type = type; Dof = GetDof(Type); DerivativeType = dt; IsBubble = isBubble; ShowType = showType; AllocValues(pointCnt); }
public void UpdateFieldValueValuesFromCoordValues( uint valueId, FieldDerivativeType dt, System.Numerics.Complex[] coordValues) { System.Diagnostics.Debug.Assert(FieldValueArray.IsObjectId(valueId)); FieldValue fv = FieldValueArray.GetObject(valueId); //uint quantityId = fv.QuantityId; //uint dof = fv.Dof; System.Numerics.Complex[] values = fv.GetComplexValues(dt); System.Diagnostics.Debug.Assert(values.Length == coordValues.Length); coordValues.CopyTo(values, 0); }
private void UpdateSymmetricTensor2(uint valueId, FieldDerivativeType dt, FEWorld world) { ValueDof = 6; FieldValue fv = world.GetFieldValue(valueId); uint quantityId = fv.QuantityId; uint dof = fv.Dof; System.Diagnostics.Debug.Assert(fv.IsBubble == true); var mesh = world.Mesh; MeshType meshType; int[] vertexs; mesh.GetConnectivity(MeshId, out meshType, out vertexs); if (Type == ElementType.Tri) { Values = new double[ElemCount * ValueDof]; for (int iTri = 0; iTri < ElemCount; iTri++) { // Bubble uint feId = world.GetTriangleFEIdFromMesh(quantityId, MeshId, (uint)iTri); System.Diagnostics.Debug.Assert(feId != 0); double[] sigma = new double[dof]; for (int iDof = 0; iDof < dof; iDof++) { sigma[iDof] = fv.GetShowValue((int)(feId - 1), iDof, dt); } double[] vecs; double ls; double[] vecl; double ll; GetPrincipalStressVectorForSymmetricTensor2(sigma, out vecs, out ls, out vecl, out ll); Values[iTri * ValueDof + 0] = vecs[0]; Values[iTri * ValueDof + 1] = vecs[1]; Values[iTri * ValueDof + 2] = ls; Values[iTri * ValueDof + 3] = vecl[0]; Values[iTri * ValueDof + 4] = vecl[1]; Values[iTri * ValueDof + 5] = ll; } } else if (Type == ElementType.Quad) { // TRIと同じでよいが要素IDを取得するメソッドが現状ない throw new NotImplementedException(); } }
public void Update(uint valueId, FieldDerivativeType dt, VectorFieldDrawerType drawerType, FEWorld world) { DrawerType = drawerType; if (DrawerType == VectorFieldDrawerType.Vector) { UpdateVector(valueId, dt, world); } else if (DrawerType == VectorFieldDrawerType.SymmetricTensor2) { UpdateSymmetricTensor2(valueId, dt, world); } else { throw new NotImplementedException(); } }
public double GetShowValue(int coId, int iDof, FieldDerivativeType dt) { double value = 0; switch (ShowType) { case FieldShowType.Real: { double[] values = GetDoubleValues(dt); value = values[coId * Dof + iDof]; } break; case FieldShowType.Abs: { double[] values = GetDoubleValues(dt); value = Math.Abs(values[coId * Dof + iDof]); } break; case FieldShowType.ZReal: { System.Numerics.Complex[] values = GetComplexValues(dt); value = values[coId * Dof].Real; } break; case FieldShowType.ZImaginary: { System.Numerics.Complex[] values = GetComplexValues(dt); value = values[coId * Dof].Imaginary; } break; case FieldShowType.ZAbs: { System.Numerics.Complex[] values = GetComplexValues(dt); value = values[coId * Dof].Magnitude; } break; } return(value); }
public void GetMinMaxShowValue(out double min, out double max, int iDof, FieldDerivativeType dt) { min = Double.MaxValue; max = Double.MinValue; uint ptCnt = GetPointCount(); for (int coId = 0; coId < ptCnt; coId++) { double value = GetShowValue(coId, iDof, dt); if (value < min) { min = value; } if (value > max) { max = value; } } }
public System.Numerics.Complex[] GetComplexValues(FieldDerivativeType dt) { System.Numerics.Complex[] values = null; if (dt.HasFlag(FieldDerivativeType.Value) && ComplexValues != null) { values = ComplexValues; } else if (dt.HasFlag(FieldDerivativeType.Velocity) && ComplexVelocityValues != null) { values = ComplexVelocityValues; } else if (dt.HasFlag(FieldDerivativeType.Acceleration) && ComplexAccelerationValues != null) { values = ComplexAccelerationValues; } else { System.Diagnostics.Debug.Assert(false); } return(values); }
public double[] GetDoubleValues(FieldDerivativeType dt) { double[] values = null; if (dt.HasFlag(FieldDerivativeType.Value) && DoubleValues != null) { values = DoubleValues; } else if (dt.HasFlag(FieldDerivativeType.Velocity) && DoubleVelocityValues != null) { values = DoubleVelocityValues; } else if (dt.HasFlag(FieldDerivativeType.Acceleration) && DoubleAccelerationValues != null) { values = DoubleAccelerationValues; } else { System.Diagnostics.Debug.Assert(false); } return(values); }
public void UpdateBubbleFieldValueValuesFromCoordValues( uint valueId, FieldDerivativeType dt, double[] coordValues) { System.Diagnostics.Debug.Assert(FieldValueArray.IsObjectId(valueId)); FieldValue fv = FieldValueArray.GetObject(valueId); uint quantityId = fv.QuantityId; uint dof = fv.Dof; double[] values = fv.GetDoubleValues(dt); uint coCnt = GetCoordCount(quantityId); System.Diagnostics.Debug.Assert(coCnt * dof == coordValues.Length); IList <uint> feIds = GetTriangleFEIds(quantityId); foreach (uint feId in feIds) { TriangleFE triFE = GetTriangleFE(quantityId, feId); int[] coIds = triFE.NodeCoordIds; uint elemNodeCnt = triFE.NodeCount; double[] bubbleValue = new double[dof]; for (int iNode = 0; iNode < elemNodeCnt; iNode++) { int coId = coIds[iNode]; for (int iDof = 0; iDof < dof; iDof++) { bubbleValue[iDof] += coordValues[coId * dof + iDof]; } } for (int iDof = 0; iDof < dof; iDof++) { bubbleValue[iDof] /= (double)elemNodeCnt; } for (int iDof = 0; iDof < dof; iDof++) { values[(feId - 1) * dof + iDof] = bubbleValue[iDof]; } } }
private void Set(uint valueId, FieldDerivativeType valueDt, FEWorld world) { System.Diagnostics.Debug.Assert(world.IsFieldValueId(valueId)); ValueId = valueId; var mesh = world.Mesh; uint dim = world.Dimension; { if (dim == 2) { SutableRotMode = RotMode.RotMode2D; } else if (dim == 3) { SutableRotMode = RotMode.RotMode3D; } } FieldValue fv = world.GetFieldValue(valueId); if (fv.Type == FieldValueType.Vector2 || fv.Type == FieldValueType.Vector3) { Type = VectorFieldDrawerType.Vector; } else if (fv.Type == FieldValueType.SymmetricTensor2) { Type = VectorFieldDrawerType.SymmetricTensor2; } { DrawParts.Clear(); IList <uint> meshIds = mesh.GetIds(); foreach (uint meshId in meshIds) { VectorFieldDrawPart dp = new VectorFieldDrawPart(meshId, world); DrawParts.Add(dp); } } Update(world); }
public uint AddFieldValue(FieldValueType fieldType, FieldDerivativeType derivativeType, uint quantityId, bool isBubble, FieldShowType showType) { uint pointCnt = 0; if (isBubble) { pointCnt = (uint)GetTriangleFEIds(quantityId).Count; } else { pointCnt = GetCoordCount(quantityId); } FieldValue fv = new FieldValue(quantityId, fieldType, derivativeType, isBubble, showType, pointCnt); uint freeId = FieldValueArray.GetFreeObjectId(); uint valueId = FieldValueArray.AddObject(freeId, fv); System.Diagnostics.Debug.Assert(valueId == freeId); return(valueId); }
private void UpdateVector(uint valueId, FieldDerivativeType dt, FEWorld world) { ValueDof = 2; FieldValue fv = world.GetFieldValue(valueId); uint quantityId = fv.QuantityId; uint dof = fv.Dof; System.Diagnostics.Debug.Assert(fv.IsBubble == true); var mesh = world.Mesh; MeshType meshType; int[] vertexs; mesh.GetConnectivity(MeshId, out meshType, out vertexs); if (Type == ElementType.Tri) { Values = new double[ElemCount * ValueDof]; for (int iTri = 0; iTri < ElemCount; iTri++) { // Bubble uint feId = world.GetTriangleFEIdFromMesh(quantityId, MeshId, (uint)iTri); System.Diagnostics.Debug.Assert(feId != 0); System.Diagnostics.Debug.Assert(dof >= ValueDof); for (int iDof = 0; iDof < ValueDof; iDof++) { double u = fv.GetShowValue((int)(feId - 1), iDof, dt); Values[iTri * ValueDof + iDof] = u; } } } else if (Type == ElementType.Quad) { // TRIと同じでよいが要素IDを取得するメソッドが現状ない throw new NotImplementedException(); } }
public void UpdateFieldValueValuesFromNodeValues( uint valueId, FieldDerivativeType dt, double[] nodeValues) { System.Diagnostics.Debug.Assert(FieldValueArray.IsObjectId(valueId)); FieldValue fv = FieldValueArray.GetObject(valueId); uint quantityId = fv.QuantityId; uint dof = fv.Dof; System.Diagnostics.Debug.Assert(fv.Dof == GetDof(quantityId)); double[] values = fv.GetDoubleValues(dt); uint coCnt = GetCoordCount(quantityId); uint offsetNode = 0; for (uint qId = 0; qId < quantityId; qId++) { offsetNode += GetNodeCount(qId) * GetDof(qId); } for (int coId = 0; coId < coCnt; coId++) { int nodeId = Coord2Node(quantityId, coId); if (nodeId == -1) { for (int iDof = 0; iDof < dof; iDof++) { values[coId * dof + iDof] = 0; } } else { for (int iDof = 0; iDof < dof; iDof++) { values[coId * dof + iDof] = nodeValues[offsetNode + nodeId * dof + iDof]; } } } }
private void Set(uint valueId, FieldDerivativeType valueDt, bool isntDisplacementValue, bool isDrawInnerEdge, FEWorld world) { var mesh = world.Mesh; if (!world.IsFieldValueId(valueId)) { throw new ArgumentException(); //return; } ValueId = valueId; ValueDt = valueDt; IsntDisplacementValue = isntDisplacementValue; var fv = world.GetFieldValue(ValueId); // 線要素を生成 uint quantityId = fv.QuantityId; if (isDrawInnerEdge) { // 内部の全ての辺を描画 LineFEs = world.MakeBoundOfElements(quantityId); } else { // 境界の辺だけ描画 LineFEs = new List <LineFE>(); IList <MeshBarArray> barArrays = mesh.GetBarArrays(); foreach (MeshBarArray barArray in barArrays) { uint eCadId = barArray.ECadId; IList <int> allCoIds = world.GetCoordIdsFromCadId(quantityId, eCadId, CadElementType.Edge); for (int i = 0; i < (allCoIds.Count - 1); i++) { int workFEOrder = 1; LineFE lineFE = new LineFE(workFEOrder); { int[] coIds = { allCoIds[i], allCoIds[i + 1] }; lineFE.SetVertexCoordIds(coIds); lineFE.SetNodeCoordIds(coIds); } LineFEs.Add(lineFE); } } } int feOrder; { LineFE lineFE = LineFEs[0]; // 先頭の要素 feOrder = lineFE.Order; LinePtCount = lineFE.NodeCount; } uint ptCnt = LineCount * LinePtCount; uint dim = world.Dimension; uint drawDim; if (!IsntDisplacementValue && dim == 2 && (fv.Type == FieldValueType.Scalar || fv.Type == FieldValueType.ZScalar)) { drawDim = 3; } else { drawDim = dim; } VertexArray.SetSize(ptCnt, drawDim); if (drawDim == 2) { SutableRotMode = RotMode.RotMode2D; } else if (dim == 3) { SutableRotMode = RotMode.RotMode3D; } else { SutableRotMode = RotMode.RotMode2DH; } Update(world); }
public void Update(FEWorld world) { FieldValue fv = world.GetFieldValue(ValueId); uint quantityId = fv.QuantityId; uint dim = world.Dimension; uint ptCnt = 0; if (IsNsvDraw) { ptCnt = fv.GetPointCount(); // あとで実装する throw new NotImplementedException(); } else { ptCnt = world.GetCoordCount(quantityId); } System.Diagnostics.Debug.Assert(VertexArray.PointCount == ptCnt); if (!IsntDisplacementValue) { // 変位を伴う場合 if (dim == 2 && (fv.Type == FieldValueType.Scalar || fv.Type == FieldValueType.ZScalar)) { // 垂直方向の変位として捉える System.Diagnostics.Debug.Assert(VertexArray.Dimension == 3); for (int coId = 0; coId < ptCnt; coId++) { double[] coord = world.GetCoord(quantityId, coId); FieldDerivativeType dt = ValueDt; double value = fv.GetShowValue(coId, 0, dt); VertexArray.VertexCoordArray[coId * 3 + 0] = coord[0]; VertexArray.VertexCoordArray[coId * 3 + 1] = coord[1]; VertexArray.VertexCoordArray[coId * 3 + 2] = value; } } else { System.Diagnostics.Debug.Assert(VertexArray.Dimension == dim); for (int coId = 0; coId < ptCnt; coId++) { double[] coord = world.GetCoord(quantityId, coId); FieldDerivativeType dt = ValueDt; for (int iDim = 0; iDim < dim; iDim++) { double value = fv.GetShowValue(coId, iDim, dt); VertexArray.VertexCoordArray[coId * dim + iDim] = coord[iDim] + value; } } } } else { System.Diagnostics.Debug.Assert(VertexArray.Dimension == dim); for (int coId = 0; coId < ptCnt; coId++) { double[] coord = world.GetCoord(quantityId, coId); for (int iDim = 0; iDim < dim; iDim++) { VertexArray.VertexCoordArray[coId * dim + iDim] = coord[iDim]; } } } if (world.IsFieldValueId(ColorValueId)) { FieldValue colorfv = world.GetFieldValue(ColorValueId); FieldDerivativeType dt = ColorValueDt; bool isBubble = colorfv.IsBubble; uint colorQuantityId = colorfv.QuantityId; uint colorPtCnt = world.GetCoordCount(colorQuantityId); if (!ColorMap.IsFixedMinMax) { double min; double max; colorfv.GetMinMaxShowValue(out min, out max, 0, dt); ColorMap.MinValue = min; ColorMap.MaxValue = max; } if (!isBubble) { // color is assigned to vertex if (ColorArray == null) { ColorArray = new float[colorPtCnt * 4]; } for (int coId = 0; coId < colorPtCnt; coId++) { double value = colorfv.GetShowValue(coId, 0, dt); double[] color = ColorMap.GetColor(value); ColorArray[coId * 4] = (float)color[0]; ColorArray[coId * 4 + 1] = (float)color[1]; ColorArray[coId * 4 + 2] = (float)color[2]; ColorArray[coId * 4 + 3] = 0.0f; } } else { // color is assigned to face for (int idp = 0; idp < DrawParts.Count; idp++) { FaceFieldDrawPart dp = DrawParts[idp]; dp.SetColors(ColorValueId, dt, world, ColorMap); } } } if (NormalArray != null) { MakeNormal(); } if (UVArray != null) { for (int coId = 0; coId < ptCnt; coId++) { double[] coord = world.GetCoord(quantityId, coId); UVArray[coId * 2 + 0] = coord[0] * TexScale; UVArray[coId * 2 + 1] = coord[1] * TexScale; } } }
private void Set(uint valueId, FieldDerivativeType valueDt, bool isntDisplacementValue, FEWorld world, uint colorValueId, FieldDerivativeType colorValueDt) { var mesh = world.Mesh; if (!world.IsFieldValueId(valueId)) { throw new ArgumentException(); //return; } if (world.IsFieldValueId(colorValueId)) { IsColorLegendDraw = true; } else { IsColorLegendDraw = false; } ValueId = valueId; ValueDt = valueDt; ColorValueId = colorValueId; ColorValueDt = colorValueDt; IsntDisplacementValue = isntDisplacementValue; var fv = world.GetFieldValue(valueId); uint quantityId = fv.QuantityId; uint dim = world.Dimension; uint ptCnt = 0; if (IsNsvDraw) { ptCnt = fv.GetPointCount(); // あとで実装する throw new NotImplementedException(); } else { ptCnt = world.GetCoordCount(quantityId); } uint drawDim; if (!IsntDisplacementValue && dim == 2 && (fv.Type == FieldValueType.Scalar || fv.Type == FieldValueType.ZScalar)) { drawDim = 3; } else { drawDim = dim; } VertexArray.SetSize(ptCnt, drawDim); { bool isNormal = (NormalArray != null); if (NormalArray != null) { NormalArray = null; } if (isNormal) { NormalArray = new double[ptCnt * 3]; } } { bool isUv = (UVArray != null); if (UVArray != null) { UVArray = null; } if (isUv) { UVArray = new double[ptCnt * 2]; } } if (drawDim == 2) { SutableRotMode = RotMode.RotMode2D; } else if (dim == 3) { SutableRotMode = RotMode.RotMode3D; } else { SutableRotMode = RotMode.RotMode2DH; } { DrawParts.Clear(); IList <uint> meshIds = mesh.GetIds(); foreach (uint meshId in meshIds) { if (IsNsvDraw) { // あとで実装する throw new NotImplementedException(); } else { } FaceFieldDrawPart dp = new FaceFieldDrawPart(meshId, world, valueId); DrawParts.Add(dp); } } Update(world); }
public VectorFieldDrawer(uint valueId, FieldDerivativeType valueDt, FEWorld world) { Set(valueId, valueDt, world); }
public void Update(FEWorld world) { FieldValue fv = world.GetFieldValue(ValueId); uint quantityId = fv.QuantityId; uint dim = world.Dimension; uint ptCnt = LineCount * LinePtCount; uint lineCnt = LineCount; uint drawDim = VertexArray.Dimension; if (IsntDisplacementValue) { System.Diagnostics.Debug.Assert(drawDim == 2); // いまはそうなってる for (int iEdge = 0; iEdge < lineCnt; iEdge++) { LineFE lineFE = LineFEs[iEdge]; for (int iPt = 0; iPt < LinePtCount; iPt++) { int coId = lineFE.NodeCoordIds[iPt]; double[] co = world.GetCoord(quantityId, coId); VertexArray.VertexCoordArray[(iEdge * LinePtCount + iPt) * drawDim + 0] = co[0]; VertexArray.VertexCoordArray[(iEdge * LinePtCount + iPt) * drawDim + 1] = co[1]; } } } else { // 変位を伴う場合 if (dim == 2 && drawDim == 3) { for (int iEdge = 0; iEdge < LineCount; iEdge++) { LineFE lineFE = LineFEs[iEdge]; System.Diagnostics.Debug.Assert(lineFE.NodeCoordIds.Length == LinePtCount); for (int iPt = 0; iPt < LinePtCount; iPt++) { int coId = lineFE.NodeCoordIds[iPt]; double[] coord = world.GetCoord(quantityId, coId); FieldDerivativeType dt = ValueDt; double value = fv.GetShowValue(coId, 0, dt); VertexArray.VertexCoordArray[(iEdge * LinePtCount + iPt) * drawDim + 0] = coord[0]; VertexArray.VertexCoordArray[(iEdge * LinePtCount + iPt) * drawDim + 1] = coord[1]; VertexArray.VertexCoordArray[(iEdge * LinePtCount + iPt) * drawDim + 2] = value; } } } else { for (int iEdge = 0; iEdge < lineCnt; iEdge++) { LineFE lineFE = LineFEs[iEdge]; System.Diagnostics.Debug.Assert(lineFE.NodeCoordIds.Length == LinePtCount); for (int iPt = 0; iPt < LinePtCount; iPt++) { int coId = lineFE.NodeCoordIds[iPt]; double[] coord = world.GetCoord(quantityId, coId); FieldDerivativeType dt = ValueDt; for (int iDim = 0; iDim < drawDim; iDim++) { double value = fv.GetShowValue(coId, iDim, dt); VertexArray.VertexCoordArray[(iEdge * LinePtCount + iPt) * drawDim + iDim] = coord[iDim] + value; } } } } } }
public FaceFieldDrawer(uint valueId, FieldDerivativeType valueDt, bool isntDisplacementValue, FEWorld world, uint colorValueId = 0, FieldDerivativeType colorValueDt = FieldDerivativeType.Value) { Set(valueId, valueDt, isntDisplacementValue, world, colorValueId, colorValueDt); }