Source code for fibomat.from_file.dxf

from typing import Union, List
import pathlib
import math

import numpy as np

import ezdxf
import ezdxf.entities

from fibomat.shapes import Shape, Polyline, Polygon, Line, Spot, Circle, Ellipse, Rect
from fibomat.utils import PathLike


def _is_rect(vertices):
    if len(vertices) == 4:
        # check if the diagonals are equal
        if math.isclose(np.linalg.norm(vertices[0]-vertices[2]),
                        np.linalg.norm(vertices[1]-vertices[3])):
            return True
    return False


def _rect_from_vertices(vertices):

    # find the vertex with the minimum y value to get a base site of the rect.
    i_min = np.where(vertices[:, 1] == vertices[:, 1].min())[0]

    if len(i_min) == 1:
        # we found one, now check, if the left or right vertex will result in a
        # smaller rotation of the base site compared to x axis.
        base_1 = vertices[(i_min[0]+1) % 4] - vertices[i_min[0]]
        base_2 = vertices[i_min[0]] - vertices[(i_min[0]-1) % 4]

        if np.abs(np.arctan2(base_1[1], base_1[0])) < np.abs(np.arctan2(base_2[1], base_2[0])):
            base_start_vertex_i = i_min[0]
            base_end_vertex_i = (i_min[0]+1) % 4

            theta = np.arctan2(base_1[1], base_1[0])
        else:
            base_start_vertex_i = (i_min[0]-1) % 4
            base_end_vertex_i = i_min[0]

            theta = np.arctan2(base_2[1], base_2[0])

    elif len(i_min) == 2:
        # we got two, the base site is parallel to x axis
        base_start_vertex_i = i_min[0]
        base_end_vertex_i = i_min[1]

        if vertices[base_start_vertex_i, 0] > vertices[base_end_vertex_i, 0]:
            base_start_vertex_i, base_end_vertex_i = base_end_vertex_i, base_start_vertex_i

        base = vertices[base_end_vertex_i] - vertices[base_start_vertex_i]
        theta = np.arctan2(base[1], base[0])
    else:
        raise RuntimeError('Something went horribly wrong.')

    width = np.linalg.norm(vertices[base_start_vertex_i]-vertices[base_end_vertex_i])
    height = np.linalg.norm(vertices[base_end_vertex_i]-vertices[(base_end_vertex_i+1) % 4])

    center = np.mean(vertices, axis=0)

    return Rect(width=width, height=height, theta=theta, center=center)


# TODO: let vertices_raw be a generator object
def _polyline(ocs, vertices_raw, closed):
    vertices = np.empty(shape=(len(vertices_raw), 2))
    for i, vertex in enumerate(vertices_raw):
        vertices[i][0] = ocs.to_wcs(vertex)[0]
        vertices[i][1] = ocs.to_wcs(vertex)[1]

    if closed:
        if _is_rect(vertices):
            return _rect_from_vertices(vertices)
        return Polygon(vertices)

    # it could be possible that start and end are equal but it is not marked as
    # closed.
    if np.allclose(vertices[0], vertices[-1]):
        cropped_vertices = vertices[:-1]
        if _is_rect(cropped_vertices):
            return _rect_from_vertices(cropped_vertices)
        return Polygon(cropped_vertices)

    return Polyline(vertices)


def _ellipse(major, minor, center):
    major = np.array(major)
    minor = np.array(minor)

    angle_major = np.abs(np.arctan2(major[1], major[0]))

    if angle_major > np.pi/2:
        major *= -1.
        angle_major -= np.pi

    angle_minor = np.abs(np.arctan2(minor[1], minor[0]))
    if angle_minor > np.pi / 2:
        angle_minor *= -1.
        angle_major -= np.pi

    # if angle_major > np.pi/2:
    #     angle_major -= np.pi / 2
    #
    # if angle_minor > np.pi / 2:
    #     angle_minor -= np.pi / 2

    print(angle_major, angle_minor)

    if angle_major <= angle_minor:
        a = np.linalg.norm(major)
        b = np.linalg.norm(minor)

        theta = np.arctan2(major[1], major[0])

    else:
        a = np.linalg.norm(minor)
        b = np.linalg.norm(major)

        theta = np.arctan2(minor[1], minor[0])

    if theta > np.pi / 2:
        theta -= np.pi
    elif theta < -np.pi / 2:
        theta += np.pi

    return Ellipse(a, b, theta, center)


[docs]def shapes_from_dxf(file_path: PathLike) -> List[Shape]: """ Reads a dxf file an convert the contained entities (shapes) to fibomat shapes. .. note:: z components of all entities are ignored. Args: file_path (pathlib.Path): path to file Returns: List[Shape] """ doc = ezdxf.readfile(str(file_path)) shapes = [] # we try to get all objects from modelspace for obj in doc.modelspace(): ocs = obj.ocs() dxftype = obj.dxftype() if dxftype == 'POINT': spot: ezdxf.entities.Point = obj shapes.append(Spot(spot.dxf.location[:2])) elif dxftype == 'LINE': line: ezdxf.entities.Line = obj shapes.append(Line(line.dxf.start[:2], line.dxf.end[:2])) elif dxftype == 'POLYLINE': poly: ezdxf.entities.Polyline = obj # maybe these to clauses are equivalent if not poly.is_2d_polyline or not poly.get_mode() == 'AcDb2dPolyline': raise RuntimeError('Polyline is not 2d.') if poly.has_arc: raise RuntimeError('Arcs are not supported in polyline.') vertices = list(poly.points()) shapes.append(_polyline(ocs, vertices, poly.is_closed)) elif dxftype == 'LWPOLYLINE': poly: ezdxf.entities.LWPolyline = obj if poly.has_arc: raise RuntimeError('Arcs are not supported in polyline.') vertices = list(poly.vertices()) shapes.append(_polyline(ocs, vertices, poly.closed)) elif dxftype == 'CIRCLE': circle: ezdxf.entities.Circle = obj shapes.append( Circle( r=circle.dxf.radius, center=ocs.to_wcs(circle.dxf.center)[:2]) ) elif dxftype == 'ELLIPSE': ellipse: ezdxf.entities.Ellipse = obj if not math.isclose(ellipse.dxf.start_param, 0.) or not math.isclose(ellipse.dxf.end_param, 2.* np.pi): raise RuntimeError("Elliptical arcs are not supported.") shapes.append(_ellipse(ellipse.dxf.major_axis[:2], ellipse.minor_axis[:2], ellipse.dxf.center[:2])) else: raise RuntimeError( f'Encountered (currently) unsupported shape {obj.dxftype()}' ) return shapes