I’m working on a script to identify overlapping, intersecting and non-connected entities. It will process up to about 100 entities at once just fine. If I try more than that, QCAD crashes. Is there some kind of watchdog running or other timed function that could be blocked by the script that would cause this to happen? Am I barking up the wrong tree? What could be going on here?
I suspect that the error is immediate but only leads to the crash once the garbage collectors cleans up non-referenced objects. If you can post the script, I can likely point you to the problem.
Posted the script files and a dxf that it crashes on, (if more than 2 of the characters selected).
Basil Garden Marker DXF.dxf (49.7 KB)
Part.js (2.26 KB)
PreferencesPage.ui (15 KB)
PartPrepInit.js (519 Bytes)
PartPrep.js (25.9 KB)
Hi butch,
One thing I remark within your code is the frequent use of for example doc.queryEntityDirect().
And that you change those entities. Sometimes without an operation.
Please refer to:
Another dangerous thing is long inline code sequences where an intermediate object is sourced for example:
- from an array, altered and put back into another array.
The reference of the intermediate object is not stored in a value and at some point the garbage collector will delete that.
At some points you need to work with/on clones, you can not do 2 different things with an object in an array as it is by reference.
The code already starts funny:
// Move all selected entities to 0 layer and flatten to 2D
var layerName = "0";
if (!document.hasLayer(layerName)) {
...
Have you ever tried to remove or rename layer ‘0’ ?
It is a mandatory layer, each document will have that.
document.setCurrentLayer(layerName);
document or di ? The difference is subtle.
for (i=0; i<selectedIds.length; i++) {
document.queryEntityDirect(selectedIds[i]).setLayerName(layerName);
document.queryEntityDirect(selectedIds[i]).to2D();
}
document.updateAllEntities();
di.deselectAll();
Probably functional but it is not an operation, you handle the REntity’s in direct.
The REntity object itself is intermediate. Agreed, the usage is not long lived in this case.
op = new RModifyObjectsOperation();
for (i=0; i<selectedIds.length; i++) {
var e = document.queryEntity(selectedIds[i]);
e.to2D();
e.setLayerName(layerName);
e.setSelected(false);
op.addObject(e, false, false); // NOTuseCurrentAttributes, NOTforceNew
}
di.applyOperation(op);
Would be more appropriate and such an operation can be undone.
At some point you might consider to use a transaction groups to merge different operations into one undo/redo.
For the moment I am still debugging your code with the given example file.
It fails critically at a certain point, for a certain id …
Already remark that if we explode (XP) everything in the drawing then there will be some duplicates (MD =2) and some zero-length (MZ =6)
The goal is of course: Neat and singular closed contours, disregarding duplicates and zeros, merging overlapping, … All in one tool.
MD doesn’t handle overlapping.
Will report back later.
Regards,
CVH
Butch,
Are you sure about this code:
if (isArcShape(candidateShapes[i])) {
if (!spi.equalsFuzzy(epi, PartPrepB.tolerance)) { //not closed curve
candidateShapes[i].moveEndPoint(spi, true);
}
It essentially says that:
When the end point of an arc does not match the start point within some tolerance then move the endpoint to match the start point.
First it alters item i in the list of candidateShapes in situ.
Moving an endpoint of an arc is handled by a new 3 point arc, keeping the radius is true by default.
The chances that you get something else than an invalid RArc back are slim to none. ![]()
Detected that RArc’s turned invalid in PartPrep.getOverlaps between start and if (caught) {continue;}
At some point you get an undefined RShape and that failed critical in ShapeAlgorithms.
Funny on this matter is that appending arcs to a polyline does not record the start point, the endpoint is recorded as the next node.
So, if you want to correct arcs from an exploded polyline then modifying the start point is of interest.
But there is no mathematical way to do this because the bulge factor without the original chord do no longer relate with the original arc.
Regards,
CVH
Butch,
The translation of the arc endpoint seems to be intended to make a circle, “not closed curve” gives that away
Of that an RBox is defined … Whatever arc is in that box may connect, overlap, be a duplicate, be itself or is just another arc.
I changed that code to:
/*
if (!spi.equalsFuzzy(epi, PartPrepB.tolerance)) { //not closed curve
candidateShapes[i].moveEndPoint(spi, true);
}
var box = candidateShapes[i].getBoundingBox();
*/
var c = new RCircle(candidateShapes[i].getCenter(), candidateShapes[i].getRadius());
var box = c.getBoundingBox();
That doesn’t alter candidateShapes #i in situ and it defines a box around the arc but then as full circle.
BTW: if this was polyline code then there are other resources to close an open polyline.
Moving the endpoint will deform the last segment for a geometrical open type.
At the end of PartPrep.getCandidates
I replaced:
candidateShapes.push(tmpShape);
with:
candidateShapes.push(tmpShape.clone());
Now the code doesn’t let QCAD fail critical.
Except that it moves all entities to Layer ‘0’ (with my code fragment)
It seems it is unfinished … ![]()
Regards,
CVH
Many thanks CVH. I followed your advice and my code now runs all the way through.
PartPrep.js (25.8 KB)
Butch,
Is see, you removed the early termination … ![]()
I don’t think that you need document.updateAllEntities(); any longer (2x).
Modifying entities is now with an operation, that will update everything related.
I presume that updateAllEntities updates all from scratch.
In PartPrep.explodeEntity
- For the record: I would call this function explodePolylineEntity, it serves only that purpose.
Exploding other types may require a slightly adapted code. - Avoid polylines with global/local width, their explosion is imperfect with mixed segment type.
Take a clone of the entity and clone.setGlobalWidth(0.0) then explode the clone. - Filter on RLine and RArc shapes (Nothing else is expected from a polyline).
- Only re-create an entity if they are valid isValid(…) (RLine with sane endpoints, RArc with valid center and R>0.0).
- Already avoid zero-length entities at this point.
- Add the object not using current attributes, current means the default for new (Here, they are copied from the REntity).
But there are some catches to it … As there are always.
- An RLine with a length of less than 1e-6 doesn’t play well, the nearest point on it is always RVector.invalid (See: RLine.getVectorTo(p))
All methods that rely on getVectorTo may fail unexpected (e.g.: getDistanceTo>>RNANDOUBLE, isOnShape>>false, …). - Additionally all near the start point within 1e-6 return the start point (= circular area) what doesn’t apply for the end point.
- Non QCAD native documents may still include full circular arc segments, the bulge factor reaches infinity.
That is considered an error: bulge = tan(sweep/4) = tan(pi/2) = Err …
… In this case +Infinite would be OK but you can’t do any math with that, aditionally it can’t be stored in DXF.
Since some time QCAD enforces two semi-circular arc segments instead (bulge = +/-1.0). - RArc.getLength() act strange in limit situations.
Not expected but a mathematical inverse arc (not to be confused with ‘reversed’) has a negative radius and thus a negative length.
Considers abs(Sweep) < RS.PointTolerance to be a full circular arc (0° = circle).
And considers abs(Sweep) > 2PI-RS.PointTolerance to be a zero arc (360° = nothing).
Remind that ‘reversed’ is QCAD native, common DXF arcs are always CCW.
Under ‘move entities of each contour to respective layers’:
- It is not required to set the current active layer, in essence that is only required for drawing manually in the GUI.
It tells QCAD on what layer new entities will be drawn with useCurrentAttributes = true.
In your code you specify the REntity its layer. - Your most probably are creating all new REntity’s … Then use op.addObject(e, false, true); // NOTuseCurrentAttributes, forceNew
- After the for loop you don’t have to set layer ‘0’ as current, it was already the current layer since the beginEvent.
(Setting a current layer is an additional operation with one undo step> Undo: Change document setting)
Am I correct that you don’t verify that we are working in the Model_Space block?
There are many more of such unexpected (limit) situations.
First, always (correctly) validate what is returned … A new RShape() is a shape but it is nothing …
… A new RArc() is an arc with no center, no radius and no end angles defined … An RVector is not per definition valid …
In doubt verify the used function in JS and further in cpp, that certainly explain the difference between expected and actual.
e.g. RLine.getVectorTo(p): qcad/src/core/math/RLine.cpp at master · qcad/qcad · GitHub
In RLine.h we can see that limited and strictRange have defaults when omitted.
Sometimes the Class Reference is not up to date … Here it is: QCAD: RLine Class Reference
If considered as solved the please edit the title of your initial post and add [SOLVED] to it.
Kind regards,
CVH