Skip to content

Commit

Permalink
allow empty headers in DXF files
Browse files Browse the repository at this point in the history
  • Loading branch information
mikedh committed Nov 7, 2018
1 parent 25bc3b3 commit 4b480fc
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 74 deletions.
Binary file added models/fuze.zip
Binary file not shown.
8 changes: 4 additions & 4 deletions trimesh/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from . import transformations

from .io.export import export_mesh
from .constants import log, _log_time, tol
from .constants import log, log_time, tol

from .scene import Scene
from .parent import Geometry
Expand Down Expand Up @@ -1113,7 +1113,7 @@ def rezero(self):
"""
self.apply_translation(self.bounds[0] * -1.0)

@_log_time
@log_time
def split(self, only_watertight=True, adjacency=None, **kwargs):
"""
Returns a list of Trimesh objects, based on face connectivity.
Expand Down Expand Up @@ -1579,7 +1579,7 @@ def facets_on_hull(self):

return on_hull

@_log_time
@log_time
def fix_normals(self, multibody=None):
"""
Find and fix problems with self.face_normals and self.faces
Expand Down Expand Up @@ -1716,7 +1716,7 @@ def subdivide(self, face_index=None):
face_index=face_index)
return Trimesh(vertices=vertices, faces=faces)

@_log_time
@log_time
def smoothed(self, angle=.4):
"""
Return a version of the current mesh which will render nicely.
Expand Down
3 changes: 2 additions & 1 deletion trimesh/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,13 @@ def __init__(self, **kwargs):
log.addHandler(_NullHandler())


def _log_time(method):
def log_time(method):
"""
A decorator for methods which will time the method
and then emit a log.debug message with the method name
and how long it took to execute.
"""

def timed(*args, **kwargs):
tic = time_function()
result = method(*args, **kwargs)
Expand Down
4 changes: 2 additions & 2 deletions trimesh/io/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ..base import Trimesh
from ..points import PointCloud
from ..scene.scene import Scene, append_scenes
from ..constants import _log_time, log
from ..constants import log_time, log

from . import misc
from .ply import _ply_loaders
Expand Down Expand Up @@ -147,7 +147,7 @@ def load(file_obj, file_type=None, **kwargs):
return loaded


@_log_time
@log_time
def load_mesh(file_obj, file_type=None, **kwargs):
"""
Load a mesh file into a Trimesh object
Expand Down
77 changes: 44 additions & 33 deletions trimesh/path/io/dxf.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,45 +319,56 @@ def convert_bspline(e):

# get the section which contains the header in the DXF file
endsec = np.nonzero(blob[:, 1] == 'ENDSEC')[0]
header_start = np.nonzero(blob[:, 1] == 'HEADER')[0][0]
header_end = endsec[np.searchsorted(endsec, header_start)]
header_blob = blob[header_start:header_end]

# get the section which contains entities in the DXF file
entity_start = np.nonzero(blob[:, 1] == 'ENTITIES')[0][0]
entity_end = endsec[np.searchsorted(endsec, entity_start)]
entity_blob = blob[entity_start:entity_end]

# store some properties from the DXF header
metadata = {'DXF_HEADER': {}}

for key in ['$DIMSCALE', '$DIMUNIT', '$INSUNITS', '$LUNITS']:
value = get_key(header_blob,
key,
'70')
if value is not None:
metadata['DXF_HEADER'][key] = value

# store unit data pulled from the header of the DXF
# prefer LUNITS over INSUNITS
# I couldn't find a table for LUNITS values but they
# look like they are 0- indexed versions of
# the INSUNITS keys, so for now offset the key value
for offset, key in [(-1, '$LUNITS'),
(0, '$INSUNITS')]:
# get the key from the header blob
units = get_key(header_blob, key, '70')
# if it exists add the offset
if units is None:
continue
metadata[key] = units
units += offset
# if the key is in our list of units store it
if units in _DXF_UNITS:
metadata['units'] = _DXF_UNITS[units]
# warn on drawings with no units
if 'units' not in metadata:
log.warning('DXF doesn\'t have units specified!')
# store metadata
metadata = {}

# try reading the header, which may be malformed
header_start = np.nonzero(blob[:, 1] == 'HEADER')[0]
if len(header_start) > 0:
header_end = endsec[np.searchsorted(endsec, header_start[0])]
header_blob = blob[header_start[0]:header_end]

# store some properties from the DXF header
metadata['DXF_HEADER'] = {}
for key, group in [('$ACADVER', '1'),
('$DIMSCALE', '40'),
('$DIMALT', '70'),
('$DIMALTF', '40'),
('$DIMUNIT', '70'),
('$INSUNITS', '70'),
('$LUNITS', '70')]:
value = get_key(header_blob,
key,
group)
if value is not None:
metadata['DXF_HEADER'][key] = value

# store unit data pulled from the header of the DXF
# prefer LUNITS over INSUNITS
# I couldn't find a table for LUNITS values but they
# look like they are 0- indexed versions of
# the INSUNITS keys, so for now offset the key value
for offset, key in [(-1, '$LUNITS'),
(0, '$INSUNITS')]:
# get the key from the header blob
units = get_key(header_blob, key, '70')
# if it exists add the offset
if units is None:
continue
metadata[key] = units
units += offset
# if the key is in our list of units store it
if units in _DXF_UNITS:
metadata['units'] = _DXF_UNITS[units]
# warn on drawings with no units
if 'units' not in metadata:
log.warning('DXF doesn\'t have units specified!')

# find the start points of entities
group_check = entity_blob[:, 0] == '0'
Expand Down
4 changes: 2 additions & 2 deletions trimesh/proximity.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from . import util

from .grouping import group_min
from .constants import tol, _log_time
from .constants import tol, log_time
from .triangles import closest_point as closest_point_corresponding

from collections import deque
Expand Down Expand Up @@ -241,7 +241,7 @@ class ProximityQuery(object):
def __init__(self, mesh):
self._mesh = mesh

@_log_time
@log_time
def on_surface(self, points):
"""
Given list of points, for each point find the closest point
Expand Down
16 changes: 6 additions & 10 deletions trimesh/ray/ray_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .. import constants


