hatch problem

Discussion forum for C++ and script developers who are using the QCAD development platform or who are looking to contribute to QCAD (translations, documentation, etc).

Moderator: andrew

Forum rules

Always indicate your operating system and QCAD version.

Attach drawing files, scripts and screenshots.

Post one question per topic.

Post Reply
WildWolfCJ
Full Member
Posts: 84
Joined: Fri Oct 20, 2023 7:21 am

hatch problem

Post by WildWolfCJ » Fri Nov 24, 2023 3:04 am

win10 QCAD3.28.2 PRO
Hello everyone, the attachment is a DXF test file I designed, which contains a hatch area. I am now using the winform netdxf library to parse this DXF file. The obtained hatch entity contains four netDxf.Entities.HatchBoundaryPath objects, but I don’t know this. Which two of the four objects need to be filled and which two do not need to be filled? Is there any flag or method to distinguish them? I need to display the filled area in winform program
Attachments
填充-多图.dxf
(97.57 KiB) Downloaded 145 times
VeryCapture_20231124100314.jpg
VeryCapture_20231124100314.jpg (249.89 KiB) Viewed 5375 times

CVH
Premier Member
Posts: 3480
Joined: Wed Sep 27, 2017 4:17 pm

Re: hatch problem

Post by CVH » Fri Nov 24, 2023 8:47 am

Hi,

This is an island in an island in an ... problem.
You have to determine what HatchBoundaryPath is (fully) inside the other.
Easy for humans to do that: [0] is inside [2], [2] is inside [1] and all that is inside [3]. :lol:

The QCAD hatch engine does this automatically given some rules of engagement to render a hatch.
It gets tricky when loops are self-intersecting or intersecting with other loops.
Not all applications will render these special cases the same way.

Here you may find an image of a very odd hatch with one single boundary loop:
https://www.qcad.org/rsforum/viewtopic. ... 052#p39052
Some applications may render this as completely filled.
And for example TTF fonts are outlines rendered filled disregarding open islands.

Detecting which loop is inside the other is not straightforward.
It is a process of detection and elimination based on all shapes from all boundary paths.
Verifying isLeft or isRight regarding the shapes of the other loop(s).

With an ellipse or an ellipse arc that is mathematically fairly impossible, usually those are approximated with arc-segments.
And somewhat similar for edges that are splines or spline segments.
Arcs may impose other problems as they don't have a start or endpoint, they have start and end angles.
Comparing things requires some degree of tolerance (usually absolute) to account for Floating Point (relative) uncertainty.

Regards,
CVH

WildWolfCJ
Full Member
Posts: 84
Joined: Fri Oct 20, 2023 7:21 am

Re: hatch problem

Post by WildWolfCJ » Tue Nov 28, 2023 3:05 am

HI CVH
For hatches, I don’t need to consider too complicated scenarios. There are two areas that need to be filled in the attachment. Each filled area is determined by two HatchBoundaryPaths. My problem is that I cannot determine the HatchBoundaryPath pair corresponding to each hatch. I searched I've searched a lot of information but can't find the corresponding relationship. Can you give me some guidance?

CVH
Premier Member
Posts: 3480
Joined: Wed Sep 27, 2017 4:17 pm

Re: hatch problem

Post by CVH » Tue Nov 28, 2023 5:42 am

