vectorgraphics / asymptote

2D & 3D TeX-Aware Vector Graphics Language
https://asymptote.sourceforge.io/
GNU General Public License v3.0
552 stars 90 forks source link

'SuspendThread failed' crash with high res rasterization #114

Closed Rojetto closed 5 years ago

Rojetto commented 5 years ago

I have a 3D figure that I need to render using rasterization. Using -render 0 (vectorizing it) everything works fine, apart from 3D paths and surfaces not intersecting correctly. This is fixed by using -render 2, which gives the correct, albeit a little pixely result. When I try to raise the resolution using -render 4, Asymptote crashes and prints SuspendThread failed into the console.

There seems to be some kind of limit to the resolution of the rasterized bitmap, because this problem appeared with multiple combinations of render parameters, size() settings and even camera perspectives that change the picture size. I even had this happen in the interactive 3D viewer, which succesfully opened but crashed at some point with the same message while I was rotating the camera.

I'm running Asymptote 2.53 on Windows 10 and an Intel HD 520 integrated graphics chip. The command line arguments I use are

asy -noV -render 4 -noprc -f pdf test.asy

Here is the code for reproducing the problem. I could not figure out a minimal example that has the same problem, so you'll have to endure this small mess. test.asy:

import three;
import heli;

currentprojection=perspective(-6,1,2,showtarget=false,center=true);
size(20cm);

real pitch = 35;
real elevation = 60;
real travel = 70;

real[] a = {1, 2, 3, 4};
int[] b = {1, 2};

transform3 c = shift(1, 2, 3);

heli.draw_heli(pitch, elevation, travel);
heli.draw_all_coord_frames(pitch, elevation, travel);
heli.draw_angles(pitch, elevation, travel);

heli.asy:

import three;
import solids;

typedef real[][] matrix;

triple operator cast(matrix mat) {
    int rows = mat.length;
    int cols = mat[0].length;

    if (rows==1 && cols==3) {
        return (mat[0][0], mat[0][1], mat[0][2]);
    } else if (rows==3 && cols==1) {
        return (mat[0][0], mat[1][0], mat[2][0]);
    } else if (rows==4 && cols==4) {
        return (mat[0][3], mat[1][3], mat[2][3]);
    } else {
        abort("Matrix not castable to triple");
        return (0, 0, 0);
    }
}

matrix slice_mat(matrix mat, int row, int row_end=row+1, int col, int col_end=col+1) {
    int[] row_is = sequence(row, row_end-1);
    int[] col_is = sequence(col, col_end-1);
    return transpose(transpose(mat[row_is])[col_is]);
}

transform3 shiftx(real x) {
    return shift(x, 0, 0);
}

transform3 shifty(real y) {
    return shift(0, y, 0);
}

transform3 shiftz(real z) {
    return shift(0, 0, z);
}

// joint definition
real hjh = 0.02; // half joint height
real jr = 0.01; // joint radius
revolution joint = shift(0,0,-hjh)*cylinder(O, jr, 2*hjh, Z);
surface joint_surface = surface(surface(joint),surface(circle((0,0,hjh),jr)),surface(circle((0,0,-hjh),jr)));

// mass definition
real hmw = 0.02; // half mass width
path3[] mass = box((-hmw,-hmw,-hmw),(hmw,hmw,hmw));
surface mass_surface = shift(-hmw,-hmw,-hmw)*scale3(2*hmw)*unitcube;

// util functions
pen geom_pen = linewidth(1);

void draw_line(path3[] p) {
    draw(p,geom_pen);
}

void draw_joint(transform3 p) {
    draw(p*joint_surface,white,nolight);
    draw(p*scale3(1.05)*joint,geom_pen,nullpen,nolight);
}

void draw_mass(transform3 p) {
    draw(p*mass_surface,palegray,nolight);
    draw(p*scale3(1.02)*mass,geom_pen,nolight);
}

real ax_length = 0.1;
pen ax_style = dashed;

void draw_arrow(path3 p, pen c) {
    draw(p,p=c,arrow=Arrow3(DefaultHead3,emissive(c)));
}

void draw_axis_arrow(path3 p, pen c) {
    draw_arrow(p,c+ax_style);
}

