compass gadget for radial and spin blur fxs
This commit is contained in:
parent
33d08f0866
commit
f4b2e1dd6b
12 changed files with 97 additions and 39 deletions
|
@ -85,3 +85,13 @@
|
||||||
center
|
center
|
||||||
</Parameter>
|
</Parameter>
|
||||||
</Concept>
|
</Concept>
|
||||||
|
|
||||||
|
<Concept>
|
||||||
|
compass_ui
|
||||||
|
<Name>
|
||||||
|
Center
|
||||||
|
</Name>
|
||||||
|
<Parameter>
|
||||||
|
center
|
||||||
|
</Parameter>
|
||||||
|
</Concept>
|
||||||
|
|
|
@ -84,4 +84,14 @@
|
||||||
<Parameter>
|
<Parameter>
|
||||||
center
|
center
|
||||||
</Parameter>
|
</Parameter>
|
||||||
|
</Concept>
|
||||||
|
|
||||||
|
<Concept>
|
||||||
|
compass_spin_ui
|
||||||
|
<Name>
|
||||||
|
Center
|
||||||
|
</Name>
|
||||||
|
<Parameter>
|
||||||
|
center
|
||||||
|
</Parameter>
|
||||||
</Concept>
|
</Concept>
|
|
@ -86,6 +86,8 @@ public: // Enums
|
||||||
SIZE_UI,
|
SIZE_UI,
|
||||||
QUAD_UI,
|
QUAD_UI,
|
||||||
RECT_UI,
|
RECT_UI,
|
||||||
|
COMPASS_UI,
|
||||||
|
COMPASS_SPIN_UI,
|
||||||
|
|
||||||
CONCEPTSCOUNT,
|
CONCEPTSCOUNT,
|
||||||
UI_CONCEPTS = RADIUS_UI
|
UI_CONCEPTS = RADIUS_UI
|
||||||
|
|
|
@ -65,8 +65,8 @@ public:
|
||||||
// [TDoubleParamP] }
|
// [TDoubleParamP] }
|
||||||
LINEAR_RANGE, // A band-like range between two points.
|
LINEAR_RANGE, // A band-like range between two points.
|
||||||
// { [2 TPointParamP] }
|
// { [2 TPointParamP] }
|
||||||
|
COMPASS, // a line widget pointing toward some point. [TPointParamP]
|
||||||
RAYLIT,
|
COMPASS_SPIN, // ... with guides in rotational direction.
|
||||||
|
|
||||||
RAINBOW_WIDTH,
|
RAINBOW_WIDTH,
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ public:
|
||||||
//,this->m_radius->getValue(frame) * scale
|
//,this->m_radius->getValue(frame) * scale
|
||||||
,
|
,
|
||||||
0 /* debug:2012-02-01:ゼロ以上だとmarginが小さすぎになり画像が切れてしまう
|
0 /* debug:2012-02-01:ゼロ以上だとmarginが小さすぎになり画像が切れてしまう
|
||||||
*/
|
*/
|
||||||
|
|
||||||
,
|
,
|
||||||
(this->m_anti_alias->getValue() ? 4 : 1));
|
(this->m_anti_alias->getValue() ? 4 : 1));
|
||||||
|
@ -143,7 +143,7 @@ public:
|
||||||
|
|
||||||
// add 20140130
|
// add 20140130
|
||||||
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
|
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
|
||||||
concepts = new TParamUIConcept[length = 2];
|
concepts = new TParamUIConcept[length = 3];
|
||||||
|
|
||||||
concepts[0].m_type = TParamUIConcept::POINT;
|
concepts[0].m_type = TParamUIConcept::POINT;
|
||||||
concepts[0].m_label = "Center";
|
concepts[0].m_label = "Center";
|
||||||
|
@ -153,6 +153,9 @@ public:
|
||||||
concepts[1].m_label = "Radius";
|
concepts[1].m_label = "Radius";
|
||||||
concepts[1].m_params.push_back(m_radius);
|
concepts[1].m_params.push_back(m_radius);
|
||||||
concepts[1].m_params.push_back(m_center);
|
concepts[1].m_params.push_back(m_center);
|
||||||
|
|
||||||
|
concepts[2].m_type = TParamUIConcept::COMPASS;
|
||||||
|
concepts[2].m_params.push_back(m_center);
|
||||||
}
|
}
|
||||||
// add 20140130
|
// add 20140130
|
||||||
};
|
};
|
||||||
|
@ -210,7 +213,7 @@ void fx_(const TRasterP in_ras // with margin
|
||||||
ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), out_ras, margin);
|
ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), out_ras, margin);
|
||||||
in_gr8->unlock();
|
in_gr8->unlock();
|
||||||
}
|
}
|
||||||
}
|
} // namespace
|
||||||
//------------------------------------------------------------
|
//------------------------------------------------------------
|
||||||
void ino_radial_blur::doCompute(TTile &tile, double frame,
|
void ino_radial_blur::doCompute(TTile &tile, double frame,
|
||||||
const TRenderSettings &ri) {
|
const TRenderSettings &ri) {
|
||||||
|
@ -282,7 +285,8 @@ void ino_radial_blur::doCompute(TTile &tile, double frame,
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << "params"
|
os << "params"
|
||||||
<< " cx " << center.x << " cy " << center.y << " blur " << blur
|
<< " cx " << center.x << " cy " << center.y << " blur " << blur
|
||||||
<< " radius " << radius << " twist " << twist
|
<< " radius " << radius << " twist "
|
||||||
|
<< twist
|
||||||
// << " twist_radius " << twist_radius
|
// << " twist_radius " << twist_radius
|
||||||
<< " reference " << refer_mode << " alpha_rendering " << alpha_rend_sw
|
<< " reference " << refer_mode << " alpha_rendering " << alpha_rend_sw
|
||||||
<< " anti_alias " << anti_alias_sw << " render_center "
|
<< " anti_alias " << anti_alias_sw << " render_center "
|
||||||
|
|
|
@ -127,7 +127,7 @@ public:
|
||||||
|
|
||||||
// add 20140130
|
// add 20140130
|
||||||
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
|
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
|
||||||
concepts = new TParamUIConcept[length = 2];
|
concepts = new TParamUIConcept[length = 3];
|
||||||
|
|
||||||
concepts[0].m_type = TParamUIConcept::POINT;
|
concepts[0].m_type = TParamUIConcept::POINT;
|
||||||
concepts[0].m_label = "Center";
|
concepts[0].m_label = "Center";
|
||||||
|
@ -137,6 +137,9 @@ public:
|
||||||
concepts[1].m_label = "Radius";
|
concepts[1].m_label = "Radius";
|
||||||
concepts[1].m_params.push_back(m_radius);
|
concepts[1].m_params.push_back(m_radius);
|
||||||
concepts[1].m_params.push_back(m_center);
|
concepts[1].m_params.push_back(m_center);
|
||||||
|
|
||||||
|
concepts[2].m_type = TParamUIConcept::COMPASS_SPIN;
|
||||||
|
concepts[2].m_params.push_back(m_center);
|
||||||
}
|
}
|
||||||
// add 20140130
|
// add 20140130
|
||||||
};
|
};
|
||||||
|
@ -196,7 +199,7 @@ void fx_(const TRasterP in_ras // with margin
|
||||||
ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), out_ras, margin);
|
ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), out_ras, margin);
|
||||||
in_gr8->unlock();
|
in_gr8->unlock();
|
||||||
}
|
}
|
||||||
}
|
} // namespace
|
||||||
//------------------------------------------------------------
|
//------------------------------------------------------------
|
||||||
void ino_spin_blur::doCompute(TTile &tile, double frame,
|
void ino_spin_blur::doCompute(TTile &tile, double frame,
|
||||||
const TRenderSettings &ri) {
|
const TRenderSettings &ri) {
|
||||||
|
|
|
@ -81,7 +81,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
|
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
|
||||||
concepts = new TParamUIConcept[length = 2];
|
concepts = new TParamUIConcept[length = 3];
|
||||||
|
|
||||||
concepts[0].m_type = TParamUIConcept::POINT;
|
concepts[0].m_type = TParamUIConcept::POINT;
|
||||||
concepts[0].m_label = "Center";
|
concepts[0].m_label = "Center";
|
||||||
|
@ -91,6 +91,9 @@ public:
|
||||||
concepts[1].m_label = "Radius";
|
concepts[1].m_label = "Radius";
|
||||||
concepts[1].m_params.push_back(m_radius);
|
concepts[1].m_params.push_back(m_radius);
|
||||||
concepts[1].m_params.push_back(m_point);
|
concepts[1].m_params.push_back(m_point);
|
||||||
|
|
||||||
|
concepts[2].m_type = TParamUIConcept::COMPASS;
|
||||||
|
concepts[2].m_params.push_back(m_point);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ public:
|
||||||
concepts[1].m_params.push_back(m_radius);
|
concepts[1].m_params.push_back(m_radius);
|
||||||
concepts[1].m_params.push_back(m_p);
|
concepts[1].m_params.push_back(m_p);
|
||||||
|
|
||||||
concepts[2].m_type = TParamUIConcept::RAYLIT;
|
concepts[2].m_type = TParamUIConcept::COMPASS;
|
||||||
concepts[2].m_params.push_back(m_p);
|
concepts[2].m_params.push_back(m_p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,7 +52,7 @@ public:
|
||||||
if (dist > radius)
|
if (dist > radius)
|
||||||
blurangle = intensity * ((dist - radius));
|
blurangle = intensity * ((dist - radius));
|
||||||
else
|
else
|
||||||
blurangle = 0;
|
blurangle = 0;
|
||||||
if (blurangle > M_PI) blurangle = M_PI;
|
if (blurangle > M_PI) blurangle = M_PI;
|
||||||
return tround(4 * blurangle * dist);
|
return tround(4 * blurangle * dist);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
|
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
|
||||||
concepts = new TParamUIConcept[length = 2];
|
concepts = new TParamUIConcept[length = 3];
|
||||||
|
|
||||||
concepts[0].m_type = TParamUIConcept::POINT;
|
concepts[0].m_type = TParamUIConcept::POINT;
|
||||||
concepts[0].m_label = "Center";
|
concepts[0].m_label = "Center";
|
||||||
|
@ -98,6 +98,9 @@ public:
|
||||||
concepts[1].m_label = "Radius";
|
concepts[1].m_label = "Radius";
|
||||||
concepts[1].m_params.push_back(m_radius);
|
concepts[1].m_params.push_back(m_radius);
|
||||||
concepts[1].m_params.push_back(m_point);
|
concepts[1].m_params.push_back(m_point);
|
||||||
|
|
||||||
|
concepts[2].m_type = TParamUIConcept::COMPASS_SPIN;
|
||||||
|
concepts[2].m_params.push_back(m_point);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -141,9 +144,9 @@ void doSpinBlur(const TRasterPT<PIXEL> rout, const TRasterPT<PIXEL> rin,
|
||||||
if (dist > radius)
|
if (dist > radius)
|
||||||
blurangle = intensity * ((dist - radius));
|
blurangle = intensity * ((dist - radius));
|
||||||
else
|
else
|
||||||
blurangle = 0;
|
blurangle = 0;
|
||||||
if (blurangle > M_PI) blurangle = M_PI;
|
if (blurangle > M_PI) blurangle = M_PI;
|
||||||
range = (int)(4 * blurangle * dist);
|
range = (int)(4 * blurangle * dist);
|
||||||
if (range >= 1) {
|
if (range >= 1) {
|
||||||
angle = atan2((double)vy, (double)vx) - blurangle;
|
angle = atan2((double)vy, (double)vx) - blurangle;
|
||||||
ddist = 0.5 / dist;
|
ddist = 0.5 / dist;
|
||||||
|
|
|
@ -131,7 +131,8 @@ static const TParamUIConcept::Type
|
||||||
TParamUIConcept::ANGLE, TParamUIConcept::POINT,
|
TParamUIConcept::ANGLE, TParamUIConcept::POINT,
|
||||||
TParamUIConcept::POINT_2, TParamUIConcept::VECTOR,
|
TParamUIConcept::POINT_2, TParamUIConcept::VECTOR,
|
||||||
TParamUIConcept::POLAR, TParamUIConcept::SIZE,
|
TParamUIConcept::POLAR, TParamUIConcept::SIZE,
|
||||||
TParamUIConcept::QUAD, TParamUIConcept::RECT};
|
TParamUIConcept::QUAD, TParamUIConcept::RECT,
|
||||||
|
TParamUIConcept::COMPASS, TParamUIConcept::COMPASS_SPIN};
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
|
@ -400,7 +401,7 @@ void ShaderFx::initialize() {
|
||||||
TParamUIConcept &uiConcept = m_this->m_uiConcepts.back();
|
TParamUIConcept &uiConcept = m_this->m_uiConcepts.back();
|
||||||
uiConcept.m_type = ::l_conceptTypes[siParam.m_concept.m_type -
|
uiConcept.m_type = ::l_conceptTypes[siParam.m_concept.m_type -
|
||||||
ShaderInterface::UI_CONCEPTS];
|
ShaderInterface::UI_CONCEPTS];
|
||||||
uiConcept.m_label = siParam.m_concept.m_label.toStdString();
|
uiConcept.m_label = siParam.m_concept.m_label.toStdString();
|
||||||
uiConcept.m_params.push_back(param);
|
uiConcept.m_params.push_back(param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,9 +47,10 @@ const static QString l_typeNames[ShaderInterface::TYPESCOUNT] = {
|
||||||
"int", "ivec2", "ivec3", "ivec4", "rgba", "rgb"};
|
"int", "ivec2", "ivec3", "ivec4", "rgba", "rgb"};
|
||||||
|
|
||||||
const static QString l_conceptNames[ShaderInterface::CONCEPTSCOUNT] = {
|
const static QString l_conceptNames[ShaderInterface::CONCEPTSCOUNT] = {
|
||||||
"none", "percent", "length", "angle", "point",
|
"none", "percent", "length", "angle", "point",
|
||||||
"radius_ui", "width_ui", "angle_ui", "point_ui", "xy_ui",
|
"radius_ui", "width_ui", "angle_ui", "point_ui", "xy_ui",
|
||||||
"vector_ui", "polar_ui", "size_ui", "quad_ui", "rect_ui"};
|
"vector_ui", "polar_ui", "size_ui", "quad_ui", "rect_ui",
|
||||||
|
"compass_ui", "compass_spin_ui"};
|
||||||
|
|
||||||
const static QString l_hwtNames[ShaderInterface::HWTCOUNT] = {"none", "any",
|
const static QString l_hwtNames[ShaderInterface::HWTCOUNT] = {"none", "any",
|
||||||
"isotropic"};
|
"isotropic"};
|
||||||
|
|
|
@ -1532,7 +1532,7 @@ void LinearRangeFxGadget::leftButtonUp(const TPointD &pos,
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
|
||||||
class RayLitFxGadget final : public FxGadget {
|
class CompassFxGadget final : public FxGadget {
|
||||||
TPointParamP m_center;
|
TPointParamP m_center;
|
||||||
|
|
||||||
enum HANDLE { Body = 0, Near, Far, None } m_handle = None;
|
enum HANDLE { Body = 0, Near, Far, None } m_handle = None;
|
||||||
|
@ -1540,9 +1540,11 @@ class RayLitFxGadget final : public FxGadget {
|
||||||
TPointD m_clickedPos, m_mousePos;
|
TPointD m_clickedPos, m_mousePos;
|
||||||
TPointD m_targetPos, m_anotherPos;
|
TPointD m_targetPos, m_anotherPos;
|
||||||
|
|
||||||
|
bool m_isSpin;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RayLitFxGadget(FxGadgetController *controller,
|
CompassFxGadget(FxGadgetController *controller,
|
||||||
const TPointParamP ¢erPoint);
|
const TPointParamP ¢erPoint, bool isSpin = false);
|
||||||
|
|
||||||
void draw(bool picking) override;
|
void draw(bool picking) override;
|
||||||
|
|
||||||
|
@ -1553,16 +1555,16 @@ public:
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
RayLitFxGadget::RayLitFxGadget(FxGadgetController *controller,
|
CompassFxGadget::CompassFxGadget(FxGadgetController *controller,
|
||||||
const TPointParamP ¢erPoint)
|
const TPointParamP ¢erPoint, bool isSpin)
|
||||||
: FxGadget(controller, 3), m_center(centerPoint) {
|
: FxGadget(controller, 3), m_center(centerPoint), m_isSpin(isSpin) {
|
||||||
addParam(centerPoint->getX());
|
addParam(centerPoint->getX());
|
||||||
addParam(centerPoint->getY());
|
addParam(centerPoint->getY());
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void RayLitFxGadget::draw(bool picking) {
|
void CompassFxGadget::draw(bool picking) {
|
||||||
auto setColorById = [&](int id) {
|
auto setColorById = [&](int id) {
|
||||||
if (isSelected(id))
|
if (isSelected(id))
|
||||||
glColor3dv(m_selectedColor);
|
glColor3dv(m_selectedColor);
|
||||||
|
@ -1615,6 +1617,7 @@ void RayLitFxGadget::draw(bool picking) {
|
||||||
double angle = std::atan2(-center.y, -center.x) * M_180_PI;
|
double angle = std::atan2(-center.y, -center.x) * M_180_PI;
|
||||||
double theta = M_180_PI * lineInterval / dCenter;
|
double theta = M_180_PI * lineInterval / dCenter;
|
||||||
|
|
||||||
|
// draw guides
|
||||||
glColor3d(0, 0, 1);
|
glColor3d(0, 0, 1);
|
||||||
glLineStipple(1, 0x00FF);
|
glLineStipple(1, 0x00FF);
|
||||||
glEnable(GL_LINE_STIPPLE);
|
glEnable(GL_LINE_STIPPLE);
|
||||||
|
@ -1622,14 +1625,26 @@ void RayLitFxGadget::draw(bool picking) {
|
||||||
glTranslated(center.x, center.y, 0);
|
glTranslated(center.x, center.y, 0);
|
||||||
glRotated(angle, 0, 0, 1);
|
glRotated(angle, 0, 0, 1);
|
||||||
for (int i = -3; i <= 3; i++) {
|
for (int i = -3; i <= 3; i++) {
|
||||||
if (i == 0) continue;
|
if (!m_isSpin) { // radial direction
|
||||||
glPushMatrix();
|
if (i == 0) continue;
|
||||||
glRotated(theta * (double)i, 0, 0, 1);
|
glPushMatrix();
|
||||||
glBegin(GL_LINES);
|
glRotated(theta * (double)i, 0, 0, 1);
|
||||||
glVertex2d(dCenter - lineHalf, 0.0);
|
glBegin(GL_LINES);
|
||||||
glVertex2d(dCenter + lineHalf, 0.0);
|
glVertex2d(dCenter - lineHalf, 0.0);
|
||||||
glEnd();
|
glVertex2d(dCenter + lineHalf, 0.0);
|
||||||
glPopMatrix();
|
glEnd();
|
||||||
|
glPopMatrix();
|
||||||
|
} else { // rotational direction
|
||||||
|
if (i == 3 || i == -3) continue;
|
||||||
|
double tmpRad = dCenter + (double)i * lineInterval;
|
||||||
|
double d_angle = (lineInterval / dCenter) * 6.0 / 10.0;
|
||||||
|
glBegin(GL_LINE_STRIP);
|
||||||
|
for (int r = -5; r <= 5; r++) {
|
||||||
|
double tmpAngle = (double)r * d_angle;
|
||||||
|
glVertex2d(tmpRad * std::cos(tmpAngle), tmpRad * std::sin(tmpAngle));
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
@ -1663,7 +1678,7 @@ void RayLitFxGadget::draw(bool picking) {
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void RayLitFxGadget::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
|
void CompassFxGadget::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
|
||||||
m_handle = (HANDLE)m_selected;
|
m_handle = (HANDLE)m_selected;
|
||||||
if (m_handle == None) return;
|
if (m_handle == None) return;
|
||||||
m_clickedPos = pos;
|
m_clickedPos = pos;
|
||||||
|
@ -1672,7 +1687,7 @@ void RayLitFxGadget::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void RayLitFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
|
void CompassFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
|
||||||
if (m_handle == None) return;
|
if (m_handle == None) return;
|
||||||
TPointD d = pos - m_clickedPos;
|
TPointD d = pos - m_clickedPos;
|
||||||
|
|
||||||
|
@ -1695,7 +1710,7 @@ void RayLitFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void RayLitFxGadget::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
|
void CompassFxGadget::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
|
||||||
m_handle = None;
|
m_handle = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1979,9 +1994,15 @@ FxGadget *FxGadgetController::allocateGadget(const TParamUIConcept &uiConcept) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TParamUIConcept::RAYLIT: {
|
case TParamUIConcept::COMPASS: {
|
||||||
assert(uiConcept.m_params.size() == 1);
|
assert(uiConcept.m_params.size() == 1);
|
||||||
gadget = new RayLitFxGadget(this, uiConcept.m_params[0]);
|
gadget = new CompassFxGadget(this, uiConcept.m_params[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TParamUIConcept::COMPASS_SPIN: {
|
||||||
|
assert(uiConcept.m_params.size() == 1);
|
||||||
|
gadget = new CompassFxGadget(this, uiConcept.m_params[0], true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue