Skip to content

Commit 4e12d6a

Browse files
committed
remove DXF xrecord and nicer names in split_scene
1 parent 842652a commit 4e12d6a

8 files changed

Lines changed: 108 additions & 144 deletions

File tree

tests/test_dxf.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -68,32 +68,6 @@ def test_spline(self):
6868
assert len(d.entities[0].points) == len(r.entities[0].points)
6969
assert len(d.entities[0].knots) == len(r.entities[0].knots)
7070

71-
def test_xrecord(self):
72-
# data to store to test export / import round trip
73-
data = {'thangs': 'poppin',
74-
'pnts': g.np.arange(9).reshape((-1, 3))}
75-
76-
# get a drawing and add our data to metadata
77-
d = g.get_mesh('2D/wrench.dxf')
78-
d.metadata.update(data)
79-
80-
# get a path we can write
81-
temp_name = g.tempfile.NamedTemporaryFile(
82-
suffix='.dxf', delete=False).name
83-
84-
# export as a DXF file, which should put our
85-
# custom data into an XRecord
86-
d.export(temp_name, include_metadata=True)
87-
88-
# reload from export
89-
r = g.trimesh.load(temp_name)
90-
91-
# check numpy round trip
92-
assert g.np.allclose(r.metadata['pnts'],
93-
data['pnts'])
94-
# check string roundtrip
95-
assert r.metadata['thangs'] == 'poppin'
96-
9771
def test_versions(self):
9872
"""
9973
DXF files have a bajillion versions, so test against

tests/test_scene.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ def test_scene(self):
4040
len(scene_base.geometry))
4141

4242
for s in [scene_split, scene_base]:
43+
pre = s.md5()
4344
assert len(s.geometry) > 0
45+
assert s.is_valid
4446

4547
flattened = s.graph.to_flattened()
4648
g.json.dumps(flattened)
@@ -54,6 +56,7 @@ def test_scene(self):
5456
assert g.trimesh.util.is_shape(s.triangles, (-1, 3, 3))
5557
assert len(s.triangles) == len(s.triangles_node)
5658

59+
assert s.md5() == pre
5760
assert s.md5() is not None
5861

5962
assert len(s.duplicate_nodes) > 0

trimesh/path/io/dxf.py

Lines changed: 38 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,6 @@
6060
get_resource('dxf.json.template')).items()}
6161

6262

63-
def get_key(blob, field, code):
64-
try:
65-
line = blob[np.nonzero(blob[:, 1] == field)[0][0] + 1]
66-
except IndexError:
67-
return None
68-
if line[0] == code:
69-
try:
70-
return int(line[1])
71-
except ValueError:
72-
return line[1]
73-
else:
74-
return None
75-
76-
7763
def load_dxf(file_obj, **kwargs):
7864
"""
7965
Load a DXF file to a dictionary containing vertices and
@@ -88,56 +74,6 @@ def load_dxf(file_obj, **kwargs):
8874
result: dict, keys are entities, vertices and metadata
8975
"""
9076

91-
def get_metadata():
92-
"""
93-
Get metadata from DXF objects section of the file.
94-
95-
Returns
96-
----------
97-
metadata : dict
98-
Any available metadata stored in XRecord
99-
"""
100-
metadata = {}
101-
# save file version info
102-
metadata['ACADVER'] = get_key(header_blob, '$ACADVER', '1')
103-
104-
# get the section which contains objects in the DXF file
105-
obj_start = np.nonzero(blob[:, 1] == 'OBJECTS')[0]
106-
107-
if len(obj_start) == 0:
108-
return metadata
109-
obj_start = obj_start[0]
110-
111-
obj_end = endsec[np.searchsorted(endsec, obj_start)]
112-
obj_blob = blob[obj_start:obj_end]
113-
114-
# the index of xrecords are one past the group code key
115-
xrecords = np.nonzero((
116-
obj_blob == ['100', 'ACDBXRECORD']).all(axis=1))[0] + 1
117-
118-
# if there are no XRecords return
119-
if len(xrecords) == 0:
120-
return metadata
121-
122-
# resplit the file data without upper() to preserve case
123-
blob_lower = np.array(str.splitlines(raw)).reshape((-1, 2))
124-
# newlines and split should never be effected by upper()
125-
assert len(blob_lower) == len(blob)
126-
127-
# the likely exceptions are related to JSON decoding
128-
try:
129-
# loop through xrecords by group code
130-
for code, data in blob_lower[obj_start:obj_end][xrecords]:
131-
if code == str(XRECORD_METADATA):
132-
metadata.update(json.loads(data))
133-
# we could store xrecords in the else here
134-
# but they have a lot of garbage so don't
135-
# metadata['XRECORD_' + code] = data
136-
except BaseException:
137-
log.error('failed to load metadata!', exc_info=True)
138-
139-
return metadata
140-
14177
def info(e):
14278
"""
14379
Pull metadata based on group code, and return as a dict.
@@ -367,7 +303,7 @@ def convert_bspline(e):
367303
# no converter to ASCII DXF available
368304
raise ValueError('binary DXF not supported!')
369305
else:
370-
# convert to R14 ASCII DXF
306+
# convert binary DXF to R14 ASCII DXF
371307
raw = _teigha_convert(raw, extension='dxf')
372308
else:
373309
# we've been passed bytes that don't have the
@@ -392,26 +328,30 @@ def convert_bspline(e):
392328
entity_end = endsec[np.searchsorted(endsec, entity_start)]
393329
entity_blob = blob[entity_start:entity_end]
394330

