1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
package my3d; import javafx.scene.canvas.GraphicsContext; import java.util.ArrayList; import java.util.LinkedList; import java.util.ListIterator; import my3d._math3d._transform; import my3d._shape3d; import my3d._shape3d._line; import my3d._shape3d._plane; public class _line_art3d { private GraphicsContext gc; private _transform tf; public _line_art3d( GraphicsContext gc, double view_w, double view_h, _transform tf ) { this.gc = gc; this.tf = tf; gc.setTransform(view_w, 0, 0, -view_w, view_w / 2, view_h / 2); gc.setLineWidth(1 / view_w); } private ArrayList<_shape3d> all_shapes = new ArrayList<>(); public void add_shape(_shape3d shape) { if (shape != null) all_shapes.add(shape); } private LinkedList<_line> all_lines = new LinkedList<>(); private ArrayList<ArrayList<_plane>> all_clips = new ArrayList<>(); public void draw() { all_lines.clear(); all_clips.clear(); // 形状から線情報とクリップ情報を作成する for (_shape3d shape : all_shapes) create_line_and_clip_list(shape, all_lines, all_clips); // 線をクリップする for (ArrayList<_plane> clip_plane_list : all_clips) clip_line(clip_plane_list, all_lines); // 線を描画する gc.clearRect(-0.5, -0.5, 1, 1); for (_line line : all_lines) draw_line(line); } // 形状から線情報とクリップ情報を作成する private static final int VA_BUF_SIZE = 50; private static final double MIN_LEN = 1e-9; private static final double FRONT = 1; private double[][] va_buf = new double[VA_BUF_SIZE][]; private ArrayList<_line> out_line_list = new ArrayList<>(); private void create_line_and_clip_list( _shape3d shape, LinkedList<_line> line_list, ArrayList<ArrayList<_plane>> clip_list ) { // 座標変換 double[][] va = shape.get_vertices(); double[] v; for (int i = 0; i < va.length; i++) { v = new double[3]; _math3d.mul(tf, va[i], v); if (v[2] < FRONT) return; va_buf[i] = v; } int[][] lines = shape.get_lines(); if (lines != null) { // 線形状の場合 for (int[] line : lines) line_list.add(new _line(va_buf[line[0]], va_buf[line[1]])); return; } int[][] faces = shape.get_faces(); if (faces == null) return; // 面形状の場合 out_line_list.clear(); ArrayList<_plane> clip_plane_list = new ArrayList<>(); _plane plane; ListIterator<_line> line_iter; double[] v1, v2; for (int[] face : faces) { plane = new _plane( va_buf[face[0]], va_buf[face[1]], va_buf[face[2]]); if (!is_visible(plane)) continue; // 可視面ならクリップ面に追加 adjust_len(plane); clip_plane_list.add(plane); // 外周線リストの更新 int j; _line line; face_loop: for (int i = 0; i < face.length; i++) { j = (i + 1) % face.length; v1 = va_buf[face[i]]; v2 = va_buf[face[j]]; // 線の追加と外周線リストの更新 line_iter = out_line_list.listIterator(); while (line_iter.hasNext()) { line = line_iter.next(); if (v1 == line.v2 && v2 == line.v1) { line_iter.remove(); continue face_loop; } } line = new _line(v1, v2); line_list.add(line); line_iter.add(line); } } // 外周面をクリップ面リストに追加 for (_line line : out_line_list) clip_plane_list.add(new _plane(line)); // クリップ情報にクリップ面リストを追加 clip_list.add(clip_plane_list); } private boolean is_visible(_plane plane) { return plane.len < -MIN_LEN; } private void adjust_len(_plane plane) { plane.len -= MIN_LEN; } // 線をクリップする private double[] v_out1 = new double[3], v_out2 = new double[3]; void clip_line( ArrayList<_plane> clip_plane_list, LinkedList<_line> line_list ) { ListIterator<_line> line_iter = line_list.listIterator(); _line line; double[] v1, v2; int result; while (line_iter.hasNext()) { line = line_iter.next(); v1 = line.v1; v2 = line.v2; result = _shape3d.clip(v1, v2, clip_plane_list, v_out1, v_out2); if (result == -1) continue; else if (result == 0) line_iter.remove(); else if (result == 1) { line.v2 = v_out1; v_out1 = new double[3]; } else if (result == 2) { line.v1 = v_out2; v_out2 = new double[3]; } else { line.v2 = v_out1; v_out1 = new double[3]; line_iter.add(new _line(v2, v_out2)); v_out2 = new double[3]; } } } // 線を描画する private double[] p1 = new double[2], p2 = new double[2]; void draw_line(_line line) { project(line.v1, p1); project(line.v2, p2); gc.strokeLine(p1[0], p1[1], p2[0], p2[1]); } private void project(double[] v, double[] po) { double z = v[2]; po[0] = v[0] / z; po[1] = v[1] / z; } }