Skip to content

Commit

Permalink
[TD]allow rotation of hatch directions
Browse files Browse the repository at this point in the history
- faces can have their svg and bitmap hatches rotated to match
  the view rotation or special situations
- section cut face can have svg hatch rotated
  • Loading branch information
WandererFan committed Dec 3, 2022
1 parent 53804d7 commit 276e6c4
Show file tree
Hide file tree
Showing 17 changed files with 403 additions and 123 deletions.
57 changes: 48 additions & 9 deletions src/Mod/TechDraw/App/DrawGeomHatch.cpp
Expand Up @@ -86,6 +86,10 @@ DrawGeomHatch::DrawGeomHatch()
ADD_PROPERTY_TYPE(ScalePattern, (1.0), vgroup, App::Prop_None,
"GeomHatch pattern size adjustment");
ScalePattern.setConstraints(&scaleRange);
ADD_PROPERTY_TYPE(PatternRotation, (0.0), vgroup, App::Prop_None,
"Pattern rotation in degrees anticlockwise");
ADD_PROPERTY_TYPE(PatternOffset, (0.0, 0.0, 0.0), vgroup, App::Prop_None,
"Pattern offset");

m_saveFile = "";
m_saveName = "";
Expand Down Expand Up @@ -242,14 +246,17 @@ std::vector<LineSet> DrawGeomHatch::getTrimmedLines(int i) //get the trimmed
Base::Console().Log("DGH::getTrimmedLines - no source geometry\n");
return result;
}
return getTrimmedLines(source, m_lineSets, i, ScalePattern.getValue());
return getTrimmedLines(source, m_lineSets, i, ScalePattern.getValue(),
PatternRotation.getValue(), PatternOffset.getValue());
}

/* static */
std::vector<LineSet> DrawGeomHatch::getTrimmedLinesSection(DrawViewSection* source,
std::vector<LineSet> lineSets,
TopoDS_Face f,
double scale )
double scale,
double hatchRotation,
Base::Vector3d hatchOffset)
{
std::vector<LineSet> result;
gp_Pln p;
Expand All @@ -270,26 +277,35 @@ std::vector<LineSet> DrawGeomHatch::getTrimmedLinesSection(DrawViewSection* sou
result = getTrimmedLines(source,
lineSets,
fMoved,
scale );
scale,
hatchRotation,
hatchOffset );
return result;
}

//! get hatch lines trimmed to face outline
std::vector<LineSet> DrawGeomHatch::getTrimmedLines(DrawViewPart* source, std::vector<LineSet> lineSets, int iface, double scale )
std::vector<LineSet> DrawGeomHatch::getTrimmedLines(DrawViewPart* source, std::vector<LineSet> lineSets,
int iface, double scale, double hatchRotation ,
Base::Vector3d hatchOffset)
{
TopoDS_Face face = extractFace(source, iface);
std::vector<LineSet> result = getTrimmedLines(source,
lineSets,
face,
scale );
scale,
hatchRotation,
hatchOffset );
return result;
}

