KeithSloan / OpenSCAD_Alt_Import

Inprocess : Installable version of FreeCAD OpenSCAD workbench
Other
10 stars 6 forks source link

Is there anything that can be done about this DXF file so that inner sections are hollow. #22

Closed KeithSloan closed 1 year ago

KeithSloan commented 1 year ago

No Parking Symbol.dxf.txt

Which when imported looks like

Image 04-08-2023 at 06 16

And when imported as an Object, Due to the need to make a Compund looks like Image 04-08-2023 at 06 15

mconsidine commented 1 year ago

Just so I'm clear: You would expect the display in the second case to appear like that in the first case? If so, it appears the issue is under the "View" tab in each. In the first case the default for "Display Mode" is "Flat Lines" versus in the second case it is "Shaded."

If that's the issue, can we force "Flat Lines" on import??

Or, do you want to somehow how detect and show holes? When I was first tackling this, I wasn't sure how to approach that. Which is why I started thinking about having an option to return faces, rather than a shape. The latter then can be exploded and one can manually start to clean things up. But if there are thoughts on how to approach that, please advise!

KeithSloan commented 1 year ago

I assume who ever created the file would expect the overall shape to have five holes.

If the above were true then I would expect the compound would look the same.

Don't know enough about DXF to know if there is enough info for the holes.

Maybe I/we should opt for ViewMode : WireFrame.

However if one could create with holes, then better for people importing DXF and then extruding

mconsidine commented 1 year ago

Ok, I think I might have a solution, but I can't get to try it until tomorrow. A clothes dryer needs repair ...

Sent from allegedly smart phone

On Fri, Aug 4, 2023, 1:00 PM Keith Sloan @.***> wrote:

I assume who ever created the file would expect the overall shape to have five holes.

If the above were true then I would expect the compound would look the same.

Don't know enough about DXF to know if there is enough info for the holes.

Maybe I/we should opt for ViewMode : WireFrame.

However if one could create with holes, then better for people importing DXF and then extruding

— Reply to this email directly, view it on GitHub https://github.com/KeithSloan/OpenSCAD_Alt_Import/issues/22#issuecomment-1665924396, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEPIPGGB4EQ2VIFKQO7EUDXTUTDLANCNFSM6AAAAAA3DW5TVY . You are receiving this because you commented.Message ID: @.***>

mconsidine commented 1 year ago

Okay, this sorta works. Which surprises me, frankly ...

`def makeholedshape(shapes) : #takes a list of faces

create a test based on bounding boxes whether or not something "contains" something else

def AholdsB(A, B) : if ((A.BoundBox.XMin < B.BoundBox.XMin) and \ (A.BoundBox.YMin < B.BoundBox.YMin) and \ (A.BoundBox.XMax > B.BoundBox.XMax) and \ (A.BoundBox.YMax > B.BoundBox.YMax)) : return 1 else : return 0

import numpy as np ra = [] #an array to hold flags for what faces "hold" other faces num_shapes = len(shapes)

newlist=[] #a list to hold new versions of faces that have other faces cut from them. Later we add things back for i in range(0,num_shapes):

obj=Part.Shape(Part.Face(Part.Wire(shapes[i].toShape()))) #if not already faces, eg circles/edges

obj=Part.Shape(((shapes[i])))    
for j in range(0, num_shapes):
  holdflag = AholdsB(shapes[i],shapes[j]) #0,1=false, true; self=0
  ra.append(holdflag)
  if holdflag == 1:
    #cut holes
    #y = Part.Shape(Part.Face(Part.Wire(shapes[j].toShape()))) #ditto
    y = Part.Shape(((shapes[j])))        
    obj=obj.cut(y) #cut "y" from the "base" face we are working off of
newlist.append(obj)

ra=np.reshape(ra,(num_shapes,num_shapes))

print(ra)

ra2 = np.apply_along_axis(sum, 0, ra) #sum up number of times something is "held"

print(ra2)

find indices of faces that should be holes

https://www.geeksforgeeks.org/python-get-indices-of-even-elements-from-list/

res = [idx for idx, ele in enumerate(ra2) if ((ele % 2 == 0) and (ele > 0))]

holes = [idx for idx, ele in enumerate(ra2) if ((ele % 2 != 0) and (ele > 0))] #even number=not a hole

print(holes)

obj = Part.makeCompound([shp for idx, shp in enumerate(newlist) if idx not in holes]) #now collect all fixed "not holes" return obj `

This can be tested by pulling in the No Parking Symbol as a shape, then sending it to the Python Console, where it ends up living as "obj".

At that point create "theshapes":

theshapes = list(obj.getSubObject(obj.getSubObjects()))

and then

Part.show(makeholedshape(theshapes))

