|
|
|
|
|
#pragma once
|
|
|
|
|
|
#include "dllExport.h"
|
|
|
|
|
|
#include "XJPoint2D.h"
|
|
|
|
|
|
#include "XJPoint3D.h"
|
|
|
|
|
|
#include "Isopleth.h"
|
|
|
|
|
|
#include "ControlPoint2d.h"
|
|
|
|
|
|
#include "ColorItem.h"
|
|
|
|
|
|
#include "PLineList.h"
|
|
|
|
|
|
#include "FaultPLine.h"
|
|
|
|
|
|
#include "kdtree.hpp"
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
#include <mutex>
|
|
|
|
|
|
#include "RectificationMeshWell.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define FAULT_Z 1
|
|
|
|
|
|
#define INVALID_Z 2
|
|
|
|
|
|
#define FAULT_OPEN 4
|
|
|
|
|
|
#define HIDE_Z 7
|
|
|
|
|
|
#define FIXED_Z 8
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct IntPos
|
|
|
|
|
|
{
|
|
|
|
|
|
IntPos(uint64_t x = 0, uint64_t y = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ix = x;
|
|
|
|
|
|
iy = y;
|
|
|
|
|
|
}
|
|
|
|
|
|
uint64_t ix;
|
|
|
|
|
|
uint64_t iy;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PeriodicLattice
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
PeriodicLattice();
|
|
|
|
|
|
virtual ~PeriodicLattice();
|
|
|
|
|
|
|
|
|
|
|
|
/***************<2A><>ȡ<EFBFBD>ļ<EFBFBD>*********************/
|
|
|
|
|
|
bool ReadGRD(const std::string& sFileName);
|
|
|
|
|
|
|
|
|
|
|
|
bool ReadKEV(const std::string& sFileName, bool isOffset);
|
|
|
|
|
|
|
|
|
|
|
|
/***************д<>ļ<EFBFBD>*********************/
|
|
|
|
|
|
bool WriteGRD(const std::string& sFileName);
|
|
|
|
|
|
|
|
|
|
|
|
bool WriteKEV(const std::string& sFileName);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD><EFBFBD>Zֵ<5A><D6B5>Χ
|
|
|
|
|
|
void SetZRange(double zMin, double zMax);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
int Create(unsigned int numx, unsigned int numy, double x0, double y0, double dx, double dy, double* values);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫģ<C9AB><C4A3>
|
|
|
|
|
|
void SetColor(const std::vector<ColorItem>& colorList);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD>õ<EFBFBD>ֵ<EFBFBD><D6B5>
|
|
|
|
|
|
void SetContourList(const std::vector<PlineList>& contourList);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD>öϲ<C3B6><CFB2><EFBFBD>
|
|
|
|
|
|
void SetFaultList(const std::vector<PlineList>& faultList, bool isOffset);
|
|
|
|
|
|
void SetFaultFootwall(const PlineList& footwalls);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD>Ƶ<EFBFBD>
|
|
|
|
|
|
void SetControlPointList(const std::vector<ControlPoint2d>& pointList);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F>߽<EFBFBD>
|
|
|
|
|
|
void SetBound(const PlineList& bound);
|
|
|
|
|
|
void SetOtherLines(const PlineList& lines);
|
|
|
|
|
|
|
|
|
|
|
|
/********************̧<><CCA7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѹ<EFBFBD><D1B9>ͿĨ<CDBF><C4A8>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>**********************/
|
|
|
|
|
|
void StretchlUp(const Point2D& cen, float rad, float per, int type, float wellrad);
|
|
|
|
|
|
|
|
|
|
|
|
void DuabAway(const Point2D& cen1, const Point2D& cen2, float rad, float per, int type, float wellrad);
|
|
|
|
|
|
|
|
|
|
|
|
void Smooth3x3(const Point2D& cen, float rad, float per, int type, float wellrad);
|
|
|
|
|
|
void Smooth5x5(const Point2D& cen, float rad, float per, int type, float wellrad);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD>Ϸ<EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
*
|
|
|
|
|
|
* \param expression <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD>exprtk <EFBFBD><EFBFBD>֧<EFBFBD>ֵĸ<EFBFBD>ʽ<EFBFBD><EFBFBD>https://github.com/ArashPartow/exprtk
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CompileExpressionZ(const std::string& expressionString);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* <EFBFBD><EFBFBD><EFBFBD>ݱ<EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> z ֵ
|
|
|
|
|
|
*
|
|
|
|
|
|
* \param expression <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD>exprtk <EFBFBD><EFBFBD>֧<EFBFBD>ֵĸ<EFBFBD>ʽ<EFBFBD><EFBFBD>https://github.com/ArashPartow/exprtk
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CalculateZByExpression(const std::string& expressionString);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD>µ<EFBFBD>ֵ<EFBFBD><D6B5>
|
|
|
|
|
|
void UpdateIsopleths();
|
|
|
|
|
|
void UpdateIsopleths_MultiThread();
|
|
|
|
|
|
|
|
|
|
|
|
//<2F>ϲ<EFBFBD><CFB2><EFBFBD><EFBFBD>и<EFBFBD><D0B8><EFBFBD>ֵ<EFBFBD><D6B5>
|
|
|
|
|
|
void CutIsopleths();
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD>¿<EFBFBD><C2BF>Ƶ<EFBFBD>
|
|
|
|
|
|
void UpdateWellPoints();
|
|
|
|
|
|
|
|
|
|
|
|
/*****************<2A><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>************************/
|
|
|
|
|
|
unsigned int GetRawCount() { return static_cast<unsigned int>(m_row); }
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int GetColCount() { return static_cast<unsigned int>(m_col); }
|
|
|
|
|
|
|
|
|
|
|
|
Point2D GetMinPoint() { return m_minPoint; }
|
|
|
|
|
|
|
|
|
|
|
|
Point2D GetMaxPoint() { return m_maxPoint; }
|
|
|
|
|
|
|
|
|
|
|
|
void GetVertexColors(std::vector<float>& colorData);
|
|
|
|
|
|
void GetVertexColors2(std::vector<float>& colorData);
|
|
|
|
|
|
void GetDirtyColorData(unsigned& minX, unsigned& maxX,
|
|
|
|
|
|
unsigned& minY, unsigned& maxY,
|
|
|
|
|
|
std::vector<float>& colors) const;
|
|
|
|
|
|
|
|
|
|
|
|
void GetFaultData(std::vector<std::vector<Point2D>>& faults, std::vector<bool>& vClosed) const;
|
|
|
|
|
|
|
|
|
|
|
|
//<2F>ϲ<EFBFBD><CFB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
const std::vector<Polyline2d>& GetFaultFootwallData() const;
|
|
|
|
|
|
|
|
|
|
|
|
void GetControlPointData(std::vector<Point3D>& cps, std::vector<std::string>& names);
|
|
|
|
|
|
const std::vector<ControlPoint2d>& GetControlPointData() const;
|
|
|
|
|
|
void ClearWellPointsDirty();
|
|
|
|
|
|
|
|
|
|
|
|
const std::vector<Polyline2d>& GetBoundaryData() const;
|
|
|
|
|
|
const std::vector<Polyline2d>& GetOtherLineData() const;
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><>ȡ<EFBFBD><C8A1>ֵ<EFBFBD><D6B5>
|
|
|
|
|
|
void GetDirtyLays(std::vector<unsigned int>& layList) const;
|
|
|
|
|
|
void GetDirtyPlineEdges(unsigned int lay, std::vector<std::vector<Point2D>>& edges);
|
|
|
|
|
|
double GetLayZValue(unsigned int lay);
|
|
|
|
|
|
bool IsLayMarked(unsigned int lay);
|
|
|
|
|
|
void GetContourList(std::vector<PlineList>& contourList);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><>ȡZֵ
|
|
|
|
|
|
double GetZValue(uint64_t idx) const { return m_data[idx]; }
|
|
|
|
|
|
void SetZValue(uint64_t idx, double val) {
|
|
|
|
|
|
m_data[idx] = val;
|
|
|
|
|
|
if (val < m_zMin || val > m_zMax)
|
|
|
|
|
|
m_fault[idx] |= INVALID_Z;
|
|
|
|
|
|
else
|
|
|
|
|
|
m_fault[idx] &= (~INVALID_Z);
|
|
|
|
|
|
}
|
|
|
|
|
|
double* GetValue() { return m_data; }
|
|
|
|
|
|
double GetZMin() const { return m_zMin; }
|
|
|
|
|
|
double GetZMax() const { return m_zMax; }
|
|
|
|
|
|
|
|
|
|
|
|
//undo/redo<64><6F>
|
|
|
|
|
|
const std::vector<uint64_t>& GetBackupIdxs() { return m_backupIdx; }
|
|
|
|
|
|
const std::vector<double>& GetBackupValues() { return m_backupVal; }
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD>ظ<EFBFBD><D8B8>°<EFBFBD>Χ<EFBFBD><CEA7>(<28><>Χ)
|
|
|
|
|
|
const BBox& GetBBox() const { return m_bbox; }
|
|
|
|
|
|
void SetUpdateBBox(const BBox& bbox) { m_bbox = bbox; }
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><>ȡ<EFBFBD><C8A1>ֵ<EFBFBD><EFBFBD><DFBC><EFBFBD>
|
|
|
|
|
|
double GetIsoplethStep() const { return m_isoplethStep; }
|
|
|
|
|
|
//<2F><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
int GetMarkIsoplethStep() const { return m_markStep; }
|
|
|
|
|
|
//<2F><>ȡ<EFBFBD><C8A1>ֵ<EFBFBD><EFBFBD><DFBC>㷶Χ
|
|
|
|
|
|
double GetMinIsopleth() const { return m_isoplethMin; }
|
|
|
|
|
|
double GetMaxIsopleth() const { return m_isoplethMax; }
|
|
|
|
|
|
|
|
|
|
|
|
void SetIsoplethParameter(double isopStep, int markStep, double minIsop, double maxIsop,
|
|
|
|
|
|
double zMin, double zMax);
|
|
|
|
|
|
void SetIsoplethProperty(double isopStep, int markStep, double minIsop, double maxIsop,
|
|
|
|
|
|
double zMin, double zMax);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Чֵ<D0A7><D6B5>ɫ
|
|
|
|
|
|
void SetInvalidColor(int r, int g, int b);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F>ϲ<EFBFBD>Zֵ<5A><D6B5><EFBFBD><EFBFBD>ƫ<EFBFBD><C6AB>
|
|
|
|
|
|
void OffsetFault();
|
|
|
|
|
|
void OffsetFault2(bool bFlag = false);
|
|
|
|
|
|
|
|
|
|
|
|
double GetNearPoint(Point2D& pt);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD><EFBFBD>Zֵ<5A>Զ<EFBFBD>ƥ<EFBFBD><C6A5>
|
|
|
|
|
|
void WellAdaptiveAdjustment(double rad, double range, double miu = 1.0);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD>˵<EFBFBD>ֵ<EFBFBD><D6B5>
|
|
|
|
|
|
void FilterIsopleths(int per);
|
|
|
|
|
|
|
|
|
|
|
|
//<2F>ķ<DEB8>Χ<EFBFBD><CEA7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
void SetPolygon(const std::vector<Point2D>& poly);
|
|
|
|
|
|
void StretchlUpPolygon(const std::vector<Point2D>& poly, float per, float wellrad, bool state = false);
|
|
|
|
|
|
void SmoothPolygon(const std::vector<Point2D>& poly, float per, float wellrad, bool state = false);
|
|
|
|
|
|
|
|
|
|
|
|
//ƽ<><C6BD><EFBFBD>ϲ<EFBFBD>
|
|
|
|
|
|
void TransitFault(const Point2D& cen, const Point2D& cen2, float rad, float per);
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
int Rows() const;
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
int Columns() const;
|
|
|
|
|
|
|
|
|
|
|
|
void setDecimalNumber(int num);
|
|
|
|
|
|
int getDecimalNumber();
|
|
|
|
|
|
|
|
|
|
|
|
//<2F>ϲ<EFBFBD><CFB2><EFBFBD><EFBFBD><EFBFBD>״̬ (<28>Ƿ<EFBFBD><C7B7>ɲ<EFBFBD><C9B2><EFBFBD><EFBFBD>ϲ<EFBFBD><CFB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϲ<EFBFBD><CFB2><EFBFBD><EFBFBD>Ƶ<EFBFBD>)
|
|
|
|
|
|
void setFaultControlState(bool state) { m_sFaultControl = state; }
|
|
|
|
|
|
bool getFaultControlState() { return m_sFaultControl; }
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD><EFBFBD>У<EFBFBD><D0A3>
|
|
|
|
|
|
bool RectificationMeshWellCompute(double deltaz, int type = 0, double factor = 1);
|
|
|
|
|
|
void MeshWellThreadStop();
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD><EFBFBD>У<EFBFBD><D0A3> <20><><EFBFBD>Ȼص<C8BB>
|
|
|
|
|
|
void OnRectificationProgress(int progress);
|
|
|
|
|
|
int GetMeshWellRectificationProgress();
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD>ڸ<EFBFBD><DAB8>¾<EFBFBD><C2BE><EFBFBD>У<EFBFBD><D0A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD><DDA3><EFBFBD><EFBFBD>߳<EFBFBD><DFB3><EFBFBD>Ҫ<EFBFBD><D2AA>
|
|
|
|
|
|
void UpdataMeshWellRectificationView();
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
bool calZval(const Point2D& pt, double& val);
|
|
|
|
|
|
|
|
|
|
|
|
void calColor(ColorItem& val) const;
|
|
|
|
|
|
|
|
|
|
|
|
void RemoveEdges(const BBox& bb, Isopleth& isopleth);
|
|
|
|
|
|
|
|
|
|
|
|
int getState(float z, uint64_t a, uint64_t b, uint64_t c, uint64_t d);
|
|
|
|
|
|
int getState2(float z, uint64_t a, uint64_t b, uint64_t c, uint64_t d);
|
|
|
|
|
|
|
|
|
|
|
|
void MarchingSquare(const BBox& bb, Isopleth& isopleth);
|
|
|
|
|
|
|
|
|
|
|
|
void addFaultPolyline();
|
|
|
|
|
|
|
|
|
|
|
|
IntPos getStartPos(const Point2D& pt);
|
|
|
|
|
|
|
|
|
|
|
|
float ControlPointWeight(const Point2D& pt, float wellrad);
|
|
|
|
|
|
|
|
|
|
|
|
void UpdateWellPoints(const Point2D& pt, double r);
|
|
|
|
|
|
|
|
|
|
|
|
void UpdateIsopleth(const BBox& bb, Isopleth& isopleth);
|
|
|
|
|
|
|
|
|
|
|
|
double dTruncateToDecimal(double value, int decimal_places = 4);
|
|
|
|
|
|
|
|
|
|
|
|
float fTruncateToDecimal(float value, int decimal_places = 4);
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
uint64_t m_row; //<2F><><EFBFBD><EFBFBD>
|
|
|
|
|
|
uint64_t m_col; //<2F><><EFBFBD><EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
int m_decimalNumber; //С<><D0A1>λ<EFBFBD><CEBB>
|
|
|
|
|
|
bool m_sFaultControl; //<2F>ϲ<EFBFBD><CFB2><EFBFBD><EFBFBD><EFBFBD>״̬
|
|
|
|
|
|
|
|
|
|
|
|
Point2D m_minPoint; //<2F><>С<EFBFBD><D0A1>
|
|
|
|
|
|
Point2D m_maxPoint; //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
double m_zMin;
|
|
|
|
|
|
double m_zMax;
|
|
|
|
|
|
double m_stepX; //<2F><><EFBFBD><EFBFBD>
|
|
|
|
|
|
double m_stepY;
|
|
|
|
|
|
|
|
|
|
|
|
double m_isoplethStep; //<2F><>ֵ<EFBFBD><EFBFBD><DFBC><EFBFBD>
|
|
|
|
|
|
int m_markStep; //<2F><>ע<EFBFBD><D7A2><EFBFBD><EFBFBD>
|
|
|
|
|
|
double m_isoplethMin; //<2F><>С<EFBFBD><D0A1>ֵ<EFBFBD><D6B5>
|
|
|
|
|
|
double m_isoplethMax; //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>
|
|
|
|
|
|
Point3D m_invalidColor; //<2F><>Чֵ<D0A7><D6B5>ɫ
|
|
|
|
|
|
bool m_isOffset; //<2F>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϲ<EFBFBD><CFB2><EFBFBD>
|
|
|
|
|
|
BBox m_bbox; //<2F><><EFBFBD>·<EFBFBD>Χ
|
|
|
|
|
|
|
|
|
|
|
|
double* m_data; //zֵ<7A><D6B5><EFBFBD><EFBFBD>
|
|
|
|
|
|
std::vector<ColorItem> m_colorCard; //<2F><>ɫ<EFBFBD><C9AB>
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<Isopleth> m_isopleths; //<2F><>ֵ<EFBFBD><D6B5>
|
|
|
|
|
|
std::vector<FaultPline> m_faultPaths; //<2F>ϲ<EFBFBD><CFB2><EFBFBD>
|
|
|
|
|
|
std::vector<uint8_t> m_fault; //<2F>ϲ<EFBFBD><CFB2><EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<ControlPoint2d> m_controlPnts; //<2F><><EFBFBD>Ƶ<EFBFBD>
|
|
|
|
|
|
Kdtree::KdNodeVector m_nodes; // <20><><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
Kdtree::KdTree* m_kdtree; // <20><><EFBFBD><EFBFBD>kdtree
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<Polyline2d> m_boundarys; // <20>߽<EFBFBD>
|
|
|
|
|
|
std::vector<Polyline2d> m_footwalls; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
std::vector<Polyline2d> m_otherLines; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
//undo
|
|
|
|
|
|
std::vector<uint64_t> m_backupIdx; //ÿһ<C3BF>δ<EFBFBD><CEB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
std::vector<double> m_backupVal;
|
|
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<RectificationMeshWell> m_rectMeshPtr; // <20><><EFBFBD><EFBFBD>У<EFBFBD><D0A3>
|
|
|
|
|
|
int m_progress; // <20><><EFBFBD><EFBFBD>У<EFBFBD><D0A3><EFBFBD><EFBFBD><EFBFBD>ȣ<EFBFBD><C8A3><EFBFBD><EFBFBD>̰߳<DFB3>ȫ<EFBFBD><C8AB>ʵ<EFBFBD><CAB5><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF>atomic<69><63>mutex<65><78><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
std::mutex m_rectMeshPtrMutex; // <20><><EFBFBD>ӻ<EFBFBD><D3BB><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|