// TnzTools includes
#include <tools/assistant.h>
#include <tools/assistants/guidelineline.h>
#include "assistantline.h"
#include "assistantvanishingpoint.h"
// TnzCore includes
#include <tgl.h>
//*****************************************************************************************
// TAssistantPerspective implementation
//*****************************************************************************************
class TAssistantPerspective final : public TAssistant {
Q_DECLARE_TR_FUNCTIONS(TAssistantPerspective)
public:
const TStringId m_idParallelX;
const TStringId m_idParallelY;
const TStringId m_idParallelZ;
const TStringId m_idGridXY;
const TStringId m_idGridYZ;
const TStringId m_idGridZX;
protected:
TAssistantPoint &m_o;
TAssistantPoint &m_x;
TAssistantPoint &m_y;
TAssistantPoint &m_z;
TAssistantPoint &m_xy;
TAssistantPoint &m_yz;
TAssistantPoint &m_zx;
TAssistantPoint &m_vx;
TAssistantPoint &m_vy;
TAssistantPoint &m_vz;
public:
TAssistantPerspective(TMetaObject &object):
TAssistant(object),
m_idParallelX("parallelX"),
m_idParallelY("parallelY"),
m_idParallelZ("parallelZ"),
m_idGridXY("gridXY"),
m_idGridYZ("gridYZ"),
m_idGridZX("gridZX"),
m_o ( addPoint("o", TAssistantPoint::CircleCross) ),
m_x ( addPoint("x", TAssistantPoint::CircleFill, TPointD( 50, 0 )) ),
m_y ( addPoint("y", TAssistantPoint::CircleFill, TPointD( 0, 50 )) ),
m_z ( addPoint("z", TAssistantPoint::CircleFill, TPointD( 25, -25 )) ),
m_xy ( addPoint("xy", TAssistantPoint::Circle, TPointD( 50, 50 )) ),
m_yz ( addPoint("yz", TAssistantPoint::Circle, TPointD( 25, 25 )) ),
m_zx ( addPoint("zx", TAssistantPoint::Circle, TPointD( 75, -25 )) ),
m_vx ( addPoint("vx", TAssistantPoint::Circle) ),
m_vy ( addPoint("vy", TAssistantPoint::Circle) ),
m_vz ( addPoint("vz", TAssistantPoint::Circle) )
{
addProperty( new TBoolProperty(m_idParallelX.str(), getParallelX()) );
addProperty( new TBoolProperty(m_idParallelY.str(), getParallelY()) );
addProperty( new TBoolProperty(m_idParallelZ.str(), getParallelZ()) );
addProperty( new TBoolProperty(m_idGridXY.str(), getGridXY()) );
addProperty( new TBoolProperty(m_idGridYZ.str(), getGridYZ()) );
addProperty( new TBoolProperty(m_idGridZX.str(), getGridZX()) );
}
static QString getLocalName()
{ return tr("Perspective"); }
void updateTranslation() const override {
TAssistant::updateTranslation();
setTranslation(m_idParallelX, tr("Parallel X"));
setTranslation(m_idParallelY, tr("Parallel Y"));
setTranslation(m_idParallelZ, tr("Parallel Z"));
setTranslation(m_idGridXY, tr("Grid XY"));
setTranslation(m_idGridYZ, tr("Grid YZ"));
setTranslation(m_idGridZX, tr("Grid ZX"));
}
inline bool getParallelX() const
{ return data()[m_idParallelX].getBool(); }
inline bool getParallelY() const
{ return data()[m_idParallelY].getBool(); }
inline bool getParallelZ() const
{ return data()[m_idParallelZ].getBool(); }
inline bool getGridXY() const
{ return data()[m_idGridXY].getBool(); }
inline bool getGridYZ() const
{ return data()[m_idGridYZ].getBool(); }
inline bool getGridZX() const
{ return data()[m_idGridZX].getBool(); }
void onDataChanged(const TVariant &value) override {
TAssistant::onDataChanged(value);
m_xy.visible = !getParallelX() || !getParallelY();
m_yz.visible = !getParallelY() || !getParallelZ();
m_zx.visible = !getParallelZ() || !getParallelX();
fixPoints();
}
private:
void fixAxisPoint(
TPointD &a,
const TAssistantPoint &v,
const TPointD &oldV ) const
{
const TPointD &o = m_o.position;
if (!v.visible)
{ a = o + v.position; return; }
TPointD dv = v.position - o;
TPointD oldDv = oldV - o;
double l = norm2(oldDv);
double ln = norm2(dv);
if (!(l > TConsts::epsilon) || !(ln > TConsts::epsilon))
return;
double d = (a - o)*oldDv;
a = o + dv*(d/l);
}
inline void fixAxisPoint(
TAssistantPoint &a,
const TAssistantPoint &v,
const TPointD &oldV ) const
{ fixAxisPoint(a.position, v, oldV); }
void fixVanishingPoint(
TAssistantPoint &v,
const TPointD &a,
const TPointD &oldA ) const
{
const TPointD &o = m_o.position;
TPointD da = a - o;
if (!v.visible)
{ v.position = da; return; }
TPointD oldDa = oldA - o;
double l = norm2(oldDa);
double ln = norm2(da);
if (!(l > TConsts::epsilon) || !(ln > TConsts::epsilon))
return;
double d = (v.position - o)*oldDa;
v.position = o + da*(d/l);
}
inline void fixVanishingPoint(
TAssistantPoint &v,
const TAssistantPoint &a,
const TPointD &oldA ) const
{ fixVanishingPoint(v, a.position, oldA); }
void fixVanishingPoint(
TAssistantPoint &v,
const TPointD &a0,
const TPointD &a1,
const TPointD &b0,
const TPointD &b1 ) const
{
TPointD da = a1 - a0;
TPointD db = b1 - b0;
const TPointD ab = b0 - a0;
double k = db.x*da.y - db.y*da.x;
if ( (&v == &m_vx && getParallelX())
|| (&v == &m_vy && getParallelY())
|| (&v == &m_vz && getParallelZ()) )
k = 0;
if (fabs(k) > TConsts::epsilon) {
double lb = (da.x*ab.y - da.y*ab.x)/k;
v.position.x = lb*db.x + b0.x;
v.position.y = lb*db.y + b0.y;
v.visible = true;
} else {
v.position = da;
v.visible = false;
}
}
inline void fixVanishingPoint(
TAssistantPoint &v,
const TAssistantPoint &a0,
const TAssistantPoint &a1,
const TAssistantPoint &b0,
const TAssistantPoint &b1 ) const
{ fixVanishingPoint(v, a0.position, a1.position, b0.position, b1.position); }
static bool lineCross(
TPointD &p,
const TPointD &a,
const TPointD &b,
const TPointD &da,
const TPointD &db )
{
double d = da.x*db.y - da.y*db.x;
if (!(fabs(d) > TConsts::epsilon))
return false;
d = 1/d;
p = TPointD(
(a.y*db.x + b.x*db.y)*da.x - (a.x*da.y + b.y*da.x)*db.x,
(a.y*da.x + b.x*da.y)*db.y - (a.x*db.y + b.y*db.x)*da.y )*d;
return true;
}
void fixSidePoint(
TPointD &p,
const TPointD &a, // pass 'a' and 'b' by copy
const TPointD &b,
const TAssistantPoint &va,
const TAssistantPoint &vb ) const
{
TPointD da = va.visible ? va.position - a : va.position;
TPointD db = vb.visible ? vb.position - b : vb.position;
lineCross(p, a, b, da, db);
}
inline void fixSidePoint(
TAssistantPoint &p,
const TAssistantPoint &a,
const TAssistantPoint &b,
const TAssistantPoint &va,
const TAssistantPoint &vb ) const
{ fixSidePoint(p.position, a.position, b.position, va, vb); }
void fixSidePoints() {
fixSidePoint(m_xy, m_x, m_y, m_vy, m_vx);
fixSidePoint(m_yz, m_y, m_z, m_vz, m_vy);
fixSidePoint(m_zx, m_z, m_x, m_vx, m_vz);
}
void addGuideline(
const TPointD &position,
const TAffine &toTool,
const TAssistantPoint &v,
TGuidelineList &outGuidelines ) const
{
if (v.visible) {
TPointD p = toTool * v.position;
if (tdistance2(p, position) > 4*TConsts::epsilon*TConsts::epsilon)
outGuidelines.push_back(
new TGuidelineRay(
getEnabled(),
getMagnetism(),
p,
position ));
} else {
TPointD d = toTool.transformDirection(v.position);
if (norm2(d) > 4*TConsts::epsilon*TConsts::epsilon)
outGuidelines.push_back(
new TGuidelineInfiniteLine(
getEnabled(),
getMagnetism(),
position,
position + d ));
}
}
public:
void onFixPoints() override {
fixVanishingPoint(m_vx, m_o, m_x, m_y, m_xy);
fixVanishingPoint(m_vy, m_o, m_y, m_x, m_xy);
fixVanishingPoint(m_vz, m_o, m_z, m_x, m_zx);
fixSidePoints();
}
void onMovePoint(TAssistantPoint &point, const TPointD &position) override {
if (!point.visible)
return;
TPointD old = point.position;
point.position = position;
if (&point == &m_o) {
TPointD d = m_o.position - old;
m_x.position += d;
m_y.position += d;
m_z.position += d;
m_xy.position += d;
m_yz.position += d;
m_zx.position += d;
if (m_vx.visible) m_vx.position += d;
if (m_vy.visible) m_vy.position += d;
if (m_vz.visible) m_vz.position += d;
} else
if (&point == &m_x) {
fixVanishingPoint(m_vx, m_x, old);
fixSidePoints();
} else
if (&point == &m_y) {
fixVanishingPoint(m_vy, m_y, old);
fixSidePoints();
} else
if (&point == &m_z) {
fixVanishingPoint(m_vz, m_z, old);
fixSidePoints();
} else
if (&point == &m_xy) {
fixVanishingPoint(m_vx, m_o, m_x, m_y, m_xy);
fixVanishingPoint(m_vy, m_o, m_y, m_x, m_xy);
fixSidePoints();
} else
if (&point == &m_yz) {
fixVanishingPoint(m_vy, m_o, m_y, m_z, m_yz);
fixVanishingPoint(m_vz, m_o, m_z, m_y, m_yz);
fixSidePoints();
} else
if (&point == &m_zx) {
fixVanishingPoint(m_vz, m_o, m_z, m_x, m_zx);
fixVanishingPoint(m_vx, m_o, m_x, m_z, m_zx);
fixSidePoints();
} else
if (&point == &m_vx) {
fixAxisPoint(m_x, m_vx, old);
fixSidePoints();
} else
if (&point == &m_vy) {
fixAxisPoint(m_y, m_vy, old);
fixSidePoints();
} else
if (&point == &m_vz) {
fixAxisPoint(m_z, m_vz, old);
fixSidePoints();
}
}
void getGuidelines(
const TPointD &position,
const TAffine &toTool,
TGuidelineList &outGuidelines ) const override
{
addGuideline(position, toTool, m_vx, outGuidelines);
addGuideline(position, toTool, m_vy, outGuidelines);
addGuideline(position, toTool, m_vz, outGuidelines);
}
void drawGrid(
const TAssistantPoint &vx,
const TAssistantPoint &vy,
const TPointD &y ) const
{
double alpha = getDrawingGridAlpha();
const TPointD &o = m_o.position;
if (!vx.visible && !vy.visible) {
TAssistantLine::drawGrid(
o, o + vx.position, o, y, false, false, false, alpha );
return;
}
if (!vx.visible) {
TAssistantLine::drawGrid(
vy.position, vy.position + vx.position, o, y, false, false, true, alpha );
return;
}
TPointD p = y;
if (vy.visible) {
const TPointD &a = vx.position;
const TPointD &b = o;
const TPointD da = y - a;
const TPointD db = vy.position - vx.position;
lineCross(p, a, b, da, db);
}
TAssistantVanishingPoint::drawPerspectiveGrid(vx.position, o, p, alpha);
}
void drawVanishingPoint(const TAssistantPoint &v, double pixelSize, double alpha) const {
if (!v.visible)
return;
const TPointD &p = v.position;
TPointD dx(20.0*pixelSize, 0.0);
TPointD dy(0.0, 10.0*pixelSize);
drawSegment(p-dx-dy, p+dx+dy, pixelSize, alpha);
drawSegment(p-dx+dy, p+dx-dy, pixelSize, alpha);
}
void draw(TToolViewer*, bool enabled) const override {
double pixelSize = sqrt(tglGetPixelSize2());
double alpha = getDrawingAlpha(enabled);
drawVanishingPoint(m_vx, pixelSize, alpha);
drawVanishingPoint(m_vy, pixelSize, alpha);
drawVanishingPoint(m_vz, pixelSize, alpha);
if (getGridXY()) {
drawGrid(m_vx, m_vy, m_y.position);
drawGrid(m_vy, m_vx, m_x.position);
}
if (getGridYZ()) {
drawGrid(m_vy, m_vz, m_z.position);
drawGrid(m_vz, m_vy, m_y.position);
}
if (getGridZX()) {
drawGrid(m_vz, m_vx, m_x.position);
drawGrid(m_vx, m_vz, m_z.position);
}
}
void drawEdit(TToolViewer *viewer) const override {
double pixelSize = sqrt(tglGetPixelSize2());
TPointD xyz;
fixSidePoint(xyz, m_xy.position, m_zx.position, m_vz, m_vy);
drawSegment(xyz, m_xy.position, pixelSize);
drawSegment(xyz, m_yz.position, pixelSize);
drawSegment(xyz, m_zx.position, pixelSize);
drawSegment(m_xy.position, m_x.position, pixelSize);
drawSegment(m_xy.position, m_y.position, pixelSize);
drawSegment(m_yz.position, m_y.position, pixelSize);
drawSegment(m_yz.position, m_z.position, pixelSize);
drawSegment(m_zx.position, m_z.position, pixelSize);
drawSegment(m_zx.position, m_x.position, pixelSize);
drawSegment(m_o.position, m_x.position, pixelSize);
drawSegment(m_o.position, m_y.position, pixelSize);
drawSegment(m_o.position, m_z.position, pixelSize);
TAssistant::drawEdit(viewer);
}
};
//*****************************************************************************************
// Registration
//*****************************************************************************************
static TAssistantTypeT<TAssistantPerspective> assistantPerspective("assistantPerspective");