This doesn't quite work though, as part of the "P" gets messed up. But I wanted to put this out there while I had time. There's a flaw in the logic somewhere and I haven't had time to probe it yet ...

mconsidine

mconsidine commented 1 year ago

So the flaw is that the bounding box of the upper curved part of the main "hole" contains the bounding box of the hole in the curved of the "P". So by the logic above that looks like an island in a hole, which then should be left alone.

What is needed is for a more sophisticated test which identifies if one part is held within the close shape of another part. Ie rather than "holds" have a "contains within" test.

Thoughts? Any magical OCC tools or a place to look for such?

mconsidine commented 1 year ago

Just to clarify. I pulled in the No Parking Sign dxf using the OpenSCAD WB, selecting the second option during File -> Open, namely import DXF. Then I used the Part WB to Explode Compound to create a group object. That is what I sent to the Python console to get at the individual faces. After running the above routine I get the following (which I should have included before): image Matt

KeithSloan commented 1 year ago

I plan to attend the FreeCAD meeting in Vancouver, I will try and discuss with Yorik to see if he has any ideas @yorikvanhavre

mconsidine commented 1 year ago

I posted this question on the forum and @edwilliams pointed out the .common() function.  So I'll test that today.  If the parking sign works then this can be dropped into the existing code easily and triggered by a flag argument, which could be set to true.  So nothing should get really disrupted.Also, if it works on the parking sign I'll go through some of the existing test cases and see if the method holds up.I'll be surprised if it does :)On Aug 6, 2023 2:04 AM, Keith Sloan @.***> wrote: I plan to attend the FreeCAD meeting in Vancouver, I will try and discuss with Yorik to see if he has any ideas @yorikvanhavre

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: @.***>

mconsidine commented 1 year ago

An initial test of the .common() function (I had missed that; was looking for an "intersection" function...) shows that this might work. I'll test more extensively later ... image

mconsidine commented 1 year ago

I've made a commit to the DxfObjects branch seems to work with relatively straightforward cases. It can get goofed up though with the rendering of arrows from dimensions or from all layers being returned. And in a DXF that shows areas for chips or capacitors for example, it will try to treat those as holes.

So in short, it seems easy to trip up this approach. That said, on a handful of vector-art images from the 'net it seemed to hold up.

One, umm, "feature" of the routine is that there are two nested loops that test in both directions (e.g. A contains B, B contains A). That's needed because I found a case where the largest face was not first in the list. Obviously as the number of faces goes up this goes way up and slows things down. I don't yet know if that can be simplified. Or if there is a more robust way of dealing with this with things like circuit boards.

As the commit stands, there is a flag that defaults to take into account holes. Dunno if that should be something user-accessible in the event that the result is poor and they just want to work with all faces that are returned.

HTH, mconsidine

KeithSloan commented 1 year ago

When you say you have made a commit to DxfObjects do you mean in your repro?

mconsidine commented 1 year ago

Yes. I think(?) a pull request was made. I'll check ...

mconsidine commented 1 year ago

Ah, okay, looks like I didn't complete the final step. Apologies ...

KeithSloan commented 1 year ago

No need to Apologise - Keep up the good work.

KeithSloan commented 1 year ago

I don't think the merged worked correctly.

