Teigha BIM: Introduction to FillPatterns [Part 1]

Daniil Zhirnov

July 04, 2017

OdBmFillPatternElem is bound to a face via OdBmFace->OdBmGFilling->PatternId. So while traversing faces in BrepRenderer, for every face we obtain an OdBmFillPatternElem ID via IBrFace->getFillPatternId(). See the code below:

OdUInt64 id;
if (face.getFillPatternId(id))
{
  if (pWd)
  {
    OdDbStub* pStub = pWd->context()->getStubByID(id);
    if (pStub)
    {
      OdGiDrawablePtr pDrawable = pWd->context()->openDrawable(pStub);
      if (!pDrawable.isNull())
      {
        pDrawable->setAtribute(&pWd->subEntityTraits());
      }
    }
  }
}

Since BIM-specific classes can not be used, this ID is opened as a drawable via GiContext and is supplied with entity traits through setAttributes(). This leads to an OdBmFillPatternElem:: subSetAttributes(OdGiDrawableTraits* pDrwTraits) call that takes responsibility of supplying the correct OdGiFill traits attribute. This can be shown schematically as:

if (m_pFill.isNull())
{
  m_pFill = OdGiHatchPattern::createObject();
  const OdBmFillPattern* pFillPattern = getFillPattern().get();
  if (pFillPattern)
  {
    // Do OdBmFillPattern to OdGiHatchPattern cobversion
  }
}

OdGiSubEntityTraits* pSub = static_cast< OdGiSubEntityTraits* >(pDrwTraits);
pSub->setFill(m_pFill);
if (m_pFill->patternLines().size() == 0)
{
  // Set solid fill attributes
}

OdGiHatchPatternPtr m_pFill is implemented as a member, so a OdBmFillPattern to OdGiHatchPattern conversion is not required again on subsequent calls. Conversion is pretty straight-forward except for simple patterns that can be stored in a new format.

double dx, d0 = (*i)->getDeltasItem(0);
double dy, d1 = (*i)->getDeltasItem(1);
if(OdZero(d0))
{
  double sina = fabs(sin(pl->m_dLineAngle));
  double cosa = fabs(cos(pl->m_dLineAngle));

  if (sina > cosa)
  {
    dx = d1 / sina;
    dy = 0.0;
  }
  else
  {
    dx = 0.0;
    dy = d1 / cosa;
  }
}
else
{
  dx = d0;
  dy = d1;
} 

If DeltasItem[0] is zero, we need to calculate x, y from the given a and d = DeltasItem[1].

image1

Finally, we have entity traits with OdGiHatchPattern that are set as a fill attribute which describes hatch structure in general. This is not enough for correct rendering of FillPattern; OdBmFillPatternPlacer data also needs to be supplied, which provides a face-specific hatch starting point and hatch direction. So there could be many faces referring to the same OdBmFillPatternElem that are drawn differently. OdGiFaceData is used to pass OdBmFillPatternPlacer attributes to the Gi-subsystem. IBrFace is extended to support this attribute access within BrepRenderer.

OdGiFaceData FaceData;
OdGePoint2dArray fillOriginArr;
OdGeVector2dArray fillDirectionArr;
{
  OdGePoint2d faceFillOrigin;
  OdGeVector2d faceFillDirection;
  if (face.getFillingAttributes(faceFillOrigin, faceFillDirection))
  {
    fillOriginArr.resize(FAceList.size(), faceFillOrigin);
    FaceData.setFillOrigins(FillOriginArr.getPtr());

    fillDirectionArr.resize(FaceList.size(), faceFillDirection);
    FaceData.setFillDirections(fillDirectionArr.getPtr());
  }
}

Teigha BIM uses a 2D coordinate system for FillPatterns that lies on the plane of the face. So every face has its own coordinate system, and it has to be taken into account when calculating the fill origin 3D coordinates. This coordinate system is saved as XVector,YVector and Origin of the corresponding OdBmPlane, so it helps to make the conversion straight-forward.

OdBmFillPatternPlacerPtr pPlacer = pFilling->getPlacer();
if (!pPlacer.isNull())
{
  OdGePoint2d baseOrigin = pFilling->getPlacer();
  OdBmPlanePtr pPlane = OdBmPlane::cast(((OdBmFace*)m_pImp)->getSurface());
  if (!pPlane.isNull())
  {
    origin = (pPlane->getOrigin() + baseOrigin.x * pPlane->getXVector() + baseOrigin.y * pPlane->getYVector()).convert2d();
  }
}

Finally, all the required data is calculated and set so the FillPattern can be rendered.