The most minimalistic approach would be to take a point on each BoundaryPath and verify if that is inside the other BoundaryPath's.
This method makes several assumptions:
- Any random point on a BoundaryPath will do
- Locally a number of boundary loops except one are inside other loops
- Boundary loops do not touch others
- Boundary loops do not intersect with others
- Boundary loops do not self-intersect
- ...
And in a way you need to verify all these conditions. :(

The QCAD resources have some methods to accomplish the minimalistic approach.
The second part of the code below is typically implemented as a function to determine the order of the BoundaryPath's nested nature.

Code: Select all

var hatchEntity;    // A hatch entity queried from the document
var segmentLength;    // Segment length to approximate curved line-art

// = = = = = = = = = 

var loops = hatchEntity.getBoundaryAsPolylines(segmentLength);
var nestOrders = []
var iMax = loops.length;
if (iMax < 1) return undefined;    // Failed, no boundary loops
if (iMax < 2) return [0];    // Single boundary loop

// Process all hatch boundary loops for their nested nature:
for (var i=0; i<iMax; i++) {
    var loop = loops[i];

    // Verify with other boundary loops:
    var nested = 0;
    var point = loop.getStartPoint();
    for (var j=0; j<iMax; j++) {
        if (i === j) continue;    // Skip itself
        var trial = loops[j];
        // Verify if the boundary is nested inside the one on trial:
        // A) Exclude boundaries that are not even close box based:
        if (!trial.getBoundingBox().contains(loop.getBoundingBox())) {
            continue;    // Is not nested
        }
        // B) Verify if a random vertex is inside the trial boundary.
        if (trial.contains(point, false, RS.PointTolerance)) {    // NotOn poly with tolerance = 1e-9
            nested++;    // Is nested in loop
        }
    }
    
    // Collect the order of the nested nature:
    nestOrders.push(nested);
}
return nestOrders;
You are not out the woods yet. :(

Let's assume this returns [0, 2, 1].
An even count is filled, an uneven not, loop 2 is inside loop 3 and all that is inside loop 1.
So far so good. Note that I index loops starting with 1, an array index is zero based.

With 4 loops this may return [0, 2, 0, 1].
Loops 1 and 3 are not inside another loop, loop 2 is most likely inside 4 but it doesn't tell you if loop 4 is inside 1 or inside 3.
And even harder with this 5 loops example [0, 2, 0, 1, 1] ... 4 and 5 may be both or not inside 1 or 3, 2 is inside 4 or 5.

Per definition it is thus not strictly pairwise as you described it.
Having the nesting order it is a question of further determination of what is inside what starting with the highest order(s).
I don't think this can be coded along with the code of the above routine there you can not predict the outcome of a yet untested nesting order.

Note that curved boundary shapes are approximated by short line segments, which simplifies the internal math.
This may be sufficient for most purposes ... :|
Too coarse it may return rubbish, way too fine the contain algorithm may take forever.

Another method may rely on a point inside a certain area to fill or not.
Counting the times that a ray starting in this point intersects with the boundary loops.
Even or uneven intersections is then again related to hatched or not, intersecting or self-intersecting loops are accounted for.
The trouble here is that the number of intersections may vary according the tested orientation.
With even and uneven counts it is not uniquely defined if an enclosed area should be filled or not.
In these complex situations other rules need to be implemented.

Regards,
CVH
Last edited by CVH on Tue Nov 28, 2023 3:13 pm, edited 1 time in total.

CVH
Premier Member
Posts: 3480
Joined: Wed Sep 27, 2017 4:17 pm

Re: hatch problem

Post by CVH » Tue Nov 28, 2023 12:51 pm

WildWolfCJ wrote:
Tue Nov 28, 2023 3:05 am
For hatches, I don’t need to consider too complicated scenarios
The 2-3 Loops below are not complicated, each one is a simple convex polygon.
WildWolfCJ wrote:
Tue Nov 28, 2023 3:05 am
Each filled area is determined by two HatchBoundaryPaths.
Not when the loops are intersecting. Then the enclosed area of a single loop may be filled but also not filled.
The minimalistic approach will not work there both startpoints of the yellow and orange loop are not inside the other.
On the bottom row the yellow and orange loop are both 1 level inside the white loop.
WildWolfCJ wrote:
Tue Nov 28, 2023 3:05 am
My problem is that I cannot determine the HatchBoundaryPath pair corresponding to each hatch.
Because you make the assumption that it is a simple pairwise relationship what is only the case for trivial layouts.
You first need to establish that it is indeed a trivial case. :wink:

Some examples:
SomeHatchLoops.png
SomeHatchLoops.png (7.78 KiB) Viewed 5117 times

With a simple flag (Not supported by QCAD) the resulting hatched areas may look like the examples below.
SomeHatchLoopsAlt.png
SomeHatchLoopsAlt.png (991 Bytes) Viewed 5117 times

Bottom line, DXF only stores the geometry of the loops, what can be in any order.
It is up to the application to render the hatched area as required.

Regards,
CVH

WildWolfCJ
Full Member
Posts: 84
Joined: Fri Oct 20, 2023 7:21 am

Re: hatch problem

Post by WildWolfCJ » Wed Nov 29, 2023 7:50 am

HI CVH
I now use a very simple way to deal with this problem. Of course, my method is very imperfect. I calculate the area of ​​each graphic based on HatchBoundaryPath, and then fill it in pairs according to the area size. Your method is so complicated that it is difficult for me to implement it.

CVH
Premier Member
Posts: 3480
Joined: Wed Sep 27, 2017 4:17 pm

Re: hatch problem

Post by CVH » Wed Nov 29, 2023 8:24 am

WildWolfCJ wrote:
Wed Nov 29, 2023 7:50 am
Your method is so complicated that it is difficult for me to implement it.
It is not my method but a method for trivial cases.
WildWolfCJ wrote:
Wed Nov 29, 2023 7:50 am
I calculate the area of ​​each graphic based on HatchBoundaryPath, and then fill it in pairs according to the area size.
Disregarding that loops can self-intersect and then the area calculation may be incorrect or complete rubbish depending the algorithm.
Disregarding that loops may intersect each other and then the area value isn't a correct indication factor.
How correct/robust are your resources for the elliptic arc area, spline area, polylines with arcs or chain of arcs area?
WildWolfCJ wrote:
Wed Nov 29, 2023 7:50 am
Of course, my method is very imperfect.
Most likely only fit for clean 1 IN 1 relations.
What if there are two islands side by side?

Regards,
CVH

Post Reply

Return to “QCAD Programming, Script Programming and Contributing”