395-
# try to load path metadata from xrecords stored in DXF
396-
try:
397-
metadata = get_metadata()
398-
except BaseException:
399-
log.error('failed to extract metadata!',
400-
exc_info=True)
401-
metadata = {}
331+
# store some properties from the DXF header
332+
metadata = {'DXF_HEADER': {}}
333+
334+
for key in ['$DIMSCALE', '$DIMUNIT', '$INSUNITS', '$LUNITS']:
335+
value = get_key(header_blob,
336+
key,
337+
'70')
338+
if value is not None:
339+
metadata['DXF_HEADER'][key] = value
402340

403341
# store unit data pulled from the header of the DXF
404342
# prefer LUNITS over INSUNITS
405343
# I couldn't find a table for LUNITS values but they
406344
# look like they are 0- indexed versions of
407345
# the INSUNITS keys, so for now offset the key value
408-
for offset, key in [(0, '$INSUNITS'),
409-
(-1, '$LUNITS')]:
346+
for offset, key in [(-1, '$LUNITS'),
347+
(0, '$INSUNITS')]:
410348
# get the key from the header blob
411349
units = get_key(header_blob, key, '70')
412350
# if it exists add the offset
413-
if units is not None:
414-
units += offset
351+
if units is None:
352+
continue
353+
metadata[key] = units
354+
units += offset
415355
# if the key is in our list of units store it
416356
if units in _DXF_UNITS:
417357
metadata['units'] = _DXF_UNITS[units]
@@ -498,7 +438,7 @@ def convert_bspline(e):
498438
return result
499439

500440

501-
def export_dxf(path, include_metadata=False):
441+
def export_dxf(path):
502442
"""
503443
Export a 2D path object to a DXF file
504444
@@ -667,28 +607,6 @@ def convert_generic(entity, vertices):
667607
"""
668608
return convert_line(entity, vertices)
669609

