mozman / ezdxf

Python interface to DXF
https://ezdxf.mozman.at
MIT License
926 stars 190 forks source link

Problems with dimensions rendering. #73

Closed ruben-ksdm closed 5 years ago

ruben-ksdm commented 5 years ago

Hello... I am trying to write a script, which will add dimensions to the DXF file based on edge coordinates... But when I try to do that with ezDXF (using UCSs and linear dimensions) I get these dimensions in the wrong place... And the problem is that they are not just replaced, they are in the wrong places compared to each other... Like you see in this screenshot, these dimensions are not ending in the same point and are creating a cross. dimensionsfail When I add lines with the same coordinates as dimensions, this is what I get... dimensionsfailwithlines So I was wondering if I do something wrong with UCSs but then I saw an example with UCSs in dimension_linear.py, so I tried to change UCS uz and ux values in that example and got this... dimensionsexamplefail And then I saw a comment in the dimstyleoverride.py For a friendly CAD applications like BricsCAD you can discard the dimension line rendering, because it is done by BricsCAD, if no dimension rendering BLOCK is available and it is likely to get better results by ezdxf. So is there any workaround for this problem? Any way to add dimensions in the right place?

mozman commented 5 years ago

I need also your script not only the result to check this issue.

ruben-ksdm commented 5 years ago

Try adding any non-vertical and non-horizontal dimensions with this script.

from ezdxf.drawing import Drawing
from ezdxf.math import UCS, Vector
from ezdxf.modern.dimension import Dimension
from math import pi
import sys

def length(coords: list) -> int:
    """ Calculating length of the vector """
    return sum((coords[0][i] - coords[1][i])**2 for i in range(3))**0.5

def get_ucs(coords: list) -> UCS:
    """ Getting UCS from coordinates """
    d = Vector(coords[1][i] - coords[0][i] for i in range(3))
    if d[2]:
        if d[0] or d[1]:
            dz = Vector(-d[0], -d[1], -(-d[0]**2 - d[1]**2) / d[2])
        else:
            dz = Vector(0, -1, 0)
    else:
        dz = Vector(0, 0, 1)
    return UCS(
        origin=Vector(
            coords[0][0], coords[0][1],
            coords[0][2] - (coords[0][2] / 2 if not d[2] else -coords[0][1])),
        ux=d,
        uz=dz)

def add_dimension(msp, coords_wcs: list, ucs: UCS, distance=0) -> Dimension:
    """ Will add dimension - for now only aligned dimensions """
    dim = msp.add_linear_dim(
        base=[0, 0, 0], p1=[0, 0], p2=[length(coords_wcs), 0])
    dim.render(ucs=ucs)  # Adding and rendering dimension
    return dim

def add_line(msp, coords_wcs: list, ucs: UCS, distance=0):
    """ Will add dimension - for now only aligned dimensions """
    line = msp.add_line(*coords_wcs)
    return line

def add_dimensions(dwg: Drawing, dimension_coordinates: list) -> None:
    """ Function to add dimensions to the DWG
    """
    msp = dwg.modelspace()
    for coords in dimension_coordinates:
        ucs = get_ucs(coords)
        add_dimension(msp, coords, ucs)
        # add_line(msp, coords, ucs)

if __name__ == '__main__':
    print(sys.argv)
    if len(sys.argv > 1):
        """ Get coordinates from the sys.argv
        just give them in right order
        d1e1x d1e1y d1e1z d1e2x d1e2y d1e2z ...
        d1 - dimension 1
        e1 - edge 1
        Like this 0 0 0 1 1 1 """
    else:
        """ Some testing """
        dwg = ezdxf.new('R2010', setup=True)
        add_dimensions(dwg, [((x, y, z), (x + 3, y, z)) for x in range(1, 21, 3)
                            for y in range(1, 21, 3) for z in range(1, 21, 3)] +
                    [((x, y, z), (x, y + 3, z)) for y in range(1, 21, 3)
                        for x in range(1, 21, 3)
                        for z in range(1, 21, 3)] + [((x, y, z), (x, y, z + 3))
                                                    for y in range(1, 21, 3)
                                                    for x in range(1, 21, 3)
                                                    for z in range(1, 21, 3)])
        dwg.saveas('DimensionsTest.dxf')