void draw_coord_frame(transform3 transform_mat, real length=ax_length, pen c=blue) {
    triple orig = (triple) transform_mat;
    triple x = orig + ax_length * slice_mat(transform_mat, col=0, row=0, row_end=3);
    triple y = orig + ax_length * slice_mat(transform_mat, col=1, row=0, row_end=3);
    triple z = orig + ax_length * slice_mat(transform_mat, col=2, row=0, row_end=3);

    draw_axis_arrow(orig--x, c);
    draw_axis_arrow(orig--y, c);
    draw_axis_arrow(orig--z, c);
}

// geometry
real lb = 0.3;
real lh = 0.67;
real dh = 0.05;
real lp = 0.178;
real lc = 0.45;
real dc = 0.1;

transform3 frame_A(real pitch, real elevation, real travel) {
    return shift(0, 0, lb)*rotate(travel, Z)*rotate(elevation, Y);
}

transform3 frame_H(real pitch, real elevation, real travel) {
    return frame_A(pitch, elevation, travel) * shift(-lh, 0, 0)*rotate(pitch, X);
}

void draw_all_coord_frames(real pitch, real elevation, real travel) {
    transform3 frame_I = shift(0, 0, 0);
    draw_coord_frame(frame_I, blue);
    label("\{I\}", frame_I, NE, blue);

    transform3 frame_A = frame_A(pitch, elevation, travel);
    draw_coord_frame(frame_A, heavygreen);
    label("\{A\}", frame_A, SE, heavygreen);

    transform3 frame_H = frame_H(pitch, elevation, travel);
    draw_coord_frame(frame_H, orange);
    label("\{H\}", frame_H, SE, orange);
}

void draw_heli(real pitch, real elevation, real travel) {
    transform3 rot_phi = rotate(pitch, X);
    transform3 rot_eps = rotate(elevation, Y);
    transform3 rot_lamb = rotate(travel, Z);

    // actual drawing
    draw_line(O--(0,0,0.5*lb-hjh));
    draw_joint(shift(0,0,0.5*lb));
    draw_line((0,0,0.5*lb+hjh)--(0,0,lb-jr));

    transform3 B = shiftz(lb)*rot_lamb*rot_eps;
    transform3 C = B * shiftx(-lh)*rot_phi;

    draw_joint(shiftz(lb)*rot_lamb*rotate(90,X));
    draw_line(B*((jr,0,0)--(-lh+hjh,0,0)));
    draw_line(B*((-jr,0,0)--(lc,0,0)));
    draw_line(B*((lc,0,0)--(lc,0,-dc+hmw)));

    draw_mass(B*shift(lc,0,-dc));

    draw_joint(C*rotate(90,Y));
    draw_line(C*((0,0,jr)--(0,0,dh)));
    draw_line(C*((0,-lp+hmw,dh)--(0,lp-hmw,dh)));

    draw_mass(C*shift(0,lp,dh));
    draw_mass(C*shift(0,-lp,dh));
}

void draw_angles(real pitch, real elevation, real travel) {
    transform3 frame1 = shiftz(lb);
    transform3 frame2 = frame1 * rotate(travel, Z);
    transform3 frame3 = frame2 * rotate(elevation, Y);
    transform3 frame4 = frame3 * shiftx(-lh);
    transform3 frame5 = frame4 * rotate(pitch, X);

    draw(frame1*(O--(-lh, 0, 0)), gray);
    draw(frame2*(O--(-lh, 0, 0)), gray);
    draw(frame4*(O--(0, 0, 1.5*dh)), gray);

    draw_arrow(arc(frame1*O, frame1*(-0.7*lh, 0, 0), frame2*(-0.7*lh, 0, 0)), gray+dashed);
    draw_arrow(arc(frame1*O, frame2*(-0.7*lh, 0, 0), frame3*(-0.7*lh, 0, 0)), gray+dashed);
    draw_arrow(arc(frame4*O, frame4*(0, 0, 1.3*dh), frame5*(0, 0, 1.3*dh)), gray+dashed);
}
Rojetto commented 5 years ago

I just happened to stumble upon the changelog of v2.53 which says

A memory leak and antialiasing artifacts in the 3D rendering engine were fixed.

which made me try out v2.52. Result: the error I described does not occur using v2.52, so the changes to the rendering engine probably introduced this bug in v2.53

johncbowman commented 5 years ago

Thank you for the bug report. I was able to reproduce it and am happy to report that the problem is fixed in version 2.54, which I have just released.