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;
}
}