670-
def convert_metadata():
671-
"""
672-
Save path metadata as a DXF Xrecord object.
673-
"""
674-
if (not include_metadata) or len(path.metadata) == 0:
675-
return ''
676-
# dump metadata to compact JSON
677-
# make sure there are no newlines to break DXF
678-
# util.jsonify will be able to convert numpy arrays
679-
as_json = util.jsonify(
680-
path.metadata,
681-
separators=(',', ':')).replace('\n', ' ')
682-
683-
# create an XRECORD for our use
684-
xrecord = TEMPLATES['xrecord'].substitute({
685-
'INDEX': XRECORD_METADATA,
686-
'DATA': as_json})
687-
# add the XRECORD to an objects section
688-
result = TEMPLATES['objects'].substitute({
689-
'OBJECTS': xrecord})
690-
return result
691-
692610
# make sure we're not losing a ton of
693611
# precision in the string conversion
694612
np.set_printoptions(precision=12)
@@ -717,12 +635,10 @@ def convert_metadata():
717635
entities = TEMPLATES['entities'].substitute({
718636
'ENTITIES': entities_str})
719637
footer = TEMPLATES['footer'].substitute()
720-
# metadata encoded as objects section
721-
objects = convert_metadata()
638+
722639
# filter out empty sections
723640
sections = [i for i in [header,
724641
entities,
725-
objects,
726642
footer]
727643
if len(i) > 0]
728644

@@ -759,6 +675,24 @@ def load_dwg(file_obj, **kwargs):
759675
return result
760676

761677

678+
def get_key(blob, field, code):
679+
"""
680+
Given a loaded (n, 2) blob and a field name
681+
get a value by code.
682+
"""
683+
try:
684+
line = blob[np.nonzero(blob[:, 1] == field)[0][0] + 1]
685+
except IndexError:
686+
return None
687+
if line[0] == code:
688+
try:
689+
return int(line[1])
690+
except ValueError:
691+
return line[1]
692+
else:
693+
return None
694+
695+
762696
def _teigha_convert(data, extension='dwg'):
763697
"""
764698
Convert any DXF/DWG to R14 ASCII DXF using Teigha Converter.
@@ -836,11 +770,13 @@ def _teigha_convert(data, extension='dwg'):
836770

837771

838772
# the DWG to DXF converter
773+
# they renamed it at some point but it is the same
839774
for _name in ['ODAFileConverter',
840775
'TeighaFileConverter']:
841776
_teigha = find_executable(_name)
842777
if _teigha is not None:
843778
break
779+
844780
# suppress X11 output
845781
_xvfb_run = find_executable('xvfb-run')
846782

trimesh/path/simplify.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ def merge_colinear(points, scale):
165165
points = np.asanyarray(points, dtype=np.float64)
166166
scale = float(scale)
167167

168+
if len(points.shape) != 2 or points.shape[1] != 2:
169+
raise ValueError('only for 2D points!')
170+
168171
# if there's less than 3 points nothing to merge
169172
if len(points) < 3:
170173
return points.copy()
@@ -185,12 +188,15 @@ def merge_colinear(points, scale):
185188
# if we have points A B C D
186189
# and direction vectors A-B, B-C, etc
187190
# these will be perpendicular to the vectors A-C, B-D, etc
188-
perpendicular = (points[2:] - points[:-2]).T[::-1].T
189-
perpendicular /= np.linalg.norm(perpendicular, axis=1).reshape((-1, 1))
191+
perp = (points[2:] - points[:-2]).T[::-1].T
192+
perp_norm = np.linalg.norm(perp, axis=1)
193+
perp_nonzero = perp_norm > tol.merge
194+
perp[perp_nonzero] /= perp_norm[perp_nonzero].reshape((-1, 1))
190195

191196
# find the projection of each direction vector
192197
# onto the perpendicular vector
193-
projection = np.abs(diagonal_dot(perpendicular, direction[:-1]))
198+
projection = np.abs(diagonal_dot(perp,
199+
direction[:-1]))
194200

195201
projection_ratio = np.max((projection / direction_norm[1:],
196202
projection / direction_norm[:-1]), axis=0)

trimesh/resources/helpers/dxf_helper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,5 @@ def read_files(path):
4343
write_json(t)
4444

4545
# dump JSON to files for editing
46-
#$t = get_json()
47-
#$write_files(t)
46+
# $t = get_json()
47+
# $write_files(t)

0 commit comments

Comments
 (0)