When I try and import No_Parking_Symbol as DXF_Object I get an error 07:17:32 ('Mod/Pyramids_and_polyhedrons', 'Mod/Pyramids-and-Polyhedrons', 'Mod/FreeCAD-Pyramids-and-Polyhedrons', 'Mod/Pyramids-and-polyhedrons') 07:17:39 (qt.qpa.fonts) Populating font family aliases took 841 ms. Replace uses of missing font family "Sans-serif" with one that exists to avoid this cost. 07:17:41 The .FCStd# backup format is deprecated as of v0.21 and may be removed in future versions. To update, check the 'Preferences->General->Document->Use date and FCBak extension' option. 07:18:11 Creating Object from : /Users/keithsloan/MEGA/MEGAsync/CAD_Files/DXF/No Parking Symbol/No Parking Symbol.dxf 07:18:11 Create Object No Parking Symbol path /Users/keithsloan/MEGA/MEGAsync/CAD_Files/DXF/No Parking Symbol/No Parking Symbol.dxf 07:18:11 No_Parking_Symbol State : ['Touched'] prop : Proxy 07:18:11 create Geometry 07:18:11 Do not process DXF source on Document recompute 07:18:11 Doc temp dir /var/folders/cj/z259fmwd41dgzl8mq8nppwd40000gn/T 07:18:11 Path /var/folders/cj/z259fmwd41dgzl8mq8nppwd40000gn/T/No_Parking_Symbol.dxf 07:18:11 Copy File /Users/keithsloan/MEGA/MEGAsync/CAD_Files/DXF/No Parking Symbol/No Parking Symbol.dxf /var/folders/cj/z259fmwd41dgzl8mq8nppwd40000gn/T/No_Parking_Symbol.dxf 07:18:11 No_Parking_Symbol State : ['Touched'] prop : sourceFile 07:18:11 View Provider OnChanged : prop DisplayMode 07:18:11 View Provider OnChanged : prop DisplayMode 07:18:11 View Provider OnChanged : prop DisplayMode 07:18:11 View Provider OnChanged : prop Proxy 07:18:11 Execute No_Parking_Symbol 07:18:11 Reading file /var/folders/cj/z259fmwd41dgzl8mq8nppwd40000gn/T/No_Parking_Symbol.dxf 07:18:11 Doc <ezdxf.document.Drawing object at 0x15660a7d0> 07:18:11 ALL LAYER NAMES : ['SVG17624'] 07:18:11 Layer name provided not found 07:18:11 No common layer name found; Selecting all 07:18:11 NUMBER OF LAYERS : 1 07:18:11 Initial Shape <Shape object at 0x6000010f08c0> 07:18:11 Returned Shape (<Compound object at 0x6000010f09b0>, [<Face object at 0x600001013890>, <Face object at 0x600001062760>, <Face object at 0x600001062620>, <Face object at 0x6000010626c0>, <Face object at 0x600001062710>, <Face object at 0x6000010620d0>]) 07:18:11 Traceback (most recent call last): File "", line 1, in File "/Users/keithsloan/Library/Application Support/FreeCAD/Mod/Alternate_OpenSCAD/./importDXFObj.py", line 61, in open obj.Proxy.executeFunction(obj) File "/Users/keithsloan/Library/Application Support/FreeCAD/Mod/Alternate_OpenSCAD/./DXFObjects.py", line 97, in executeFunction obj.Shape = shp <class 'TypeError'>: type must be 'Shape', not tuple

On Straight import

07:21:34 Processing : /Users/keithsloan/MEGA/MEGAsync/CAD_Files/DXF/No Parking Symbol/No Parking Symbol.dxf 07:21:34 Reading file /Users/keithsloan/MEGA/MEGAsync/CAD_Files/DXF/No Parking Symbol/No Parking Symbol.dxf 07:21:34 Doc <ezdxf.document.Drawing object at 0x15654fcd0> 07:21:34 ALL LAYER NAMES : ['SVG17624'] 07:21:34 Layer name provided not found 07:21:34 No common layer name found; Selecting all 07:21:34 NUMBER OF LAYERS : 1 07:21:34 /Users/keithsloan/Library/Application Support/FreeCAD/Mod/Alternate_OpenSCAD/./importDXF.py(24)<class 'TypeError'>: expect shape in sequence

KeithSloan commented 1 year ago

As a workaround for importDXFObj

I changed line 99 to

      if isinstance(shp, tuple):
                obj.Shape = shp[0]
            else:
                obj.Shape = shp

Struggling to rectify New Importer ( ImportDXF )

mconsidine commented 1 year ago

Instead of that workaround, I think the issue is with lines 801-804 of OpenSCADdxf.py. I'm not sure if I let an old revision slip in, but given how this is called, it should only return compound. I think it's defaulting to return compound and faces, which should only happen if the flags are both true. So there's a short logic block missing.

Easiest fix should be - given the current way it's called - to return only compound at that point.

I'll test later ...

mconsidine commented 1 year ago

Take a look at OpenSCADdxf.py in DxfObjects branch and you should be able to see the difference. Otherwise, I can add back the short logic block. Happy to go either way ...

KeithSloan commented 1 year ago

I am not seeing lines 801-804 in OpenSCADdxf.py in DxfObject in main repro. Line numbers only go up to 451 :-(

If you could do the update and make a new Pull Request that would be great

mconsidine commented 1 year ago

Am I looking in the wrong place? https://github.com/KeithSloan/OpenSCAD_Alt_Import/blob/master/OpenSCADdxf.py

mconsidine commented 1 year ago

Yes, Matt, I am :) I'll review DxfObjects repro ...

mconsidine commented 1 year ago

Looks like your workaround is in DXFobjects.py in master.

What branch should I work on?

KeithSloan commented 1 year ago

I think you should clone/pull a copy of my master.

Create a new branch say FixDxf Make changes Push that to your repro and make a pull request.

That is my understanding of how things need to be done

yorikvanhavre commented 1 year ago

I plan to attend the FreeCAD meeting in Vancouver, I will try and discuss with Yorik to see if he has any ideas @yorikvanhavre

I won't be in Vancouver unfortunately... But I will be online as much as possible during that time, so we can brainstorm.

KeithSloan commented 1 year ago

Now fixed and merged