b52c5a
b52c5a
b52c5a
// TnzTools includes
b52c5a
#include <tools replicator.h=""></tools>
b52c5a
#include <tools modifierclone.h="" modifiers=""></tools>
b52c5a
b52c5a
b52c5a
// TnzCore includes
b52c5a
#include <tgl.h></tgl.h>
b52c5a
b52c5a
b52c5a
//*****************************************************************************************
b52c5a
//    TReplicatorAffine implementation
b52c5a
//*****************************************************************************************
b52c5a
b52c5a
class TReplicatorAffine final : public TReplicator {
b52c5a
  Q_DECLARE_TR_FUNCTIONS(TReplicatorAffine)
b52c5a
public:
b52c5a
  const TStringId m_idFixScale;
b52c5a
  const TStringId m_idFixAspect;
b52c5a
  const TStringId m_idFixAngle;
b52c5a
  const TStringId m_idFixSkew;
b52c5a
  const TStringId m_idCount;
b52c5a
  const TStringId m_idCountInv;
b52c5a
  const TStringId m_idPressure;
b52c5a
b52c5a
protected:
b52c5a
  TAssistantPoint &m_center0;
b52c5a
  TAssistantPoint &m_a0;
b52c5a
  TAssistantPoint &m_b0;
b52c5a
  TAssistantPoint &m_center1;
b52c5a
  TAssistantPoint &m_a1;
b52c5a
  TAssistantPoint &m_b1;
b52c5a
b52c5a
public:
b52c5a
  TReplicatorAffine(TMetaObject &object):
b52c5a
    TReplicator(object),
b52c5a
    m_idFixScale("fixScale"),
b52c5a
    m_idFixAspect("fixAspect"),
b52c5a
    m_idFixAngle("fixAngle"),
b52c5a
    m_idFixSkew("fixSkew"),
b52c5a
    m_idCount("count"),
b52c5a
    m_idCountInv("countInv"),
b52c5a
    m_idPressure("pressure"),
b52c5a
    m_center0( addPoint("center0", TAssistantPoint::CircleCross) ),
b52c5a
    m_a0     ( addPoint("a0",      TAssistantPoint::CircleFill, TPointD(40,  0)) ),
b52c5a
    m_b0     ( addPoint("b0",      TAssistantPoint::Circle,     TPointD( 0, 40)) ),
b52c5a
    m_center1( addPoint("center1", TAssistantPoint::Circle,     TPointD(50,  0)) ),
b52c5a
    m_a1     ( addPoint("a1",      TAssistantPoint::CircleFill, TPointD(90,  0)) ),
b52c5a
    m_b1     ( addPoint("b1",      TAssistantPoint::Circle,     TPointD(50, 40)) )
b52c5a
  {
b52c5a
    addProperty( new TBoolProperty(m_idFixScale.str(),  getFixScale()) );
b52c5a
    addProperty( new TBoolProperty(m_idFixAspect.str(), getFixAspect()) );
b52c5a
    addProperty( new TBoolProperty(m_idFixAngle.str(),  getFixAngle()) );
b52c5a
    addProperty( new TBoolProperty(m_idFixSkew.str(),   getFixSkew()) );
b52c5a
    addProperty( new TIntProperty(m_idCount.str(), 0, multiplierSoftLimit - 1, getCount()) );
b52c5a
    addProperty( new TIntProperty(m_idCountInv.str(), 0, multiplierSoftLimit - 1, getCountInv()) );
b52c5a
    addProperty( new TDoubleProperty(m_idPressure.str(), 0.0, 2.0, getPressure()) );
b52c5a
  }
b52c5a
b52c5a
  
b52c5a
  static QString getLocalName()
b52c5a
    { return tr("Replicator Affine"); }
b52c5a
b52c5a
    
b52c5a
  void updateTranslation() const override {
b52c5a
    TReplicator::updateTranslation();
b52c5a
    setTranslation(m_idFixScale, tr("fix scale"));
b52c5a
    setTranslation(m_idFixAspect, tr("fix aspect"));
b52c5a
    setTranslation(m_idFixAngle, tr("fix angle"));
b52c5a
    setTranslation(m_idFixSkew, tr("fix skew"));
b52c5a
    setTranslation(m_idCount, tr("count"));
b52c5a
    setTranslation(m_idCountInv, tr("inv. count"));
b52c5a
    setTranslation(m_idPressure, tr("pressure"));
b52c5a
  }
b52c5a
b52c5a
  
b52c5a
  inline bool getFixScale() const
b52c5a
    { return data()[m_idFixScale].getBool(); }
b52c5a
  inline bool getFixAspect() const
b52c5a
    { return data()[m_idFixAspect].getBool(); }
b52c5a
  inline bool getFixAngle() const
b52c5a
    { return data()[m_idFixAngle].getBool(); }
b52c5a
  inline bool getFixSkew() const
b52c5a
    { return data()[m_idFixSkew].getBool(); }
b52c5a
  inline int getCount() const
b52c5a
    { return (int)data()[m_idCount].getDouble(); }
b52c5a
  inline int getCountInv() const
b52c5a
    { return (int)data()[m_idCountInv].getDouble(); }
b52c5a
  inline double getPressure() const
b52c5a
    { return data()[m_idPressure].getDouble(); }
b52c5a
b52c5a
protected:
b52c5a
  inline void setCount(int x)
b52c5a
    { if (getCount() != (double)x) data()[m_idCount].setDouble((double)x); }
b52c5a
  inline void setCountInv(int x)
b52c5a
    { if (getCountInv() != (double)x) data()[m_idCountInv].setDouble((double)x); }
b52c5a
  inline void setPressure(double x)
b52c5a
    { if (getPressure() != x) data()[m_idPressure].setDouble(x); }
b52c5a
b52c5a
    
b52c5a
  void onSetDefaults() override {
b52c5a
    setCount(1);
b52c5a
    setPressure(1);
b52c5a
    TReplicator::onSetDefaults();
b52c5a
  }
b52c5a
b52c5a
  
b52c5a
  void onFixData() override {
b52c5a
    TReplicator::onFixData();
b52c5a
    setCount( std::max(0, std::min(multiplierSoftLimit - 1, getCount())) );
b52c5a
    setCountInv( std::max(0, std::min(multiplierSoftLimit - 1, getCountInv())) );
b52c5a
    setPressure( std::max(0.0, std::min(2.0, getPressure())) );
b52c5a
  }
b52c5a
b52c5a
b52c5a
  TAffine getAffine() const {
b52c5a
    TPointD c, x, y;
b52c5a
    c = m_center0.position;
b52c5a
    x = m_a0.position - c;
b52c5a
    y = m_b0.position - c;
b52c5a
    TAffine t0( x.x, y.x, c.x,
b52c5a
                x.y, y.y, c.y );
b52c5a
    c = m_center1.position;
b52c5a
    x = m_a1.position - c;
b52c5a
    y = m_b1.position - c;
b52c5a
    TAffine t1( x.x, y.x, c.x,
b52c5a
                x.y, y.y, c.y );
b52c5a
    return t1*t0.inv();
b52c5a
  }
b52c5a
  
b52c5a
b52c5a
  void onFixPoints() override {
b52c5a
    TPointD &c0 = m_center0.position;
b52c5a
    TPointD &a0 = m_a0.position;
b52c5a
    TPointD &b0 = m_b0.position;
b52c5a
    
b52c5a
    TPointD &c1 = m_center1.position;
b52c5a
    TPointD &a1 = m_a1.position;
b52c5a
    TPointD &b1 = m_b1.position;
b52c5a
b52c5a
    if (getFixScale()) {
b52c5a
      double la0 = tdistance(a0, c0);
b52c5a
      double lb0 = tdistance(b0, c0);
b52c5a
      double la1 = tdistance(a1, c1);
b52c5a
      double lb1 = tdistance(b1, c1);
b52c5a
      a1 = la1 > TConsts::epsilon
b52c5a
         ? (a1 - c1)*(la0/la1) + c1
b52c5a
         : a0 - c0 + c1;
b52c5a
      b1 = lb1 > TConsts::epsilon
b52c5a
         ? (b1 - c1)*(lb0/lb1) + c1
b52c5a
         : b0 - c0 + c1;
b52c5a
    } else
b52c5a
    if (getFixAspect()) {
b52c5a
      double la0 = tdistance(a0, c0);
b52c5a
      double lb0 = tdistance(b0, c0);
b52c5a
      double la1 = tdistance(a1, c1);
b52c5a
      double lb1 = tdistance(b1, c1);
b52c5a
      if (la0 > TConsts::epsilon)
b52c5a
        b1 = lb1 > TConsts::epsilon
b52c5a
           ? (b1 - c1)*(lb0/lb1*la1/la0) + c1
b52c5a
           : (b0 - c0)*(la1/la0) + c1;
b52c5a
    }
b52c5a
b52c5a
    if (getFixAngle()) {
b52c5a
      double la0 = tdistance(a0, c0);
b52c5a
      double lb0 = tdistance(b0, c0);
b52c5a
      double la1 = tdistance(a1, c1);
b52c5a
      double lb1 = tdistance(b1, c1);
b52c5a
      if (la0 > TConsts::epsilon)
b52c5a
        a1 = (a0 - c0)*(la1/la0) + c1;
b52c5a
      if (lb0 > TConsts::epsilon)
b52c5a
        b1 = (b0 - c0)*(lb1/lb0) + c1;
b52c5a
    } else
b52c5a
    if (getFixSkew()) {
b52c5a
      TPointD da0 = a0 - c0;
b52c5a
      TPointD pa0(-da0.y, da0.x);
b52c5a
      double x = (b0 - c0)*da0;
b52c5a
      double y = (b0 - c0)*pa0;
b52c5a
        
b52c5a
      TPointD da1 = a1 - c1;
b52c5a
      TPointD pa1(-da1.y, da1.x);
b52c5a
      TPointD p = da1*x + pa1*y;
b52c5a
      double l = norm2(p);
b52c5a
      if (l > TConsts::epsilon*TConsts::epsilon)
b52c5a
        b1 = p*sqrt(tdistance2(b1, c1)/l) + c1;
b52c5a
    }
b52c5a
  }
b52c5a
  
b52c5a
  
b52c5a
  void onMovePoint(TAssistantPoint &point, const TPointD &position) override {
b52c5a
    TPointD pc0 = m_center0.position;
b52c5a
    TPointD pc1 = m_center1.position;
b52c5a
    point.position = position;
b52c5a
    if (&point == &m_center0) {
b52c5a
      TPointD d = m_center0.position - pc0;
b52c5a
      m_a0.position += d;
b52c5a
      m_b0.position += d;
b52c5a
      m_a1.position += d;
b52c5a
      m_b1.position += d;
b52c5a
      m_center1.position += d;
b52c5a
    } else
b52c5a
    if (&point == &m_center1) {
b52c5a
      TPointD d = m_center1.position - pc1;
b52c5a
      m_a1.position += d;
b52c5a
      m_b1.position += d;
b52c5a
    }
b52c5a
    onFixPoints();
b52c5a
  }
b52c5a
  
b52c5a
  
b52c5a
  void onDataFieldChanged(const TStringId &name, const TVariant &value) override {
b52c5a
    TReplicator::onDataFieldChanged(name, value);
b52c5a
    if ( name == m_idFixScale
b52c5a
      || name == m_idFixAspect
b52c5a
      || name == m_idFixAngle
b52c5a
      || name == m_idFixSkew )
b52c5a
        fixPoints();
b52c5a
  }
b52c5a
b52c5a
public:
b52c5a
  int getMultipler() const override
b52c5a
    { return getCount() + 1; }
b52c5a
  
b52c5a
  
b52c5a
  static TAffine makeTiltTransform(TAffine a) {
b52c5a
    double l1 = a.a11*a.a11 + a.a21*a.a22;
b52c5a
    double l2 = a.a11*a.a11 + a.a21*a.a22;
b52c5a
    double l = std::max(l1, l2);
b52c5a
    double k = l > TConsts::epsilon*TConsts::epsilon ? 1/sqrt(l) : 0;
b52c5a
    a.a11 *= k;
b52c5a
    a.a12 *= k;
b52c5a
    a.a21 *= k;
b52c5a
    a.a22 *= k;
b52c5a
    a.a13 = a.a23 = 0;
b52c5a
    return a;
b52c5a
  }
b52c5a
  
b52c5a
  
b52c5a
  void getModifiers(
b52c5a
    const TAffine&,
b52c5a
    TInputModifier::List &outModifiers ) const override
b52c5a
  {
b52c5a
    TAffine aff = getAffine();
b52c5a
    double pressure = getPressure();
b52c5a
    double pressureInv = fabs(pressure) > TConsts::epsilon ? 1/pressure : 0;
b52c5a
    
b52c5a
    struct {
b52c5a
      int count;
b52c5a
      TAffine aff;
b52c5a
      double pressure;
b52c5a
    } t[] = {
b52c5a
      { getCount(), aff, pressure },
b52c5a
      { getCountInv(), aff.inv(), pressureInv },
b52c5a
    };
b52c5a
b52c5a
    TModifierClone *modifier = new TModifierClone();
b52c5a
    for(int i = 0; i < 2; ++i) {
b52c5a
      TTrackTransform tt;
b52c5a
      for(int j = 0; j < t[i].count; ++j) {
b52c5a
        tt.transform = t[i].aff * tt.transform;
b52c5a
        tt.tiltTransform = makeTiltTransform(tt.transform);
b52c5a
        tt.pressureScale *= t[i].pressure;
b52c5a
        modifier->transforms.push_back(tt);
b52c5a
      }
b52c5a
    }
b52c5a
    
b52c5a
    outModifiers.push_back(modifier);
b52c5a
  }
b52c5a
b52c5a
  
b52c5a
  void draw(TToolViewer*, bool enabled) const override {
b52c5a
    double alpha = getDrawingAlpha(enabled);
b52c5a
    double pixelSize = sqrt(tglGetPixelSize2());
b52c5a
b52c5a
    TPointD c = m_center0.position;
b52c5a
    TPointD a = m_a0.position;
b52c5a
    TPointD b = m_b0.position;
b52c5a
    TAffine aff = getAffine();
b52c5a
b52c5a
    // draw base
b52c5a
    drawSegment(c, a, pixelSize, alpha);
b52c5a
    drawSegment(c, b, pixelSize, alpha);
b52c5a
    
b52c5a
    // draw clones
b52c5a
    TAffine t;
b52c5a
    for(int i = getCount(); i > 0; --i) {
b52c5a
      t = aff * t;
b52c5a
      drawSegment(t*c, t*a, pixelSize, alpha);
b52c5a
      drawSegment(t*c, t*b, pixelSize, alpha);
b52c5a
    }
b52c5a
    
b52c5a
    // draw inverted clones
b52c5a
    t = TAffine();
b52c5a
    aff = aff.inv();
b52c5a
    for(int i = getCountInv(); i > 0; --i) {
b52c5a
      t = aff * t;
b52c5a
      drawSegment(t*c, t*a, pixelSize, alpha);
b52c5a
      drawSegment(t*c, t*b, pixelSize, alpha);
b52c5a
    }
b52c5a
  }
b52c5a
};
b52c5a
b52c5a
b52c5a
//*****************************************************************************************
b52c5a
//    Registration
b52c5a
//*****************************************************************************************
b52c5a
b52c5a
static TAssistantTypeT<treplicatoraffine> replicatorAffine("replicatorAffine");</treplicatoraffine>