@constants.log_time
def contains_points(intersector,
points,
check_direction=None):
Expand Down Expand Up @@ -105,18 +106,20 @@ def contains_points(intersector,
broken = np.logical_and(np.logical_not(agree),
np.logical_not(one_freespace))

# if all rays agree return
if not broken.any():
return contains

# try to run again with a new random vector
# only do it if check_direction isn't specified
# to avoid infinite recursion
if broken.any() and check_direction is None:
if check_direction is None:
# we're going to run the check again in a random direction
new_direction = util.unitize(np.random.random(3) - .5)
# do the mask trick again to be able to assign results
mask = inside_aabb.copy()
mask[mask] = broken

# run a contains check again on the broken points with a
# new random direction but only once and assign it to our results
contains[mask] = contains_points(
intersector,
points[inside_aabb][broken],
Expand All @@ -126,11 +129,4 @@ def contains_points(intersector,
'detected %d broken contains test, attempted to fix',
broken.sum())

# we had unrecoverable points
if broken.any() and check_direction is not None:
constants.log.error(
'ray contains tests had %d unrecoverable!' +
'try loading mesh with use_embree=False!',
broken.sum())

return contains
42 changes: 24 additions & 18 deletions trimesh/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1309,23 +1309,25 @@ def submesh(mesh,
if append:
visuals = np.array(visuals)
vertices, faces = append_faces(vertices, faces)
appended = trimesh_type(vertices=vertices,
faces=faces,
face_normals=np.vstack(normals),
visual=visuals[0].concatenate(visuals[1:]),
process=False)
appended = trimesh_type(
vertices=vertices,
faces=faces,
face_normals=np.vstack(normals),
visual=visuals[0].concatenate(visuals[1:]),
process=False)
return appended

# generate a list of Trimesh objects
result = [trimesh_type(vertices=v,
faces=f,
face_normals=n,
visual=c,
metadata=copy.deepcopy(mesh.metadata),
process=False) for v, f, n, c in zip(vertices,
faces,
normals,
visuals)]
result = [trimesh_type(
vertices=v,
faces=f,
face_normals=n,
visual=c,
metadata=copy.deepcopy(mesh.metadata),
process=False) for v, f, n, c in zip(vertices,
faces,
normals,
visuals)]
result = np.array(result)
if len(result) > 0 and only_watertight:
# fill_holes will attempt a repair and returns the
Expand All @@ -1342,12 +1344,15 @@ def zero_pad(data, count, right=True):
"""
Parameters
--------
data: (n) length 1D array
count: int
data : (n,)
1D array
count : int
Minimum length of result array
Returns
--------
padded: (count) length 1D array if (n < count), otherwise length (n)
padded : (m,)
1D array where m >= count
"""
if len(data) == 0:
return np.zeros(count)
Expand Down Expand Up @@ -1375,7 +1380,8 @@ def jsonify(obj, **kwargs):
Returns
--------------
dumped: str, JSON dump of obj
dumped : str
JSON dump of obj
"""
class NumpyEncoder(json.JSONEncoder):

Expand Down
2 changes: 1 addition & 1 deletion trimesh/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.35.21'
__version__ = '2.35.23'
6 changes: 3 additions & 3 deletions trimesh/voxel.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from . import caching
from . import grouping

from .constants import log, _log_time
from .constants import log, log_time


class Voxel(object):
Expand Down Expand Up @@ -287,7 +287,7 @@ def show(self, solid=False):
self.as_boxes(solid=solid).show()


@_log_time
@log_time
def voxelize_subdivide(mesh,
pitch,
max_iter=10,
Expand Down Expand Up @@ -436,7 +436,7 @@ def local_voxelize(mesh, point, pitch, radius, fill=True, **kwargs):
return voxels, local_origin


@_log_time
@log_time
def voxelize_ray(mesh,
pitch,
per_cell=[2, 2],
Expand Down

0 comments on commit 4b480fc

Please sign in to comment.