std::vector<LineSet> DrawGeomHatch::getTrimmedLines(DrawViewPart* source,
std::vector<LineSet> lineSets,
TopoDS_Face f,
double scale )
double scale,
double hatchRotation,
Base::Vector3d hatchOffset)
{
// Base::Console().Message("DGH::getTrimmedLines() - rotation: %.3f hatchOffset: %s\n", hatchRotation, DrawUtil::formatVector(hatchOffset).c_str());
(void)source;
std::vector<LineSet> result;

Expand All @@ -310,12 +326,26 @@ std::vector<LineSet> DrawGeomHatch::getTrimmedLines(DrawViewPart* source,

//make Compound for this linespec
BRep_Builder builder;
TopoDS_Compound grid;
builder.MakeCompound(grid);
TopoDS_Compound gridComp;
builder.MakeCompound(gridComp);
for (auto& c: candidates) {
builder.Add(grid, c);
builder.Add(gridComp, c);
}

TopoDS_Shape grid = gridComp;
if (hatchRotation != 0.0) {
double hatchRotationRad = hatchRotation * M_PI / 180.0;
gp_Ax1 gridAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Vec(gp::OZ().Direction()));
gp_Trsf xGridRotate;
xGridRotate.SetRotation(gridAxis, hatchRotationRad);
BRepBuilderAPI_Transform mkTransRotate(grid, xGridRotate, true);
grid = mkTransRotate.Shape();
}
gp_Trsf xGridTranslate;
xGridTranslate.SetTranslation(DrawUtil::togp_Vec(hatchOffset));
BRepBuilderAPI_Transform mkTransTranslate(grid, xGridTranslate, true);
grid = mkTransTranslate.Shape();

//Common(Compound, Face)
BRepAlgoAPI_Common mkCommon(face, grid);
if ((!mkCommon.IsDone()) ||
Expand Down Expand Up @@ -369,6 +399,15 @@ std::vector<TopoDS_Edge> DrawGeomHatch::makeEdgeOverlay(PATLineSpec hl, Bnd_Box

double minX, maxX, minY, maxY, minZ, maxZ;
b.Get(minX, minY, minZ, maxX, maxY, maxZ);
//make the overlay bigger to cover rotations. might need to be bigger than 2x.
double centerX = (minX + maxX) / 2.0;
double widthX = maxX - minX;
minX = centerX - widthX;
maxX = centerX + widthX;
double centerY = (minY + maxY) / 2.0;
double widthY = maxY - minY;
minY = centerY - widthY;
maxY = centerY + widthY;

Base::Vector3d start;
Base::Vector3d end;
Expand Down
15 changes: 11 additions & 4 deletions src/Mod/TechDraw/App/DrawGeomHatch.h
Expand Up @@ -61,6 +61,8 @@ class TechDrawExport DrawGeomHatch : public App::DocumentObject
App::PropertyFileIncluded PatIncluded;
App::PropertyString NamePattern;
App::PropertyFloatConstraint ScalePattern;
App::PropertyFloat PatternRotation;
App::PropertyVector PatternOffset;

App::DocumentObjectExecReturn *execute(void) override;
void onChanged(const App::Property* prop) override;
Expand All @@ -77,17 +79,22 @@ class TechDrawExport DrawGeomHatch : public App::DocumentObject

std::vector<LineSet> getFaceOverlay(int i = 0);
std::vector<LineSet> getTrimmedLines(int i = 0);
static std::vector<LineSet> getTrimmedLines(DrawViewPart* dvp, std::vector<LineSet> lineSets, int iface, double scale);
static std::vector<LineSet> getTrimmedLines(DrawViewPart* dvp, std::vector<LineSet> lineSets, int iface,
double scale, double hatchRotation = 0.0,
Base::Vector3d hatchOffset = Base::Vector3d(0.0, 0.0, 0.0));
static std::vector<LineSet> getTrimmedLines(DrawViewPart* source,
std::vector<LineSet> lineSets,
TopoDS_Face face,
double scale );
double scale , double hatchRotation = 0.0,
Base::Vector3d hatchOffset = Base::Vector3d(0.0, 0.0, 0.0));
static std::vector<LineSet> getTrimmedLinesSection(DrawViewSection* source,
std::vector<LineSet> lineSets,
TopoDS_Face f,
double scale );
double scale , double hatchRotation = 0.0,
Base::Vector3d hatchOffset = Base::Vector3d(0.0, 0.0, 0.0));

static std::vector<TopoDS_Edge> makeEdgeOverlay(PATLineSpec hl, Bnd_Box bBox, double scale);
static std::vector<TopoDS_Edge> makeEdgeOverlay(PATLineSpec hl, Bnd_Box bBox,
double scale);
static TopoDS_Edge makeLine(Base::Vector3d s, Base::Vector3d e);
static std::vector<PATLineSpec> getDecodedSpecsFromFile(std::string fileSpec, std::string myPattern);
static TopoDS_Face extractFace(DrawViewPart* source, int iface );
Expand Down
6 changes: 5 additions & 1 deletion src/Mod/TechDraw/App/DrawViewSection.cpp
Expand Up @@ -175,6 +175,8 @@ DrawViewSection::DrawViewSection() :
"Embedded Pat pattern file. System use only."); // n/a to end users
ADD_PROPERTY_TYPE(NameGeomPattern ,(DrawGeomHatch::prefGeomHatchName()), fgroup, App::Prop_None, "The pattern name for geometric hatching");
ADD_PROPERTY_TYPE(HatchScale, (1.0), fgroup, App::Prop_None, "Hatch pattern size adjustment");
ADD_PROPERTY_TYPE(HatchRotation, (0.0), fgroup, App::Prop_None, "Rotation of hatch pattern in degrees anti-clockwise");
ADD_PROPERTY_TYPE(HatchOffset, (0.0, 0.0, 0.0), fgroup, App::Prop_None, "Hatch pattern offset");

