Question on XP Dev - RDATA and DLScan (Do I use both for sub-entity selection?)
I have been browsing through the XP tutorials located at https://rpgmaps.profantasy.com/xp-tutorials/
I have a question that involves a case where I want to have the user select a path, explode that path, and then perform operations on each of the members of that path. At this point I'm just trying to get a feel for the general requirements of the commands I will have to call.
I see parts within the tutorials that would assist me in having the user select the path entity; my question is whether I can easily access each of the members of the Path and hold them in an array, and then explode the Path and still be able to perform operations on the array of entities?
If so I'm looking at something along the lines of...
// Request Path from User
// Assign requested Path sub-entities to array
// Explode Path
// Iterate array of sub-entities and perform operations on each
I have both a general and a more specific question...
Does this flow make sense or does anyone foresee some problems I may need to be aware of?
More specifically, How do I get the members of the Path? Is there some access directly from the Path or from the Explode command, or will I have to figure it out second-hand in some way by walking the drawing list? (similar to the example of finding the symbols on a specific sheet?)
Comments
The important thing to remember about dealing with a path is that it is a primitive, it isn't a container for other entities like a sheet, symbol or multipoly is. As such, you cannot access the line segments in the path, because it doesn't contain any, it just contains nodes. So there isn't a way to get a reference to these before exploding. You can figure out what the lines will be by looking at the nodes in the path, but there aren't lines to get references to. (Also, changes in the drawing list can cause things to shift around in memory, so holding a pointer to an entity in the drawing list that was acquired prior to a modification may very well not be valid anymore.)
I haven't tried exploding from code myself, but I guess that would be the EExplode function. That function does have a parameter of type EXPPROC which looks like a callback function, maybe that could be of some use in determining the new lines, but I haven't really figured out how to use it properly.
Thanks, I'll do my best to play with it a bit later tonight and report back.
I figure worst case is that I'll have to gather up a list of entities, (maybe by ID if I can't trust the pointer?) explode the heck out of the path, and then look at the list of entities again to gather references to any that are not within my original list, (and of the correct type)
Either that or maybe I will simulate the exploding by using the nodes, not quite sure what I'll decide works best.
Quick update; I couldn't figure out much about EXPPROC but otherwise I've been able to use EEXPLODE fine just setting that second argument as NULL. Figuring I'll have to do a lot of entity filtering
I'm too stubborn for my own good. I have the callback activating but I have no idea what sort of pENTREC is being passed to me.
(not quite sure how to format below as code) I have a breakpoint to investigate the pENTREC but not quite sure what I should be looking for to identify what I can do with it.
DWORD XPCALL ExplodeCallback(pENTREC pEntRec)
{
return 0;
}
Look for EType in CStuff in the entity record first. That tells you what kind of entity this is. You'll find the ID's in _ESTRUC.H.
My issue from the very brief look I gave that one was that I didn't seem to actually get valid entities at all, just uninitialized memory. Which may be due to a mistake on my side, or perhaps something that needs to be done that I don't know about, there is no documentation about that call.
I'm kind of wondering if it was just an object for passing the CStuff about the User's line width, color, etc, I did notice the EType seemed to be 0 as far as I could tell, which indicates no point data going by _ESTRUC.H
The documentation for EExplode states:
The C wrapper for that routine (most likely the one that you're calling) is defined as:
which would put pEntRec as ESI and pExpProc as EDI. FastCAD is still mostly raw ASM code internally and doesn't often follow C compiler conventions. There are C wrappers for most of the code elements that your program can call, but not all of the callbacks from within those code elements back to your code have been correctly wrapped. This is one of those cases where the callback is doing something that's not as specified in the EXPPROC definition. The pENTREC parameter (a pointer to a discriminated union whose type is identified by the EType field in the union) is passed in ESI, not on the stack as the EXPPROC parameter would indicate. You'll need to unpack the pENTREC parameter yourself using something like
Any time you see what appears to be a garbled pointer in what you would expect to be a valid C parameter, there's a good chance that a meaningful pointer is in ESI, EDI, or EBX (and/or that there are parameters passed on the 8087 stack).
I'll put this on the bug list, but I will tell you that's it's probably not going to be a particularly high priority fix and fixing it will take a while because and be a bit risky because there are quite a few callbacks scattered around for this particular operation.
As a follow-up, please let me know if you get that to work or just abandon the problem altogether.
I'm curious if my diagnosis of the problem is correctly matching the observed results for the problem you're trying to solve (there is an EO_EXPLODE message handler for each entity type and I didn't check all of the types). You can see from the presence of the parameter on the EXPPROC C definition that it was intended to be updated, but the asm code never was updated, which could have been for many reasons, including that no calls for EExplode ever provide a C callback to EExplode.
In case it's helpful, the default handler (pExpProc = 0) looks like:
Tried a test implementation here, but getting into some issues. The callback is only called once, even though I should get 3 entities back from the entity I explode. (Using the default handler correctly leaves 3 entities in place). The entity I do get back seems correct.
EExplode then returns, and the program crashes with an access violation. Do I need to do anything else before returning from the callback?
I didn't get to work on it yesterday but I can confirm from prior experience that mine also tends to crash with an access violation upon returning the '0' from the callback (at least I assume that's the point of failure).
I would have guessed that the entity you get back would be a grouping of entities so I'm surprised it's only 1.
It occurs to me that the callback definition shouldn't have a parameter because it's a _stdcall routine and manages its own parameters via registers. What it's doing right now is discarding the 4 parameter bytes on return, which are actually the return address; it then returns to some unrelated address, which is a bad thing. The callback definition should take void and return int:
You'll need to cast the callback as EXPPROC at the point of use.
Ah, thanks, that does it.
An interesting observation is that if you have a callback method, the original entity isn't erased automatically, you need to handle that yourself, while if you don't have a callback, it is handled automatically.
Making great progress based off discussion so far.
Another thing I've run into is that I'm trying to request a (float or double) number from the user (they will have to type it in); is RD_Real4 the best RDATA.dtype to accomplish this? the comment just mentions, "real value"
I'm noticing that the number coming across is not what I'm typing in... the DWORD is coming through as something massive, 1075838976 for instance when I type in 2.5
C++ is not a language I'm very great with so apologies if it's something simple. It looks like maybe it's a bit-shift or type-cast issue but I couldn't quite match it up with anything that made sense to me.
The value in the rdata structure is defined as a dword*, but it really ought to be void*. If you want a float, cast the pointer from float* to dword* as the rdata parameter, then use the float* on return so that you're using the float.
There are lots of different kinds of float values you can request (real, distance, angle, and so on). Pick the one that best matches your problem.
I believe I have completed my first function. You maybe could see where I was going with this but I wanted to create a TAPERPATH command. So I programmed a command which will require:
The command will then explode the path and taper each line gradually.
Something I have observed is that you may find it best to change the fill style to solid if the path is currently set to hollow (before running the command) otherwise you have to try and select all the little pieces if you want to change it after. I do set an undo point so if you forget you can just undo the command.
The XP is attached within a zip file. If you want to use it you should unzip it to your install directory (default C:\Program Files (x86)\ProFantasy\CC3Plus), and to activate it type in TAPERPATH
I will follow this comment with some code for review, maybe I could learn something. Feel free to give me any feedback for how the command could improve.
Code for Review:
Most of the lifting is done with these two functions, but I have the full file attached as well to go more in-depth.