mozman commented 5 years ago

After fixing len(argv > 1) I got this: image

doesn't look that bad

KoStard commented 5 years ago

Yes, you are right, in 2D everything is working great, but if you look at it in 3D you would see some strange behavior if you add non-vertical/horizontal dimensions or if you remove the small fix, that I have already added to the script (look in function get_ucs, coords[0][2] - (coords[0][2] / 2 if not d[2] else -coords[0][1])), this is fixing the behavior for vertical and horizontal dimensions, but not for non-vertical/horizontal dimensions (those with Vector(x!=0 or y!=0, z!=0))). And yes, it's me again :)

mozman commented 5 years ago

I don't know what you expect, bring a simple understandable example. And be sure you are using cartesian coordinate system. image

ruben-ksdm commented 5 years ago

Ok, here are some examples. Here is what I get when I use this code (my fix here is turned down, so even vertical dimensions are being placed in wrong places)...

This is a script created for final processing of a DXF file after Aregnaz's DXFExporter.
Author: Ruben Kostandyan
Based on: ezDXF
Python version: 3.7.2
Code formatted with: yapf
"""

import ezdxf
from ezdxf.drawing import Drawing
from ezdxf.math import UCS, Vector
from ezdxf.modern.dimension import Dimension
from math import pi
import sys

def length(coords: list) -> int:
    """ Calculating length of the vector """
    return sum((coords[0][i] - coords[1][i])**2 for i in range(3))**0.5

def get_ucs(coords: list) -> UCS:
    """ Getting UCS from coordinates """
    d = Vector(coords[1][i] - coords[0][i] for i in range(3))
    if d[2]:
        if d[0] or d[1]:
            dz = Vector(-d[0], -d[1], -(-d[0]**2 - d[1]**2) / d[2])
        else:
            dz = Vector(0, -1, 0)
    else:
        dz = Vector(0, 0, 1)
    return UCS(
        origin=Vector(
            coords[0][0], coords[0][1],
            coords[0][2]),# - (coords[0][2] / 2 if not d[2] else -coords[0][1])),
        ux=d,
        uz=dz)

def add_dimension(msp, coords_wcs: list, ucs: UCS, distance=0) -> Dimension:
    """ Will add dimension - for now only aligned dimensions """
    dim = msp.add_linear_dim(
        base=[0, 0, 0], p1=[0, 0], p2=[length(coords_wcs), 0])
    dim.render(ucs=ucs)  # Adding and rendering dimension
    return dim

def add_line(msp, coords_wcs: list, ucs: UCS, distance=0):
    """ Will add dimension - for now only aligned dimensions """
    line = msp.add_line(*coords_wcs)
    return line

def add_dimensions(dwg: Drawing, dimension_coordinates: list) -> None:
    """ Function to add dimensions to the DWG
    """
    msp = dwg.modelspace()
    for coords in dimension_coordinates:
        ucs = get_ucs(coords)
        add_dimension(msp, coords, ucs)
        # add_line(msp, coords, ucs)

if __name__ == '__main__':
    print(sys.argv)
    if len(sys.argv) > 1:
        """ Get coordinates from the sys.argv
        just give them in right order
        d1e1x d1e1y d1e1z d1e2x d1e2y d1e2z ...
        d1 - dimension 1
        e1 - edge 1
        Like this 0 0 0 1 1 1 """
    else:
        """ Some testing """
        dwg = ezdxf.new('R2010', setup=True)
        add_dimensions(dwg, [((x, y, z), (x + 3, y, z+1.5))
                             for x in range(1, 21, 3) for y in range(1, 21, 3)
                             for z in range(1, 21, 3)] +
                       [((x, y, z), (x, y + 3, z+1.5)) for y in range(1, 21, 3)
                        for x in range(1, 21, 3)
                        for z in range(1, 21, 3)] + [((x, y, z), (x, y, z + 3))
                                                     for y in range(1, 21, 3)
                                                     for x in range(1, 21, 3)
                                                     for z in range(1, 21, 3)])
        dwg.saveas('DimensionsTest2.dxf')

dimensionsslantwithoutfixfail And this is what I get with my fix

This is a script created for final processing of a DXF file after Aregnaz's DXFExporter.
Author: Ruben Kostandyan
Based on: ezDXF
Python version: 3.7.2
Code formatted with: yapf
"""