getParameters();

Expand Down Expand Up @@ -980,7 +982,9 @@ std::vector<LineSet> DrawViewSection::getDrawableLines(int i)
std::vector<LineSet> result;
result = DrawGeomHatch::getTrimmedLinesSection(this, m_lineSets,
getSectionTopoDSFace(i),
HatchScale.getValue());
HatchScale.getValue(),
HatchRotation.getValue(),
HatchOffset.getValue());
return result;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Mod/TechDraw/App/DrawViewSection.h
Expand Up @@ -102,6 +102,8 @@ class TechDrawExport DrawViewSection : public DrawViewPart
App::PropertyFileIncluded PatIncluded;
App::PropertyString NameGeomPattern;
App::PropertyFloat HatchScale;
App::PropertyFloat HatchRotation;
App::PropertyVector HatchOffset;

App::PropertyBool FuseBeforeCut;
App::PropertyBool TrimAfterCut; //new v021
Expand Down
93 changes: 55 additions & 38 deletions src/Mod/TechDraw/Gui/QGIFace.cpp
Expand Up @@ -68,7 +68,8 @@ using namespace TechDraw;

QGIFace::QGIFace(int index) :
projIndex(index),
m_hideSvgTiles(false)
m_hideSvgTiles(false),
m_hatchRotation(0.0)
{
m_segCount = 0;
// setFillMode(NoFill);
Expand Down Expand Up @@ -257,16 +258,17 @@ void QGIFace::lineSetToFillItems(LineSet& ls)
{
m_segCount = 0;
QPen pen = setGeomPen();
for (auto& g: ls.getGeoms()) {
for (auto& geom : ls.getGeoms()) {
//geom is a tdGeometry representation of 1 line in the pattern
if (ls.isDashed()) {
double offset = 0.0;
Base::Vector3d pStart = ls.getPatternStartPoint(g, offset, m_fillScale);
Base::Vector3d pStart = ls.getPatternStartPoint(geom, offset, m_fillScale);
offset = Rez::guiX(offset);
Base::Vector3d gStart(g->getStartPoint().x,
g->getStartPoint().y,
Base::Vector3d gStart(geom->getStartPoint().x,
geom->getStartPoint().y,
0.0);
Base::Vector3d gEnd(g->getEndPoint().x,
g->getEndPoint().y,
Base::Vector3d gEnd(geom->getEndPoint().x,
geom->getEndPoint().y,
0.0);
if (DrawUtil::fpCompare(offset, 0.0, 0.00001)) { //no offset
QGraphicsPathItem* item1 = lineFromPoints(pStart, gEnd, ls.getDashSpec());
Expand All @@ -279,12 +281,12 @@ void QGIFace::lineSetToFillItems(LineSet& ls)
}
} else { //offset - pattern start not in g
double remain = dashRemain(decodeDashSpec(ls.getDashSpec()), offset);
QGraphicsPathItem* shortItem = geomToStubbyLine(g, remain, ls);
QGraphicsPathItem* shortItem = geomToStubbyLine(geom, remain, ls);
shortItem->setPen(pen);
m_fillItems.push_back(shortItem);
}
} else { //not dashed
QGraphicsPathItem* fillItem = geomToLine(g, ls);
QGraphicsPathItem* fillItem = geomToLine(geom, ls);
fillItem->setPen(pen);
m_fillItems.push_back(fillItem);
}
Expand Down Expand Up @@ -520,19 +522,20 @@ void QGIFace::makeMark(double x, double y)

void QGIFace::buildSvgHatch()
{
// Base::Console().Message("QGIF::buildSvgHatch() - offset: %s\n", DrawUtil::formatVector(getHatchOffset()).c_str());
double wTile = SVGSIZEW * m_fillScale;
double hTile = SVGSIZEH * m_fillScale;
double w = m_outline.boundingRect().width();
double h = m_outline.boundingRect().height();
QRectF r = m_outline.boundingRect();
QPointF fCenter = r.center();
double nw = ceil(w / wTile);
double nh = ceil(h / hTile);
//make the hatch tiled area big enough to handle rotations
double hatchOverlaySize = 2.0 * std::max(w, h);
QPointF faceCenter = m_outline.boundingRect().center();
double nw = ceil(hatchOverlaySize / wTile);
double nh = ceil(hatchOverlaySize / hTile);
w = nw * wTile;
h = nh * hTile;
m_rect->setRect(0., 0., w,-h);
m_rect->centerAt(fCenter);
r = m_rect->rect();
m_rect->centerAt(faceCenter);
QByteArray before, after;
before = QString::fromStdString(SVGCOLPREFIX + SVGCOLDEFAULT).toUtf8();
after = QString::fromStdString(SVGCOLPREFIX + m_svgCol).toUtf8();
Expand All @@ -544,7 +547,8 @@ void QGIFace::buildSvgHatch()
tile->setScale(m_fillScale);
if (tile->load(&colorXML)) {
tile->setParentItem(m_rect);
tile->setPos(iw*wTile, -h + ih*hTile);
tile->setPos(iw*wTile + getHatchOffset().x,
-h + ih*hTile + getHatchOffset().y);
}
tileCount++;
if (tileCount > m_maxTile) {
Expand All @@ -556,6 +560,9 @@ void QGIFace::buildSvgHatch()
break;
}
}
QPointF faceCenterToMRect = mapToItem(m_rect, faceCenter);
m_rect->setTransformOriginPoint(faceCenterToMRect);
m_rect->setRotation(m_hatchRotation);
}

void QGIFace::clearSvg()
Expand All @@ -565,21 +572,22 @@ void QGIFace::clearSvg()

void QGIFace::buildPixHatch()
{
// Base::Console().Message("QGIF::buildPixHatch() - offset: %s\n", DrawUtil::formatVector(getHatchOffset()).c_str());
double wTile = SVGSIZEW * m_fillScale;
double hTile = SVGSIZEH * m_fillScale;
double w = m_outline.boundingRect().width();
double h = m_outline.boundingRect().height();
QRectF r = m_outline.boundingRect();
QPointF fCenter = r.center();
double nw = ceil(w / wTile);
double nh = ceil(h / hTile);
w = nw * wTile;
h = nh * hTile;

m_rect->setRect(0., 0., w,-h);
m_rect->centerAt(fCenter);
double faceWidth = m_outline.boundingRect().width();
double faceHeight = m_outline.boundingRect().height();
QRectF faceRect = m_outline.boundingRect();
QPointF faceCenter = faceRect.center();
double hatchOverlaySize = 2.0 * std::max(faceWidth, faceHeight);
double numberWide = ceil(hatchOverlaySize / wTile);
double numberHigh = ceil(hatchOverlaySize / hTile);
double overlayWidth = numberWide * wTile;
double overlayHeight= numberHigh * hTile;

m_rect->setRect(0., 0., overlayWidth, -overlayHeight);
m_rect->centerAt(faceCenter);

r = m_rect->rect();
QByteArray before, after;
before = QString::fromStdString(SVGCOLPREFIX + SVGCOLDEFAULT).toUtf8();
after = QString::fromStdString(SVGCOLPREFIX + m_svgCol).toUtf8();
Expand All @@ -590,16 +598,16 @@ void QGIFace::buildPixHatch()
Base::Console().Error("QGIF::buildPixHatch - renderer failed to load\n");
}

//get the svg tile graphics as a QImage
QImage imageIn(64, 64, QImage::Format_ARGB32);
imageIn.fill(Qt::transparent);
QPainter painter(&imageIn);

renderer.render(&painter);
if (imageIn.isNull()) {
Base::Console().Error("QGIF::buildPixHatch - imageIn is null\n");
return;
}

//make a QPixmap tile of the QImage
QPixmap pm(64, 64);
pm = QPixmap::fromImage(imageIn);
pm = pm.scaled(wTile, hTile);
Expand All @@ -608,23 +616,26 @@ void QGIFace::buildPixHatch()
return;
}

QImage tileField(w, h, QImage::Format_ARGB32);
QPointF fieldCenter(w / 2.0, h / 2.0);
//layout a field of QPixmap tiles as a QImage
QImage tileField(overlayWidth, overlayHeight, QImage::Format_ARGB32);
QPointF fieldCenter(overlayWidth, overlayHeight);

//do we have to rotate the field before we clip it??
tileField.fill(Qt::transparent);
QPainter painter2(&tileField);
QPainter::RenderHints hints = painter2.renderHints();
hints = hints & QPainter::Antialiasing;
painter2.setRenderHints(hints);
QPainterPath clipper = path();
QPointF offset = (fieldCenter - fCenter);
QPointF offset = (fieldCenter - faceCenter);
clipper.translate(offset);
painter2.setClipPath(clipper);

long int tileCount = 0;
for (int iw = 0; iw < int(nw); iw++) {
for (int ih = 0; ih < int(nh); ih++) {
painter2.drawPixmap(QRectF(iw*wTile, ih*hTile, wTile, hTile), //target rect
for (int iw = 0; iw < int(numberWide); iw++) {
for (int ih = 0; ih < int(numberHigh); ih++) {
painter2.drawPixmap(QRectF(iw*wTile + getHatchOffset().x, ih*hTile + getHatchOffset().y,
wTile, hTile), //target rect
pm, //map
QRectF(0, 0, wTile, hTile)); //source rect
tileCount++;
Expand All @@ -637,13 +648,14 @@ void QGIFace::buildPixHatch()
break;
}
}
QPixmap bigMap(fabs(r.width()), fabs(r.height()));

QPixmap bigMap(fabs(faceRect.width()), fabs(faceRect.height()));
bigMap = QPixmap::fromImage(tileField);

QPixmap nothing;
m_image->setPixmap(nothing);
m_image->load(bigMap);
m_image->centerAt(fCenter);
m_image->centerAt(faceCenter);
}

//this isn't used currently
Expand Down Expand Up @@ -692,6 +704,11 @@ QPixmap QGIFace::textureFromBitmap(std::string fileSpec)
}
QByteArray bytes = f.readAll();
pix.loadFromData(bytes);
if (m_hatchRotation != 0.0) {
QTransform rotator;
rotator.rotate(m_hatchRotation);
pix = pix.transformed(rotator);
}
return pix;
}

Expand Down

0 comments on commit 276e6c4

Please sign in to comment.