import ezdxf
from ezdxf.drawing import Drawing
from ezdxf.math import UCS, Vector
from ezdxf.modern.dimension import Dimension
from math import pi
import sys

def length(coords: list) -> int:
    """ Calculating length of the vector """
    return sum((coords[0][i] - coords[1][i])**2 for i in range(3))**0.5

def get_ucs(coords: list) -> UCS:
    """ Getting UCS from coordinates """
    d = Vector(coords[1][i] - coords[0][i] for i in range(3))
    if d[2]:
        if d[0] or d[1]:
            dz = Vector(-d[0], -d[1], -(-d[0]**2 - d[1]**2) / d[2])
        else:
            dz = Vector(0, -1, 0)
    else:
        dz = Vector(0, 0, 1)
    return UCS(
        origin=Vector(
            coords[0][0], coords[0][1],
            coords[0][2] - (coords[0][2] / 2 if not d[2] else -coords[0][1])),
        ux=d,
        uz=dz)

def add_dimension(msp, coords_wcs: list, ucs: UCS, distance=0) -> Dimension:
    """ Will add dimension - for now only aligned dimensions """
    dim = msp.add_linear_dim(
        base=[0, 0, 0], p1=[0, 0], p2=[length(coords_wcs), 0])
    dim.render(ucs=ucs)  # Adding and rendering dimension
    return dim

def add_line(msp, coords_wcs: list, ucs: UCS, distance=0):
    """ Will add dimension - for now only aligned dimensions """
    line = msp.add_line(*coords_wcs)
    return line

def add_dimensions(dwg: Drawing, dimension_coordinates: list) -> None:
    """ Function to add dimensions to the DWG
    """
    msp = dwg.modelspace()
    for coords in dimension_coordinates:
        ucs = get_ucs(coords)
        add_dimension(msp, coords, ucs)
        # add_line(msp, coords, ucs)

if __name__ == '__main__':
    print(sys.argv)
    if len(sys.argv) > 1:
        """ Get coordinates from the sys.argv
        just give them in right order
        d1e1x d1e1y d1e1z d1e2x d1e2y d1e2z ...
        d1 - dimension 1
        e1 - edge 1
        Like this 0 0 0 1 1 1 """
    else:
        """ Some testing """
        dwg = ezdxf.new('R2010', setup=True)
        add_dimensions(dwg, [((x, y, z), (x + 3, y, z+1.5))
                             for x in range(1, 21, 3) for y in range(1, 21, 3)
                             for z in range(1, 21, 3)] +
                       [((x, y, z), (x, y + 3, z+1.5)) for y in range(1, 21, 3)
                        for x in range(1, 21, 3)
                        for z in range(1, 21, 3)] + [((x, y, z), (x, y, z + 3))
                                                     for y in range(1, 21, 3)
                                                     for x in range(1, 21, 3)
                                                     for z in range(1, 21, 3)])
        dwg.saveas('DimensionsTest2.dxf')

dimensionsslantwithfixfail So as you can see oblique dimensions are not being properly rendered (or my code has other problems)...

mozman commented 5 years ago

Hi!

Again, I can not reproduce your results, maybe you use other input data by command line.

def add_dimensions(dwg: Drawing, dimension_coordinates: list) -> None:
    msp = dwg.modelspace()
    for coords in dimension_coordinates:
        ucs = get_ucs(coords)
        assert ucs.is_cartesian  # test if cartesian coordinate system
        ucs.render_axis(msp)  # show ucs as red, green and blue lines
        add_dimension(msp, coords, ucs)

Add this line to show the UCS, check if they are cartesian (x, y and z-axis have to be orthogonal to each other).

If you don't bring smaller examples, I will not further waste my time, just one or two dimensions which show the problem isolated.