1%%
2%% HLines -- Hidden Line Renderer for Wings 0.98.17a and higher.
3%%
4%% Based on "HLines" hidden line elimination program from "Programming
5%% Principles in Computer Graphics" and "Computer Graphics for Java
6%% Programmers" by Leen Ammeraal, http://home.planet.nl/~ammeraal/ .
7%% The source code listed in the books can be found here:
8%% ftp://ftp.expa.fnt.hvu.nl/pub/ammeraal/English/older/ppcgraph.zip
9%% ftp://ftp.expa.fnt.hvu.nl/pub/ammeraal/English/grjava.zip
10%%
11%% Copyright (c) 2003-2015  Dmitry Efremov <defremov@aha.ru>
12%% wpc_hlines.erl,v 1.46 2006/02/26 21:40:13
13%% Support for eps/svg format exporting ,Colored faces rendering ,Processing speed up
14%%
15%% Modified Version
16%% wpc_hlines.erl,v 2.01 2016/03/03
17%% wpc_hlines.erl,v 2.0  2015/08/22
18%% wpc_hlines.erl,v 1.5  2015/07/22
19%% Show elements,Colored lines rendering,Line style,SVG options and Optimize etc.
20%% Copyright (c) 2015 Tukubado(tkbd)
21%%
22%% BUGS:
23%%  Near clipping will not work correctly if occurs in the viewport
24%%  Duplicate and zero length line segments are generated in some cases
25%%
26%% $Id$
27%%
28
29-module(wpc_hlines).
30-author('Dmitry Efremov <defremov@aha.ru>').
31
32-export([init/0, menu/2, command/2]).
33-import(lists, [
34		foldl/3,
35		foreach/2,
36		keymember/3,
37		keysearch/3,
38		keysort/2,
39		keyreplace/4
40	       ]).
41-include_lib("src/wings.hrl").
42-include_lib("e3d/e3d.hrl").
43
44-define(EPS, 1.0e-6).
45-define(EPS1, 1.0e-5).
46-define(EPS2, 1.0e-4).
47-define(BIG, 1.0e30).
48-define(MAXDEPTH, 10).
49
50-define(DEF_FILE_TYPE, file_eps).
51-define(DEF_WIDTH, 288).
52-define(DEF_HEIGHT, 216).
53-define(DEF_EDGE_MODE, hard_edges).
54-define(DEF_EDGE_WIDTH_OUTLINE, 1.0).
55-define(DEF_EDGE_WIDTH_HARD, 1.0).
56-define(DEF_EDGE_WIDTH_CREASE, 1.0).
57-define(DEF_EDGE_WIDTH_MATERIAL, 1.0).
58-define(DEF_EDGE_WIDTH_REGULAR, 1.0).
59-define(DEF_EDGE_WIDTH_LUCENT, 0.5).
60-define(DEF_EDGE_ONE_WIDTH_FOR_ALL, true).
61-define(DEF_CREASE_ANGLE, 0).
62-define(DEF_LINE_CAP, 1).
63-define(DEF_SUBDIVISIONS, 0).
64-define(DEF_OPTIMIZE, false).
65-define(DEF_COLL_ANGLE, 0.5).
66-define(DEF_COLL_DIST, 0.25).
67-define(DEF_DRAW_MODE, both).
68-define(DEF_RESPONSIVE, false).
69
70-define(DEF_VAR_STROKE, false).
71-define(DEF_SVG_ATTB_TYPE, attribute).
72-define(DEF_SVG_RENDER_TYPE, smooth_fill ).
73
74-define(DEF_DIVISION_PRIORITY, scene).
75-define(DEF_Z_DIST_OFFSET,2).
76
77-define(DEF_SVG_ART_FILL_TYPE, 1 ).
78-define(DEF_SVG_ART_LINE_TYPE, 1 ).
79
80-define(DEF_FILL_SHADE_TYPE, 0 ).
81-define(DEF_LIGHT_POS, {-480,-512,500,0.00096} ).
82
83-define(DEF_OUTL_COLOR, {0.0, 0.0, 0.0, 1.0}).
84-define(DEF_HARD_COLOR, {0.0, 0.0, 0.0, 1.0}).
85-define(DEF_CREA_COLOR, {0.0, 0.0, 0.0, 1.0}).
86-define(DEF_MATL_COLOR, {0.0, 0.0, 0.0, 1.0}).
87-define(DEF_LCNT_COLOR, {0.0, 0.0, 0.0, 1.0}).
88-define(DEF_REGL_COLOR, {0.0, 0.0, 0.0, 1.0}).
89
90
91-define(DEF_OUTL_PATTERN, "0" ).
92-define(DEF_HARD_PATTERN, "0" ).
93-define(DEF_CREA_PATTERN, "0" ).
94-define(DEF_MATL_PATTERN, "0" ).
95-define(DEF_REGL_PATTERN, "0" ).
96-define(DEF_LCNT_PATTERN, "2 2" ).
97
98-define(DEF_SCALE_PROP, false).
99-define(DEF_SVG_COLOR_INT, 0).
100
101
102init() ->
103    %% that force the wrong preferences stored before the fix to be updated
104    case get_pref( light_pos, ?DEF_LIGHT_POS) of
105	[] -> wpa:pref_delete(?MODULE, light_pos);
106	_ -> ignore
107    end,
108    true.
109
110menu({file, export}, Menu) ->
111    menu_entry(Menu);
112menu({file, export_selected}, Menu) ->
113    menu_entry(Menu);
114menu(_, Menu) -> Menu.
115
116menu_entry(Menu) ->
117    Menu ++ [{"Cartoon edges (.eps, .svg)...", eps, [option]}].
118
119command({file, {export, {eps, Arg}}}, St) ->
120    export(Arg, export, St);
121command({file, {export_selected, {eps, Arg}}}, St) ->
122    export(Arg, export_selected, St);
123command({file, {export_uv, {eps, Arg}}}, St) ->
124    export(Arg, export_uv, St);
125command(_, _) -> next.
126
127export(Arg, Op, St) when is_atom(Arg) ->
128    Fun =
129	case Op of
130            export_uv -> fun(Res) -> {file, {Op, {eps, {Res, St}}}} end;
131            _ -> fun(Res) -> {file, {Op, {eps, Res}}} end
132	end,
133    wpa:dialog(Arg, ?__(1,"Cartoon edges Render Options (EPS/SVG)"), dialog(), Fun);
134export(Arg, Op, St0) when is_list(Arg) ->
135    set_pref(Arg),
136    Camera_info = wpa:camera_info([aim, distance_to_aim,
137				   azimuth, elevation, tracking,
138				   fov, hither, yon, pos_dir_up]),
139    File_type = get_pref(file_type, ?DEF_FILE_TYPE),
140    Props = [{title, "Export"},
141	     {ext, file_type(File_type, suffix)},
142	     {ext_desc, file_type(File_type, desc)},
143	     {camera_info, Camera_info},
144	     {subdivisions, get_pref(subdivisions, ?DEF_SUBDIVISIONS)},
145	     {win_size, wings_wm:win_size()},
146	     {ortho_view, wings_wm:get_prop(orthogonal_view)}],
147
148    %% Freeze virtual mirrors.
149    Shapes0 = gb_trees:to_list(St0#st.shapes),
150    Shapes = [{Id, wpa:vm_freeze(We)} || {Id, We} <- Shapes0],
151    St = St0#st{shapes = gb_trees:from_orddict(Shapes)},
152
153    case Op of
154        export ->
155            ?SLOW(wpa:export(Props, fun_export(Props), St))
156      ;
157        export_selected ->
158            ?SLOW(wpa:export_selected(Props, fun_export(Props), St))
159    end;
160export(Arg, export_uv, _) when is_tuple(Arg) ->
161    {Arg0,St} = Arg,
162    set_pref(Arg0),
163    Camera_info = [{0.0,0.0,0.0}, 1.2,
164                   0.0, 0.0, {-0.5,-0.5},
165                   45.0, -0.001, 10.0, {0.0,1.0,0.0}],
166    File_type = get_pref(file_type, ?DEF_FILE_TYPE),
167    Props = [{title, "Export"},
168             {ext, file_type(File_type, suffix)},
169             {ext_desc, file_type(File_type, desc)},
170             {camera_info, Camera_info},
171             {subdivisions, 0},
172             {win_size, {100,100}},
173             {ortho_view, true}],
174
175    ?SLOW(wpa:export(Props, fun_export(Props), St)).
176
177
178dialog() ->
179    IntlPT = ?__(1,"pt"),
180    File_type = get_pref(file_type, ?DEF_FILE_TYPE),
181    BB_width = get_pref(bb_width, ?DEF_WIDTH),
182    BB_height = get_pref(bb_height, ?DEF_HEIGHT),
183    Edge_mode = get_pref(edge_mode, ?DEF_EDGE_MODE),
184    Edge_width_outline = get_pref(edge_width_outline,
185				  ?DEF_EDGE_WIDTH_OUTLINE),
186    Edge_width_hard = get_pref(edge_width_hard, ?DEF_EDGE_WIDTH_HARD),
187    Edge_width_crease = get_pref(edge_width_crease, ?DEF_EDGE_WIDTH_CREASE),
188    Edge_width_material = get_pref(edge_width_material, ?DEF_EDGE_WIDTH_MATERIAL),
189    Edge_width_regular = get_pref(edge_width_regular,
190				  ?DEF_EDGE_WIDTH_REGULAR),
191    Edge_width_lucent = get_pref(edge_width_lucent, ?DEF_EDGE_WIDTH_LUCENT),
192
193    Edge_one_width_for_all = get_pref(edge_one_width_for_all,
194				      ?DEF_EDGE_ONE_WIDTH_FOR_ALL),
195    Crease_angle = get_pref(crease_angle, ?DEF_CREASE_ANGLE),
196    Line_cap = get_pref(line_cap, ?DEF_LINE_CAP),
197    SubDiv = get_pref(subdivisions, ?DEF_SUBDIVISIONS),
198    Optimize = get_pref(optimize, ?DEF_OPTIMIZE),
199    Coll_angle = get_pref(coll_angle, ?DEF_COLL_ANGLE),
200    Coll_dist = get_pref(coll_dist, ?DEF_COLL_DIST),
201    Draw_mode = get_pref(draw_mode, ?DEF_DRAW_MODE),
202    Responsive = get_pref(responsive, ?DEF_RESPONSIVE),
203    Var_stroke = get_pref(var_stroke, ?DEF_VAR_STROKE),
204    Svg_attb_type = get_pref(svg_attb_type, ?DEF_SVG_ATTB_TYPE),
205    Division_priority = get_pref( division_priority, ?DEF_DIVISION_PRIORITY),
206    Z_dist_offset = get_pref( z_dist_offset, ?DEF_Z_DIST_OFFSET),
207
208    Svg_art_fill_type = get_pref(svg_art_fill_type, ?DEF_SVG_ART_FILL_TYPE),
209    Svg_art_line_type = get_pref(svg_art_line_type, ?DEF_SVG_ART_LINE_TYPE),
210
211    Fill_shade_type = get_pref(fill_shade_type, ?DEF_FILL_SHADE_TYPE),
212    Light_pos = get_pref( light_pos, ?DEF_LIGHT_POS),
213
214    Outl_color = get_pref( outl_color, ?DEF_OUTL_COLOR),
215    Hard_color = get_pref( hard_color, ?DEF_HARD_COLOR),
216    Crea_color = get_pref( crea_color, ?DEF_CREA_COLOR),
217    Matl_color = get_pref( matl_color, ?DEF_MATL_COLOR),
218    Regl_color = get_pref( regl_color, ?DEF_REGL_COLOR),
219    Lcnt_color = get_pref( lcnt_color, ?DEF_LCNT_COLOR),
220
221    Outl_pattern = get_pref( outl_pattern, ?DEF_OUTL_PATTERN),
222    Hard_pattern = get_pref( hard_pattern, ?DEF_HARD_PATTERN),
223    Crea_pattern = get_pref( crea_pattern, ?DEF_CREA_PATTERN),
224    Matl_pattern = get_pref( matl_pattern, ?DEF_MATL_PATTERN),
225    Regl_pattern = get_pref( regl_pattern, ?DEF_REGL_PATTERN),
226    Lcnt_pattern = get_pref( lcnt_pattern, ?DEF_LCNT_PATTERN),
227
228    Scale_prop = get_pref( scale_prop, ?DEF_SCALE_PROP),
229    Svg_color_int = get_pref( svg_color_int, ?DEF_SVG_COLOR_INT),
230
231
232    Svg_render_type = get_pref( svg_render_type, ?DEF_SVG_RENDER_TYPE),
233    [
234     {hframe, [
235	       {label, ?__(200,"File type") },
236	       {menu, [
237		       {file_type(file_eps), file_eps},
238		       {file_type(file_svg), file_svg},
239		       {file_type(file_test_svg), file_test_svg}
240		      ], File_type, [{key, file_type}]}
241	      ]},
242
243     {vframe, [
244	       {hframe,[
245			{label, ?__(100,"Width")},
246			{text, BB_width, [{key, bb_width}]},
247			{label, ?__(101,"Height")},
248			{text, BB_height, [{key, bb_height}]},
249			{label, IntlPT},
250			{label, ?__(102,"Proportional Effect")},
251			{ ?__(103,"Enable"), Scale_prop, [{key, scale_prop}
252							 ]}
253		       ]}
254	      ], [{title, ?__(201,"Bounding box") }]},
255
256     {vframe, [
257	       {hframe, [
258			 {label,?__(104,"Subdiv Steps")},
259			 {text,SubDiv,[{key,subdivisions},{range,0,4}]},
260			 {label,?__(105,"Overlap push")},
261			 {text,Z_dist_offset,[{key,z_dist_offset},{range,0,10}]}
262			]}
263
264	      ], [{title, ?__(106,"Pre-rendering") }]},
265
266
267     {hframe, [
268	       { menu, [
269			{ ?__(107,"Scene (made many sliced,and file size larger)")     , scene},
270			{ ?__(108,"Path  (divided nicely,but messed if overlap areas)"), path }
271		       ], Division_priority, [{key, division_priority }]}
272	      ]
273     , [{title, ?__(109,"Surface division Priorities") }]},
274
275     {hframe, [
276	       {vradio, [
277			 {?__(110,"Both"), both },
278			 {?__(111,"Edge"), edge_only },
279			 {?__(112,"Face"), face_only }
280			], Draw_mode, [{key, draw_mode}, {title, ?__(113 ,"Export") }]},
281
282
283	       {vframe, [
284			 {hframe, [ {label, ?__(114,"CSS:") },
285				    {menu, [
286					    {?__(115,"Use Attribute"), attribute},
287					    {?__(116,"Use ClassName"), css_class},
288					    {?__(117,"Use AttrCompd"), compound }
289					   ], Svg_attb_type, [{key, svg_attb_type}]},
290				    {label, ?__(118,"Shape-rendering") },
291				    {menu, [
292					    { ?__(119,"Auto")      , none  },
293					    { ?__(120,"Smooth")    , smooth_fill },
294					    { ?__(121,"Jaggy line"), jaggy_line  }
295					   ], Svg_render_type, [{key, svg_render_type}]}
296				  ]},
297			 {vframe, [
298				   {hframe, [ {label, ?__(122,"NPR:Fill") }, {menu, define_svg_filter(ui, 0), Svg_art_fill_type, [{key, svg_art_fill_type}]},  {?__(123,"Responsive"), Responsive, [{key, responsive}]}  ]},
299				   {hframe, [ {label, ?__(124,"NPR:Line") }, {menu, define_svg_filter(ui, 0), Svg_art_line_type, [{key, svg_art_line_type}]},  {?__(125,"VarStroke"), Var_stroke, [{key, var_stroke}]}   ]},
300				   {hframe, [ {label, ?__(126,"Shading ") }, {menu, define_fill_type() , Fill_shade_type,        [{key, fill_shade_type}]}  ,
301					      {label, ?__(127,"Lighting") }, {menu, define_light_pos() , Light_pos,              [{key, light_pos}]}  ]},
302				   {hframe, [ {label, ?__(128,"Color interpolation")},  { menu, [{ "sRGB", 0 }, { "linearRGB", 1 }, { "auto", 2 } ], Svg_color_int, [{key, svg_color_int} ]}  ]}
303				  ]}
304			], [{title, ?__(202,"SVG Export Options") }]}
305
306	      ]},
307     separator,
308     {vframe, [  {label, ?__(129,"Edge display Options")} ]},
309
310     {hframe, [
311	       {hradio, [
312			 {?__(130,"All ")  , all_edges},
313			 {?__(131,"Hard")  , hard_edges},
314			 {?__(132,"Others"), no_edges}
315			], Edge_mode, [{key, edge_mode},
316				       {title, ?__(133,"Show edges") }]},
317
318	       {hframe, [
319			 {slider,
320			  {text, Crease_angle, [
321						{key, crease_angle}, {range, {0, 180}}
322					       ]}}
323			], [{title, ?__(134,"Crease angle(Exclude if exceed an angle.)") }]}
324
325	      ]},
326
327     {vframe, [
328	       {hframe,  [ {label, ?__(135, "Outline ") }, {text, Edge_width_outline,  [{key, edge_width_outline}, {range, {0.0, ?BIG}}]}, {label, IntlPT},
329			   {color,  Outl_color, [{key, outl_color}] },  { menu, define_dot_styles(), Outl_pattern, [{key, outl_pattern }]}
330			 ]},separator,
331
332	       {hframe, [
333			 {vframe, [
334				   {label, ?__(136,"Hard") },
335				   {label, ?__(137,"Crease") },
336				   {label, ?__(138,"Material") },
337				   {label, ?__(139,"Lucent") }
338				  ]},
339			 {vframe, [
340				   {text, Edge_width_hard,  [{key, edge_width_hard}, {range, {0.0, ?BIG}}]},
341				   {text, Edge_width_crease, [{key, edge_width_crease}, {range, {0.0, ?BIG}}]},
342				   {text, Edge_width_material, [{key, edge_width_material}, {range, {0.0, ?BIG}}]},
343				   {text, Edge_width_lucent, [{key, edge_width_lucent}, {range, {0.0, ?BIG}}]}
344				  ]},
345			 {vframe, [
346				   {hframe,  [      {label, IntlPT}, {color,  Hard_color, [{key, hard_color}] }  ]},
347				   {hframe,  [      {label, IntlPT}, {color,  Crea_color, [{key, crea_color}] }  ]},
348				   {hframe,  [      {label, IntlPT}, {color,  Matl_color, [{key, matl_color}] }  ]},
349				   {hframe,  [      {label, IntlPT}, {color,  Lcnt_color, [{key, lcnt_color}] }  ]}
350				  ]},
351			 {vframe, [
352				   {hframe,  [    { menu, define_dot_styles(), Hard_pattern, [{key, hard_pattern }]} ]},
353				   {hframe,  [    { menu, define_dot_styles(), Crea_pattern, [{key, crea_pattern }]} ]},
354				   {hframe,  [    { menu, define_dot_styles(), Matl_pattern, [{key, matl_pattern }]} ]},
355				   {hframe,  [    { menu, define_dot_styles(), Lcnt_pattern, [{key, lcnt_pattern }]} ]}
356				  ]}
357			]},
358	       separator,
359	       {hframe,  [   {label, ?__(140,"Regular ") } ,  {text, Edge_width_regular, [{key, edge_width_regular}, {range, {0.0, ?BIG}} ]},
360			     {label, IntlPT}, {color, Regl_color, [{key, regl_color}] } ,
361			     { menu, define_dot_styles(), Regl_pattern, [{key, regl_pattern }]} ,
362			     {?__(141,"All") , Edge_one_width_for_all, [{key, edge_one_width_for_all}]}
363			 ]}
364
365	      ], [{title, ?__(142,"Edge width,Color,dashed style") }]},
366
367
368     {hframe, [
369	       {vradio, [
370			 {?__(143,"Butt")  , 0},
371			 {?__(144,"Round") , 1},
372			 {?__(145,"Square"), 2}
373			], Line_cap, [{key, line_cap}, {title, ?__(146,"Line caps") }]},
374
375	       {vframe, [
376			 { ?__(147,"Merge"), Optimize, [{key, optimize}]},
377			 {hframe, [
378				   {vframe, [
379					     {label, ?__(148,"Angle") },
380					     {label, ?__(149,"Distance") }
381					    ]},
382				   {vframe, [
383					     {text, Coll_angle, [
384								 {key, coll_angle}, {range, {0.0, 90.0}}
385								]},
386					     {text, Coll_dist, [
387								{key, coll_dist}, {range, {0.0, ?BIG}}
388							       ]}
389					    ]},
390				   {vframe, [
391					     {label, [176]},
392					     {label, IntlPT}
393					    ]}
394				  ]}
395			], [{title, ?__(150,"Collinear lines") }]}
396
397	      ]}
398    ].
399
400get_pref(Key, Def) ->
401    wpa:pref_get(?MODULE, Key, Def).
402
403set_pref(KeyVals) ->
404    wpa:pref_set(?MODULE, KeyVals).
405
406fun_export(Props) ->
407    fun (File_name, Scene) -> do_export(Props, File_name, Scene) end.
408
409file_type(file_eps, desc) -> "Encapsulated Postscript";
410file_type(file_eps, suffix) -> ".eps";
411file_type(file_svg, desc) ->       "Scalable Vector Graphics";
412file_type(file_svg, suffix) -> ".svg";
413file_type(file_test_svg, desc) -> "SVG style setting Help(Non 3D)";
414file_type(file_test_svg, suffix) -> ".svg".
415
416
417
418file_type(File_type) ->
419    lists:flatten([file_type(File_type, desc), " (*",
420		   file_type(File_type, suffix), ")"]).
421
422file_type_funs(file_eps) ->
423    {fun write_eps_header/5, fun write_eps_line_group/9,
424     fun write_eps_polygons/5, fun write_eps_footer/1};
425file_type_funs(file_svg) ->
426    {fun write_svg_header/5, fun write_svg_line_group/9,
427     fun write_svg_polygons/5, fun write_svg_footer/1};
428file_type_funs(file_test_svg) ->
429    {fun write_svg_header/5, fun write_svg_line_group/9,
430     fun write_svg_polygons/5, fun write_svg_footer/1}.
431
432%%
433%%  Main
434%%
435
436%%  Style Catalog Mode (for SVG Style preview,don't save the 3D Model Data)
437do_export(Props, File_name, #e3d_file{objs=Objs, mat=Mats}) ->
438
439    Is_style_catalog = (file_test_svg =:= get_pref(file_type, ?DEF_FILE_TYPE)),
440    case Is_style_catalog of
441	true ->
442
443	    Start_time = os:timestamp(),
444
445
446	    Wbb = get_max(get_pref(bb_width, ?DEF_WIDTH), 1500),
447	    Hbb = get_max(get_pref(bb_height, ?DEF_HEIGHT), 750),
448
449
450
451	    Mats_dict = materials(Mats),
452
453	    wings_pb:start(""),
454	    io:format("~n", []),
455	    LSpos = {get_pref(fill_shade_type, ?DEF_FILL_SHADE_TYPE), get_pref( light_pos, ?DEF_LIGHT_POS) },
456	    BB_size = {Wbb, Hbb},
457	    Line_cap = get_pref(line_cap, ?DEF_LINE_CAP),
458
459	    {Write_header, _Write_line_group, _Write_polygons, Write_footer} = file_type_funs(get_pref(file_type, ?DEF_FILE_TYPE)),
460	    wings_pb:update(0.5, "All the SVG styles are exporting"),
461	    wings_pb:pause(),
462	    {ok, F} = file:open(File_name, [write]),
463
464	    Write_header(F, BB_size, Line_cap,Mats_dict,LSpos),
465	    Write_footer(F),
466	    ok = file:close(F),
467
468	    wings_pb:update(1.0, "done"),
469	    wings_pb:done(),
470	    io:format(" "++"done in ~.1f sec"++"~n", [timer:now_diff(os:timestamp(), Start_time) / 1.0e6]);
471
472
473	%% Export Mode (3D Model export to SVG/EPS)
474	false ->
475
476	    Start_time = os:timestamp(),
477
478	    [Aim, Distance, Azimuth, Elevation, {TrackX, TrackY}, Fov, Hither, Yonder, _Pos_dir_up] =
479		proplists:get_value(camera_info, Props),
480
481	    Taim = e3d_mat:translate(Aim),
482	    Ry = e3d_mat:rotate(Azimuth, {0.0, 1.0, 0.0}),
483	    Rx = e3d_mat:rotate(Elevation, {1.0, 0.0, 0.0}),
484	    Tdist = e3d_mat:translate({TrackX, TrackY, -Distance}),
485	    Veye = e3d_mat:mul(Tdist, e3d_mat:mul(e3d_mat:mul(Rx, Ry), Taim)),
486
487
488
489	    %% io:format("EyePos ~p   ~n",[Pos_eye]),
490
491	    LSpos = {get_pref(fill_shade_type, ?DEF_FILL_SHADE_TYPE), get_pref( light_pos, ?DEF_LIGHT_POS) },
492
493
494	    Wbb = get_max(get_pref(bb_width, ?DEF_WIDTH), 1),
495	    Hbb = get_max(get_pref(bb_height, ?DEF_HEIGHT), 1),
496	    ARbb = Wbb / Hbb,
497	    {Wwin, Hwin} = proplists:get_value(win_size, Props),
498	    ARwin = Wwin / Hwin,
499
500	    {ARw, ARh, Eps} = if
501				  ARbb > ARwin -> {ARbb, 1.0, 1.0 / Hbb * 0.01};
502				  true -> {ARwin, ARwin / ARbb, ARwin / Wbb * 0.01}
503			      end,
504
505	    Epseps = Eps * Eps,
506
507	    Flen = 0.5 / math:tan(Fov * math:pi() / 180.0 / 2.0),
508	    Zf = -Flen,
509
510	    Is_ortho = proplists:get_value(ortho_view, Props),
511
512	    Proj = if Is_ortho ->
513	    		   fun({X, Y, _Z}) -> {X, Y};
514	    		      ({{X1, Y1, _Z1}, {X2, Y2, _Z2}}) ->
515	    			   {{X1, Y1}, {X2, Y2}}
516	    		   end;
517	    	      true ->
518	    		   fun({X, Y, Z}) ->
519	    			   Rz = case catch Zf / Z of
520	    				    R when is_float(R) -> R; _ -> ?BIG end,
521	    			   {X * Rz, Y * Rz};
522	    		      ({{X1, Y1, Z1}, {X2, Y2, Z2}}) ->
523	    			   Rz_1 = case catch Zf / Z1 of
524	    				      R1 when is_float(R1) -> R1; _ -> ?BIG end,
525	    			   Rz_2 = case catch Zf / Z2 of
526	    				      R2 when is_float(R2) -> R2; _ -> ?BIG end,
527	    			   {{X1 * Rz_1, Y1 * Rz_1}, {X2 * Rz_2, Y2 * Rz_2}}
528	    		   end
529	    	   end,
530	    Front_face = if Is_ortho -> fun ortho_ff/1;
531	    		    true -> fun persp_ff/1
532	    		 end,
533	    Side_face = if Is_ortho -> fun ortho_sf/1;
534	    		   true -> fun persp_sf/1
535	    		end,
536	    Wvp = if Is_ortho -> Distance * ARw / Flen;
537	    	     true -> ARw
538	    	  end,
539	    Hvp = if Is_ortho -> Distance * ARh / Flen;
540	    	     true -> ARh
541	    	  end,
542
543	    View_port = {{-Wvp / 2.0, -Hvp / 2.0, -Yonder},
544			 {Wvp / 2.0, Hvp / 2.0, -Hither}},
545
546	    Frustum = if
547			  Is_ortho -> View_port;
548			  true -> {Wvp / 2.0, Hvp / 2.0, -Yonder, -Hither, Zf}
549		      end,
550
551	    Frustum_planes = frustum_planes(Frustum),
552
553	    VCs = foldl(fun(#e3d_object{obj=Obj}, VCs_acc) ->
554				VCs_acc ++ Obj#e3d_mesh.vs
555			end, [], Objs),
556
557	    EyeMesh = e3d_mesh:transform(#e3d_mesh{vs = VCs}, Veye),
558
559	    VC_tree0 = ctree__from_list(Proj, EyeMesh#e3d_mesh.vs),
560
561						%io:format("Mats=~w~n~n", [Mats]),
562	    Mats_dict = materials(Mats),
563						%io:format("Mats_dict=~w~n~n", [Mats_dict]),
564	    Crease_angle = get_pref(crease_angle, ?DEF_CREASE_ANGLE),
565	    Thresh_cos = math:cos((180.0 - Crease_angle) * math:pi() / 180.0) + ?EPS,
566	    Edge_mode = get_pref(edge_mode, ?DEF_EDGE_MODE),
567	    Edge_type_fun =
568		case get_pref(edge_one_width_for_all, ?DEF_EDGE_ONE_WIDTH_FOR_ALL) of
569		    false ->
570			fun edge_type_group/6
571			    ;
572		    true ->
573			fun edge_type_copy/6
574		end,
575
576
577
578	    %% Process Start
579
580	    wings_pb:start(""),
581	    io:format("~ts~n", ["Start"]),
582
583	    Objs_total = length(Objs),
584	    Draw_mode = get_pref(draw_mode, ?DEF_DRAW_MODE),
585
586	    {VC_tree, Edge_dict, Face_tree, _VI_incr, _Obj_count}
587		= foldl(fun(#e3d_object{name=Name0, obj=Mesh},
588			    {VC_tree_acc0, Edge_dict_acc0, Face_tree_acc0,
589			     VI_incr, Obj_count0}) ->
590				Obj_count = Obj_count0 + 1,
591
592				Percent = Obj_count0 / Objs_total,
593				wings_pb:update(Percent * 0.28 + 0.01,
594						?__(1,"reading objects") ++ " " ++
595						    integer_to_list(round(Percent * 100.0)) ++ "%"),
596				wings_pb:pause(),
597				Name = if is_list(Name0) -> Name0; true -> [] end,	%% exporting uv map name is not valid
598				io:format(?__(2,"Reading object ~B of") ++" ~B \"~ts\"...",
599					  [Obj_count, Objs_total, Name]),
600
601				#e3d_mesh{vs=MVCs, fs=MFs, he=MHEs}=Mesh,
602				Is_open = is_open(Mesh) orelse has_transp_faces(Mesh, Mats_dict),
603
604
605
606				case Draw_mode =:= both orelse  Draw_mode =:= face_only of
607				    false ->
608
609					#e3d_mesh{fs=TMFs} = e3d_mesh:triangulate(Mesh),
610					{VC_tree_acc, ME_dict, Face_tree_acc} =
611					    add_trimesh(incr(TMFs, VI_incr),
612							Is_open, Frustum, Frustum_planes, Front_face, Side_face,
613							VC_tree_acc0, dict:new(), Face_tree_acc0)
614					    ;
615				    true ->
616
617
618%%%%% Add convex and flatness tests
619					{VC_tree_acc, ME_dict, Face_tree_acc} =
620					    add_mesh(incr(MFs, VI_incr), VCs, Epseps,
621						     Is_open, Frustum, Frustum_planes, Front_face, Side_face,
622						     VC_tree_acc0, dict:new(), Face_tree_acc0 )
623
624%%%%%
625				end,
626
627
628%%%%  Edge draw
629				{HardEdge_set, Edge_set} = edge_sets(MHEs, MFs, Edge_mode, VI_incr),
630				Edge_dict_acc = group_edges(Thresh_cos, HardEdge_set, Edge_set,
631							    Edge_type_fun, ME_dict, Edge_dict_acc0,Mats_dict),
632
633				io:format(" "++?__(3,"done")++"~n", []),
634
635				{VC_tree_acc, Edge_dict_acc, Face_tree_acc,
636				 VI_incr + length(MVCs), Obj_count}
637			end, {VC_tree0, dict__new(), tree__empty(), 0, 0}, Objs),
638
639
640
641	    %%  add_Mesh / add_trimesh  Is_open/transparent
642						%
643	    %% EDGE_DICT:
644	    %% [{crease,[{3,7},{6,7},{0,2},{0,4},{5,7},{0,1}]},
645	    %%  {outline,[{2,3},{4,6},{1,3},{1,5},{4,5},{2,6}]}]
646						%
647
648	    %% io:format("EDGE_DICT:~n ~p ~n",[Edge_dict]),
649
650
651	    wings_pb:update(0.3, ?__(4,"reading objects")++" 100%"),
652	    wings_pb:pause(),
653	    io:format("Exporting"),
654
655%%% for debug function (Warnings are get,but don't delete these functions)
656	    %%   tree__to_list/1
657	    %%   tree__from_list/1
658	    %%   project/2
659
660	    %%io:format("*\tVC_tree=~w~n", [VC_tree]),
661	    %%    VCs1 = tree__to_list(VC_tree),
662	    %%io:format("*\tVCs=~w~n", [VCs1]),
663	    %%    V2C_tree = tree__from_list(project(Proj, VCs1)),
664	    %%io:format("*\tV2C_tree=~w~n", [V2C_tree]),
665
666						%io:format("*\tFace_tree=~w~n", [Face_tree]),
667
668
669	    {Face_bspt, VC_tree1} = tree__fold(fun(FI, {FVIs, _FP, _Mat} = Face, {Face_bspt_acc0, VC_tree_acc0}) ->
670						       FVCs = ctree__get(FVIs, VC_tree_acc0),
671						       FZt = face_zlim(FVCs),
672						       bspt__insert(FI, Face, FZt, Face_bspt_acc0, VC_tree_acc0)
673					       end, {bspt__empty(), VC_tree}, Face_tree),
674
675
676						%io:format("*\tFace_bspt=~w~n", [Face_bspt]),
677
678	    O_qtree = tree__fold(fun(_FI, {FVIs, FP, Mat} = _Face, O_qtree_acc0) ->
679					 {Is_transparent, _RGBA} = hd(dict__fetch(hd(Mat), Mats_dict)),
680					 case Is_transparent of
681					     true ->
682						 O_qtree_acc0
683						     ;
684					     false ->
685						 FVCs = ctree__get(FVIs, VC_tree),
686						 Fzmax = face_zmax(FVCs),
687						 FV2Cs = ctree__get2d(FVIs, VC_tree),
688						 FBB = bbox(FV2Cs),
689						 qtree__insert({{FVIs, FP}, FBB, Fzmax}, O_qtree_acc0)
690					 end
691				 end, qtree__new(bbox_2d(View_port)), Face_tree),
692
693
694	    %% Line Group structure by Line Code
695	    %% Edge dict + Style(Line width,color etc) = Line Group
696	    %% [{ "Outline", %Line_Code
697	    %%      [
698	    %%       Width,
699	    %%       { x0, y0 },
700	    %%       { x1, y1 },
701	    %%            :
702	    %%            :
703	    %%        { x  ,y },
704	    %%        {R,G,B},
705	    %%        "Pattern" ]
706
707	    {Line_groups, Edges_total} =
708		foldl(fun({Line_code, { Line_width ,Edges, Line_color,Line_pattern} }, {Dict_acc, Edge_count}) ->
709			      {dict__append_list(Line_code, { Line_width, Edges, Line_color,Line_pattern } , Dict_acc),
710			       Edge_count + length(Edges)}
711		      end,
712		      {dict__new(), 0 },
713		      [{Line_code, {  Line_width,  Edges, Line_color, Line_pattern } } ||
714			  {Edge_type1, Line_code, Line_width, Line_color, Line_pattern } <- [
715											     {outline,  "Outline", get_pref(edge_width_outline,  ?DEF_EDGE_WIDTH_OUTLINE) , get_pref( outl_color, ?DEF_OUTL_COLOR), get_pref( outl_pattern, ?DEF_OUTL_PATTERN) },
716											     {hard,     "Hadedge", get_pref(edge_width_hard,     ?DEF_EDGE_WIDTH_HARD)    , get_pref( hard_color, ?DEF_HARD_COLOR), get_pref( hard_pattern, ?DEF_HARD_PATTERN) },
717											     {crease,   "Crease" , get_pref(edge_width_crease,   ?DEF_EDGE_WIDTH_CREASE)  , get_pref( crea_color, ?DEF_CREA_COLOR), get_pref( crea_pattern, ?DEF_CREA_PATTERN) },
718											     {material, "Materia", get_pref(edge_width_material, ?DEF_EDGE_WIDTH_MATERIAL), get_pref( matl_color, ?DEF_MATL_COLOR), get_pref( matl_pattern, ?DEF_MATL_PATTERN) },
719											     {regular,  "Regular", get_pref(edge_width_regular,  ?DEF_EDGE_WIDTH_REGULAR) , get_pref( regl_color, ?DEF_REGL_COLOR), get_pref( regl_pattern, ?DEF_REGL_PATTERN) },
720											     {transp, "Trans_hide", get_pref(edge_width_lucent,  ?DEF_EDGE_WIDTH_LUCENT)  , get_pref( lcnt_color, ?DEF_LCNT_COLOR), get_pref( lcnt_pattern, ?DEF_LCNT_PATTERN) }
721
722											    ],% Add Line Group code (Aboid the bug when same width size setting in dictionary)
723			  {Edge_type2, Edges  } <- Edge_dict,
724			  Edge_type1 =:= Edge_type2]),
725
726	    %% io:format("LGP: ~p ~n",[Line_groups]),
727
728
729	    Prog_step = get_max(Edges_total div 20, 250),
730
731
732
733	    %%
734	    %%  Header
735	    %%
736
737
738	    BB_size = {Wbb, Hbb},
739	    Line_cap = get_pref(line_cap, ?DEF_LINE_CAP),
740
741	    File_type_g = get_pref(file_type, ?DEF_FILE_TYPE),
742	    {Write_header, Write_line_group, Write_polygons, Write_footer}
743		= file_type_funs(File_type_g),
744	    {ok, F} = file:open(File_name, [write]),
745	    Write_header(F, BB_size, Line_cap,Mats_dict,LSpos),
746
747	    Offset = divide(bbox_size(bbox_2d(View_port)), 2.0),
748
749
750
751	    %%
752	    %%  Face Draw
753	    %%
754
755	    case Draw_mode == both orelse  Draw_mode == face_only of
756		true ->
757
758		    %% Begin Draw Face
759
760		    Write_polygons(F, BB_size,
761				   [{transvc(bbox_2d(View_port), Offset, Wbb / Wvp,  ctree__get2d(get_fis(FI, FVIs, Face_tree), VC_tree1) ), FP,  Mat}
762				    || {FI, {FVIs, FP, Mat}} <- bspt__to_list(Face_bspt)], Mats_dict,LSpos);
763
764		%% End Draw Face
765
766		false -> ok
767	    end,
768
769	    %%
770	    %%  Edge Draw
771	    %%
772
773	    case Draw_mode == both orelse  Draw_mode == edge_only of
774		true ->
775
776		    case File_type_g of
777			file_svg ->
778			    Stype =  get_pref(svg_art_line_type, ?DEF_SVG_ART_LINE_TYPE),
779			    io:put_chars(F,"\n<g id=\"edges\" inkscape:label=\"edges\" inkscape:groupmode=\"layer\" "++
780					     add_svg_filter(line,Stype) ++
781					     " xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\">\n");
782			_ -> ok
783		    end,
784
785		    %% Begin Draw Edge
786
787		    foldl(fun({ Line_code, {Line_width ,Edges, Line_color,Line_pattern} }, {Group_count, Edge_count0}) ->
788				  %% Line code is only access dictionary
789				  {Ls0, Edge_count} = foldl(fun(EVIt, {Ls_acc0, Edge_count_acc0}) ->
790								    EVCt = vct(EVIt, VC_tree),
791								    {Ls_acc, Edge_count_acc} =
792									case clip_z(EVCt, View_port) of
793									    nil ->
794										{Ls_acc0, Edge_count_acc0}
795										    ;
796									    LVCt ->
797										LV2Ct = Proj(LVCt),
798										{Ls_acc0 ++ line_segment(EVIt, LVCt, LV2Ct,
799													 qtree__get_objs(bbox(LV2Ct), O_qtree),
800													 VC_tree, Proj, Is_ortho, Epseps),
801										 Edge_count_acc0 + 1}
802									end,
803								    if
804									Edge_count_acc rem Prog_step =:= 0 ->
805									    Percent = Edge_count_acc / Edges_total,
806									    wings_pb:update(Percent * 0.69 + 0.3,
807											    integer_to_list(round(Percent * 100.0)) ++ "%"),
808									    wings_pb:pause()
809										;
810									true ->
811									    ok
812								    end,
813								    {Ls_acc, Edge_count_acc}
814							    end, {[], Edge_count0}, Edges),
815				  Ls1 = translate(bbox_2d(View_port), Offset, Wbb / Wvp, Ls0),
816				  Ls = case get_pref(optimize, ?DEF_OPTIMIZE) of
817					   true ->
818					       Athr_deg = get_pref(coll_angle, ?DEF_COLL_ANGLE),
819					       Athr = math:sin(Athr_deg * math:pi() / 180.0),
820					       Dthr = get_pref(coll_dist, ?DEF_COLL_DIST),
821					       lstree_to_list(lists:foldl(fun(L, Ls_acc) ->
822										  lstree_insert(L, Ls_acc, Athr, Dthr)
823									  end, lstree_empty(), Ls1))
824						   ;
825					   false ->
826					       Ls1
827				       end,
828				  Write_line_group(F, BB_size, Ls, Line_code, Line_width, Line_color, Line_pattern, Group_count,Line_cap),
829				  {Group_count + 1, Edge_count}
830			  end, {0, 0}, Line_groups),
831
832		    case File_type_g of
833			file_svg ->  io:put_chars(F,"</g>\n");
834			_ -> ok
835		    end;
836
837		%% End Draw Edge
838
839		false -> ok
840	    end,
841
842
843	    %%
844	    %% Footer
845	    %%
846
847	    Write_footer(F),
848
849	    ok = file:close(F),
850	    wings_pb:update(1.0, ?__(6,"done") ),
851	    wings_pb:done(),
852	    io:format(" "++ ?__(7,"done in ~.1f sec")++"~n", [timer:now_diff(os:timestamp(), Start_time) / 1.0e6])
853
854	    %% since case Style_atalog
855    end.
856
857
858
859%%
860%%  Make Edges
861%%
862
863edge_sets(Hard_edges, Faces, Edge_mode, Incr) when Edge_mode =:= all_edges ->
864    {gb_sets:from_list(incr(Hard_edges, Incr)),
865     gb_sets:from_list(edges(incr(Faces, Incr)))};
866edge_sets(Hard_edges, _Faces, Edge_mode, Incr) when Edge_mode =:= hard_edges ->
867    {gb_sets:from_list(incr(Hard_edges, Incr)), gb_sets:empty()};
868edge_sets(_Hard_edges, _Faces, _Edge_mode, _Incr) ->
869    {gb_sets:empty(), gb_sets:empty()}.
870
871
872incr({A, B, C}, Incr) -> {A + Incr, B + Incr, C + Incr};
873incr({A, B}, Incr) -> {A + Incr, B + Incr};
874incr(#e3d_face{vs = VIs, mat = Mat}, Incr) ->
875    #e3d_face{vs = incr(VIs, Incr), mat = Mat};
876incr([], _Incr)  -> [];
877incr([A | T], Incr) -> [incr(A, Incr) | incr(T, Incr)];
878incr(A, Incr) -> A + Incr.
879
880%%
881%%  [9, 2, 5, 3] -> [{9, 2}, {2, 5}, {5, 3}, {3, 9}]
882%%
883pair(L) when is_list(L) -> pair(hd(L), L).
884%pair(_, []) -> [];
885pair(H, [E]) -> [{E, H}];
886pair(H, [E | T]) -> [{E, hd(T)} | pair(H, T)].
887
888normalize({P, Q}) when P > Q -> {Q, P};
889normalize({P, Q})            -> {P, Q}.
890
891%%
892%%  [9, 2, 5, 3] -> [{2, 9}, {2, 5}, {3, 5}, {3, 9}]
893%%
894npair(L) when is_list(L) -> npair(hd(L), L).
895%npair(_, []) -> [];
896npair(H, [E]) -> [normalize({E, H})];
897npair(H, [E | T]) -> [normalize({E, hd(T)}) | npair(H, T)].
898
899cull([], _Frustum, Sum) -> Sum;
900cull([FVC | T], Frustum, Sum) ->
901    cull(T, Frustum, outcode(FVC, Frustum) band Sum).
902
903cull([FVC | T], Frustum) ->
904    cull(T, Frustum, outcode(FVC, Frustum)) =:= 0;
905cull({LVC1, LVC2}, Frustum) ->
906    outcode(LVC1, Frustum) band outcode(LVC2, Frustum) =:= 0;
907cull({TVC1, TVC2, TVC3}, Frustum) ->
908    outcode(TVC1, Frustum)
909        band outcode(TVC2, Frustum)
910        band outcode(TVC3, Frustum) =:= 0.
911
912flip({N, D}) -> {neg(N), -D}.
913
914is_open(#e3d_mesh{fs = Fs}) ->
915    EFs_dict = foldl(fun(#e3d_face{vs = FVIs}, ME_acc) ->
916			     foldl(fun(EVIt, FE_acc) ->
917					   case dict:find(EVIt, FE_acc) of
918					       {ok, Count} ->
919						   dict:store(EVIt, Count + 1, FE_acc)
920						       ;
921					       error ->
922						   dict:store(EVIt, 1, FE_acc)
923					   end
924				   end, ME_acc, npair(FVIs))
925		     end, dict:new(), Fs),
926    [] /= [C || {_, C} <- dict:to_list(EFs_dict), C < 2].
927
928has_transp_faces(#e3d_mesh{fs = Fs}, Mats_dict) ->
929    has_transp_faces(Fs, Mats_dict);
930has_transp_faces([], _) -> false;
931has_transp_faces([#e3d_face{mat = Mat} | T], Mats_dict) ->
932    case hd(dict__fetch(hd(Mat), Mats_dict)) of
933        {true, _RGBA} ->
934            true
935		;
936        _ ->
937            has_transp_faces(T, Mats_dict)
938    end.
939
940vct({TVI1, TVI2, TVI3}, VC_tree) ->
941    {ctree__get(TVI1, VC_tree), ctree__get(TVI2, VC_tree), ctree__get(TVI3, VC_tree)};
942vct({EVI1, EVI2}, VC_tree) -> {ctree__get(EVI1, VC_tree), ctree__get(EVI2, VC_tree)}.
943
944face_zmax([] = _FVCs, Zmax) -> Zmax;
945face_zmax([{_, _, Z} | T] = _FVCs, Zmax) when Z > Zmax -> face_zmax(T, Z);
946face_zmax([_VC | T] = _FVCs, Zmax) -> face_zmax(T, Zmax).
947
948face_zmax([{_, _, Z} | T]) -> face_zmax(T, Z).
949
950
951face_zlim([] = _FVCs, Zlim) -> Zlim;
952face_zlim([{_, _, Z} | T] = _FVCs, {Zmin, Zmax} = _Zlim) when Z > Zmax ->
953    face_zlim(T, {Zmin, Z});
954face_zlim([{_, _, Z} | T] = _FVCs, {Zmin, Zmax} = _Zlim) when Z < Zmin ->
955    face_zlim(T, {Z, Zmax});
956face_zlim([_VC | T] = _FVCs, Zlim) -> face_zlim(T, Zlim).
957
958face_zlim([{_, _, Z} | T]) -> face_zlim(T, {Z, Z}).
959
960
961face_type(_Is_FF, Is_SF) when Is_SF -> side;
962face_type(Is_FF, _Is_SF) when Is_FF -> front;
963face_type(_Is_FF, _Is_SF) -> back.
964
965edges(#e3d_face{vs = FVIs}) -> npair(FVIs);
966edges([]) -> [];
967edges([F | T]) -> edges(F) ++ edges(T).
968
969add_edges(FVIs, FVCs, Frustum, FN, Is_FF, Is_SF, Mat, Edge_dict0) ->
970    foldl(fun({EVIt, EVCt}, Edge_dict_acc0) ->
971		  case cull(EVCt, Frustum) of
972		      true ->
973			  Face_type = face_type(Is_FF, Is_SF),
974			  dict:append(EVIt, {Face_type, FN, Mat}, Edge_dict_acc0)
975			      ;
976		      false ->
977			  Edge_dict_acc0
978		  end
979	  end, Edge_dict0, lists:zip(npair(FVIs), npair(FVCs))).
980
981
982add_poly(FVIs0, FP, Mat, Frustum, Frustum_planes, VC_tree0, Face_tree0) ->
983    FVCs = ctree__get(FVIs0, VC_tree0),
984    case inside(FVCs, Frustum) of
985        true ->
986            Face_tree = tree__insert({FVIs0, FP, Mat}, Face_tree0),
987            {VC_tree0, Face_tree}
988		;
989        false ->
990            case splitpoly__clip(Frustum_planes, FVIs0, VC_tree0) of
991                {FVIs, VC_tree} ->
992                    Face_tree = tree__insert({FVIs, FP, Mat}, Face_tree0),
993                    {VC_tree, Face_tree}
994			;
995                _ ->
996                    {VC_tree0, Face_tree0}
997            end
998    end.
999
1000add_face(FVIs, FVCs, {FN, _FD} = FP, Mat, Is_open, Frustum, Frustum_planes,
1001	 Front_face, Side_face,
1002	 VC_tree0, Edges_dict0, Face_tree0) ->
1003    Is_FF = Front_face(FP),
1004    case Is_open of
1005        true ->
1006            Is_SF = Side_face(FP),
1007            Edges_dict = add_edges(FVIs, FVCs, Frustum, FN,
1008				   Is_FF, Is_SF, Mat, Edges_dict0),
1009            {VC_tree, Face_tree} = if
1010				       Is_SF ->
1011					   {VC_tree0, Face_tree0}
1012					       ;
1013				       true ->
1014					   case Is_FF of
1015					       true ->
1016						   add_poly(FVIs, FP, Mat, Frustum, Frustum_planes,
1017							    VC_tree0, Face_tree0)
1018						       ;
1019					       false ->
1020						   add_poly(lists:reverse(FVIs), flip(FP), Mat,
1021							    Frustum, Frustum_planes, VC_tree0, Face_tree0)
1022					   end
1023				   end,
1024            {VC_tree, Edges_dict, Face_tree}
1025		;
1026        false ->
1027            case Is_FF of
1028                true ->
1029                    Edges_dict = add_edges(FVIs, FVCs, Frustum, FN,
1030					   Is_FF, false, Mat, Edges_dict0),
1031                    {VC_tree, Face_tree} = add_poly(FVIs, FP, Mat, Frustum,
1032						    Frustum_planes, VC_tree0, Face_tree0),
1033                    {VC_tree, Edges_dict, Face_tree}
1034			;
1035                false ->
1036                    {VC_tree0, Edges_dict0, Face_tree0}
1037            end
1038    end.
1039
1040add_trimesh(TMFs, Is_open, Frustum, Frustum_planes, Front_face, Side_face,
1041	    VC_tree0, Edges_dict0, Face_tree0) ->
1042    foldl(fun(#e3d_face{vs = TVIs, mat = Mat},
1043	      {VC_tree_acc0, Edge_dict_acc0, Face_tree_acc0}) ->
1044		  TVCs = ctree__get(TVIs, VC_tree0),
1045		  case cull(TVCs, Frustum) of
1046		      true ->
1047			  TP = plane(list_to_tuple(TVCs)),
1048			  add_face(TVIs, TVCs, TP, Mat, Is_open,
1049				   Frustum, Frustum_planes, Front_face, Side_face,
1050				   VC_tree_acc0, Edge_dict_acc0, Face_tree_acc0)
1051			      ;
1052		      false ->
1053			  {VC_tree_acc0, Edge_dict_acc0, Face_tree_acc0}
1054		  end
1055	  end, {VC_tree0, Edges_dict0, Face_tree0}, TMFs).
1056
1057add_mesh(MFs, VCs, _Eps, % need VCs for e3d_mesh:triangulate_face
1058	 Is_open, Frustum, Frustum_planes, Front_face, Side_face,
1059	 VC_tree0, Edges_dict0, Face_tree0) ->
1060
1061    DP = get_pref(division_priority, ?DEF_DIVISION_PRIORITY),
1062    DP_factor =  case DP of scene -> 1; path -> -1; _ -> 1 end,
1063
1064    Z_dist_offset =  get_pref(z_dist_offset, ?DEF_Z_DIST_OFFSET),
1065
1066    foldl(fun(#e3d_face{vs=FVIs, mat=Mat}=Face,
1067	      {VC_tree_acc0, Edge_dict_acc0, Face_tree_acc0}) ->
1068
1069		  FVCs = ctree__get(FVIs, VC_tree0),
1070		  case cull(FVCs, Frustum) of
1071		      true ->
1072
1073			  %% need a nice looking triangle (slow):
1074			  TFs = e3d_mesh:triangulate_face(Face, VCs),
1075
1076			  #e3d_face{vs=T0VIs} = hd(TFs),
1077
1078			  T0VCs = ctree__get(T0VIs, VC_tree0),
1079
1080			  FP = plane(list_to_tuple(T0VCs)),
1081			  FV2Cs = ctree__get2d(FVIs, VC_tree0),
1082			  case length(FVIs) =:= 3 orelse
1083			      is_convex(FV2Cs, Front_face(FP)) andalso %% Front_face shouldn't be called from here
1084			      is_flat(FP, FVCs) of
1085			      true ->
1086				  {FN, FD} = FP ,{_X,Y,_Z}= FN,
1087				  Z_offset = if  Y< 0.1;Y < -0.1 -> -Z_dist_offset*0.028;true-> Z_dist_offset end, % same  position by eye level then bug?
1088
1089				  FP0 ={ FN, FD-FD*0.001*(Z_offset*DP_factor) }, % Overlap faces offset : Overlap factor of Front and Back polygon fighting (push -  2   + pull  )
1090				  add_face(FVIs, FVCs, FP0, Mat, Is_open,
1091					   Frustum, Frustum_planes, Front_face, Side_face,
1092					   VC_tree_acc0, Edge_dict_acc0, Face_tree_acc0)
1093				      ;
1094			      false ->
1095				  lists:foldl(fun(#e3d_face{vs=TVIs},
1096						  {VC_tree_acc1, Edge_dict_acc1, Face_tree_acc1} ) ->
1097						      %%  'Edge_dict_acc0' shadowed in 'fun' if this to fix ,then  triangle polygon not display(For example: Torus knot)
1098						      {FN, FD} = FP ,
1099						      FP0 ={ FN, FD+FD*0.001*(Z_dist_offset*DP_factor) }, %  Overlap faces offset : experiment  mul(FN, 0.001*Z_dist_offset)
1100						      add_face(TVIs, ctree__get(TVIs, VC_tree0), %%%%% ctree_get in args
1101							       FP0, Mat, Is_open,
1102							       Frustum, Frustum_planes, Front_face, Side_face,
1103							       VC_tree_acc1, Edge_dict_acc1, Face_tree_acc1)
1104					      end,
1105					      {VC_tree_acc0, Edge_dict_acc0, Face_tree_acc0}, TFs)
1106			  end
1107
1108			      ;
1109		      false ->
1110			  {VC_tree_acc0, Edge_dict_acc0, Face_tree_acc0}
1111		  end
1112	  end, {VC_tree0, Edges_dict0, Face_tree0}, MFs).
1113
1114
1115
1116materials(Mats) when is_list(Mats) ->
1117    materials(Mats, []).
1118
1119
1120materials([], Mats_dict) -> Mats_dict;
1121materials([{Name, Props} | T], Mats_dict) ->
1122    OpenGL = proplists:get_value(opengl, Props),
1123    Is_transparent = foldl(fun(_, true) -> true;
1124			      ({emission,_}, _) -> false;
1125			      ({ambient,_}, _) -> false;
1126			      ({_,{_,_,_,1.0}}, _) -> false;
1127			      ({_,{_,_,_,_}}, _) -> true;
1128			      (_, _) -> false
1129			   end, false, OpenGL),
1130    Color = proplists:get_value(diffuse, OpenGL),
1131    materials(T, dict__store(Name, {Is_transparent, Color}, Mats_dict)).
1132
1133
1134is_visible(AFs) when length(AFs) < 2 -> true;
1135is_visible([{FT1, _TN1, _Mat1}, {FT2, _TN2, _Mat2}])
1136  when FT1 =:= side, FT2 =:= side -> false;
1137is_visible([{FT1, _TN1, _Mat1}, {FT2, _TN2, _Mat2}])
1138  when FT1 =:= side, FT2 =:= back -> false;
1139is_visible([{FT1, _TN1, _Mat1}, {FT2, _TN2, _Mat2}])
1140  when FT1 =:= back, FT2 =:= side -> false;
1141is_visible(_AFs) -> true.
1142
1143is_outline(AFs) when length(AFs) < 2 -> true;
1144is_outline([{FT1, _TN1, _Mat1}, {FT2, _TN2, _Mat2}])
1145  when (FT1 =:= front) xor (FT2 =:= front) -> true;
1146is_outline(_AFs) -> false.
1147
1148is_crease(_EVIt, AFs, _ThreshCosA) when length(AFs) < 2 -> true;
1149is_crease(_EVIt, [{_FT1, TN1, _Mat1}, {_FT2, TN2, _Mat2}], ThreshCosA) ->
1150    dot(TN1, TN2) =< ThreshCosA;
1151is_crease(_, _, _) -> false.
1152
1153is_material(AFs) when length(AFs) < 2 -> true;
1154is_material([{_FT1, _TN1, Mat1}, {_FT2, _TN2, Mat2}])
1155  when Mat1 /= Mat2 -> true;
1156is_material(_) -> false.
1157
1158
1159%% Mat1 == Mat2  is edge, Mat /= Mat2 not edge
1160%% EVIt ga naito dame
1161is_material_transp(_EVIt,AFs,_ThreshCosA, _Mats_dict) when length(AFs) < 2 -> true;
1162is_material_transp(_EVIt,[{FT1, TN1, Mat1}, {FT2, TN2, Mat2}] ,ThreshCosA, Mats_dict)
1163  when (FT1 =:= back) and (FT2 =:= back);  Mat1 /= Mat2  ->
1164    Trans = is_transparent(Mat1,Mats_dict),
1165    %% io:format("MT ~p ~n",[Trans]),
1166    %% io:format("D  ~p . ~p ~p ~p . ~p ~p ~p ~n", [EVIt,   FT1, TN1, Mat1, FT2, TN2, Mat2 ] ),
1167    case (dot(TN1, TN2) =< ThreshCosA) of
1168	true ->  Trans; false -> false end;
1169is_material_transp(_,_,_,_) -> false.
1170
1171
1172%%
1173%% From src/wings_material.erl
1174%%
1175is_transparent(Mat_name ,Mats_dict) ->
1176    Data =  case  hd(dict__fetch( hd(Mat_name), Mats_dict))  of
1177		{false, _RGBA} ->
1178		    false
1179			;
1180		_ ->
1181		    true
1182	    end,
1183    Data.
1184
1185
1186
1187%% Non hard edge draw (for crease  material outline )
1188
1189edge_type_group(EVIt, AFs, ThreshCosA, HardEdge_set, Edge_set,Mats_dict) ->
1190    case is_visible(AFs) of
1191        true ->
1192            case is_outline(AFs) of
1193                true ->
1194                    outline
1195			;
1196                false ->
1197		    case gb_sets:is_member(EVIt, HardEdge_set) of
1198			true ->
1199			    hard
1200				;
1201			false ->
1202			    case is_material_transp(EVIt,AFs, ThreshCosA, Mats_dict) of
1203				true ->
1204				    transp
1205					;
1206				false ->
1207				    case is_crease(EVIt, AFs, ThreshCosA) of
1208					true ->
1209					    crease
1210						;
1211					false ->
1212					    case is_material(AFs) of
1213						true ->
1214						    material
1215							;
1216						false ->
1217						    case gb_sets:is_member(EVIt, Edge_set) of
1218							true ->
1219							    regular
1220								;
1221							false ->
1222							    none
1223						    end
1224					    end
1225				    end
1226			    end
1227		    end
1228            end
1229		;
1230        false ->
1231            none
1232    end.
1233
1234%% Regular = All edge draw by same style
1235
1236edge_type_copy(EVIt, AFs, ThreshCosA, HardEdge_set, Edge_set,Mats_dict) ->
1237    case is_visible(AFs)
1238        andalso (is_outline(AFs)
1239		 orelse is_material(AFs)
1240		 orelse is_crease(EVIt, AFs, ThreshCosA)
1241		 orelse is_material_transp(EVIt,AFs,ThreshCosA,Mats_dict)
1242		 orelse gb_sets:is_member(EVIt, HardEdge_set)
1243		 orelse gb_sets:is_member(EVIt, Edge_set))
1244    of
1245        true ->
1246            regular
1247		;
1248        false ->
1249            none
1250    end.
1251
1252group_edges(ThreshCosA, HardEdge_set, Edge_set, Edge_type_fun,
1253	    Edge_dict, Edges_dict_acc0,Mats_dict) ->
1254    dict:fold(fun(EVIt, AFs, Es_dict_acc0) ->
1255		      case Edge_type_fun(EVIt, AFs, ThreshCosA, HardEdge_set, Edge_set,Mats_dict) of
1256			  none ->
1257			      Es_dict_acc0
1258				  ;
1259			  Edge_type ->
1260			      dict__append(Edge_type, EVIt, Es_dict_acc0)
1261		      end
1262	      end, Edges_dict_acc0, Edge_dict).
1263
1264%%
1265%% Utilities
1266%%
1267
1268
1269get_min(A, B) when A < B -> A;
1270get_min(_A, B) -> B.
1271
1272get_max(A, B) when A > B -> A;
1273get_max(_A, B) -> B.
1274
1275add({X1, Y1, Z1}, {X2, Y2, Z2}) -> {X1 + X2, Y1 + Y2, Z1 + Z2};
1276add({X1, Y1}, {X2, Y2}) -> {X1 + X2, Y1 + Y2}.
1277
1278sub({X1, Y1, Z1}, {X2, Y2, Z2}) -> {X1 - X2, Y1 - Y2, Z1 - Z2};
1279sub({X1, Y1}, {X2, Y2}) -> {X1 - X2, Y1 - Y2}.
1280
1281neg({X, Y, Z}) -> {-X, -Y, -Z};
1282neg({X, Y}) -> {-X, -Y}.
1283
1284mul({X, Y, Z}, S) -> {X * S, Y * S, Z * S};
1285mul({X, Y}, S) -> {X * S, Y * S}.
1286
1287%divide({X, Y, Z}, S) -> {X / S, Y / S, Z / S};
1288divide({X, Y}, S) -> {X / S, Y / S}.
1289
1290cross({X1, Y1, Z1}, {X2, Y2, Z2}) ->
1291    {Y1 * Z2 - Y2 * Z1, X2 * Z1 - X1 * Z2, X1 * Y2 - X2 * Y1};
1292cross({X1, Y1}, {X2, Y2}) -> X1 * Y2 - X2 * Y1.
1293
1294dot({X1, Y1, Z1}, {X2, Y2, Z2}) -> X1 * X2 + Y1 * Y2 + Z1 * Z2;
1295dot({X1, Y1}, {X2, Y2}) -> X1 * X2 + Y1 * Y2.
1296
1297area(V1, V2, V3) -> cross(sub(V2, V1), sub(V3, V1)).
1298
1299delta({X1, Y1, Z1} = LVC1, {X2, Y2, Z2}) ->
1300    {LVC1, {X2 - X1, Y2 - Y1, Z2 - Z1}};
1301delta({X1, Y1} = LVC1, {X2, Y2}) -> {LVC1, {X2 - X1, Y2 - Y1}}.
1302
1303delta({{X1, Y1, Z1} = LVC1, {X2, Y2, Z2}}) ->
1304    {LVC1, {X2 - X1, Y2 - Y1, Z2 - Z1}};
1305delta({{X1, Y1} = LVC1, {X2, Y2}}) -> {LVC1, {X2 - X1, Y2 - Y1}}.
1306
1307param({LVC1, DL}, U) -> add(LVC1, mul(DL, U)).
1308
1309section({LVC1, DL}, {TN, TD}) ->
1310    case catch (TD - dot(TN, LVC1)) / dot(TN, DL) of
1311        R when is_float(R)-> R;
1312        _ -> ?BIG
1313    end.
1314
1315ip(Delta, P) -> param(Delta, section(Delta, P)).
1316
1317ip(VC1, VC2, P) -> ip(delta(VC1, VC2), P).
1318
1319plane({VC1, VC2, VC3}) ->
1320    N = e3d_vec:normal(VC1, VC2, VC3),
1321    {N, dot(N, VC1)}.
1322
1323persp_ff({_, D}) -> D < -0.001.
1324
1325ortho_ff({{_, _, Z}, _}) -> Z > 0.001.
1326
1327persp_sf({_, D}) -> abs(D) =< 0.001.
1328
1329ortho_sf({{_, _, Z}, _}) -> abs(Z) =< 0.001.
1330
1331%%
1332%% Cohen-Sutherland
1333%%
1334outcode({X, Y, Z}, {{Xmin, Ymin, Zmin}, {Xmax, Ymax, Zmax}}) ->
1335    C0 = if X < Xmin -> 1; true -> 0 end,
1336    C1 = if X > Xmax -> 2; true -> 0 end,
1337    C2 = if Y < Ymin -> 4; true -> 0 end,
1338    C3 = if Y > Ymax -> 8; true -> 0 end,
1339    C4 = if Z < Zmin -> 16; true -> 0 end,
1340    C5 = if Z > Zmax -> 32; true -> 0 end,
1341    C0 bor C1 bor C2 bor C3 bor C4 bor C5;
1342outcode({X, Y}, {{Xmin, Ymin}, {Xmax, Ymax}}) ->
1343    C0 = if X < Xmin -> 1; true -> 0 end,
1344    C1 = if X > Xmax -> 2; true -> 0 end,
1345    C2 = if Y < Ymin -> 4; true -> 0 end,
1346    C3 = if Y > Ymax -> 8; true -> 0 end,
1347    C0 bor C1 bor C2 bor C3;
1348outcode({X, Y, Z}, {HSx, HSy, Zmin, Zmax, Zf}) ->
1349    R =  Z / Zf,
1350    Rx = HSx * R,
1351    Ry = HSy * R,
1352    {C0, C1, C2, C3} = if
1353			   Z > 0.0 ->
1354			       {if X < -Rx -> 0; true -> 1 end,
1355				if X > Rx -> 0; true -> 2 end,
1356				if Y < -Ry -> 0; true -> 4 end,
1357				if Y > Ry -> 0; true -> 8 end}
1358				   ;
1359			   true ->
1360			       {if X < -Rx -> 1; true -> 0 end,
1361				if X > Rx -> 2; true -> 0 end,
1362				if Y < -Ry -> 4; true -> 0 end,
1363				if Y > Ry -> 8; true -> 0 end}
1364		       end,
1365    C4 = if Z < Zmin -> 16; true -> 0 end,
1366    C5 = if Z > Zmax -> 32; true -> 0 end,
1367    C0 bor C1 bor C2 bor C3 bor C4 bor C5.
1368
1369%% to_boundary({X, Y, Z}, {DX, DY, DZ}, {{Xmin, _, _}, _}) when X < Xmin ->
1370%%     {Xmin, Y + DY * (Xmin - X) / DX, Z + DZ * (Xmin - X) / DX};
1371%% to_boundary({X, Y, Z}, {DX, DY, DZ}, {_, {Xmax, _, _}}) when X > Xmax ->
1372%%     {Xmax, Y + DY * (Xmax - X) / DX, Z + DZ * (Xmax - X) / DX};
1373%% to_boundary({X, Y, Z}, {DX, DY, DZ}, {{_, Ymin, _}, _}) when Y < Ymin ->
1374%%     {X + DX * (Ymin - Y) / DY, Ymin, Z + DZ * (Ymin - Y) / DY};
1375%% to_boundary({X, Y, Z}, {DX, DY, DZ}, {_, {_, Ymax, _}}) when Y > Ymax ->
1376%%     {X + DX * (Ymax - Y) / DY, Ymax, Z + DZ * (Ymax - Y) / DY};
1377%% to_boundary({X, Y, Z}, {DX, DY, DZ}, {{_, _, Zmin}, _}) when Z < Zmin ->
1378%%     {X + DX * (Zmin - Z) / DZ, Y + DY * (Zmin - Z) / DZ, Zmin};
1379%% to_boundary({X, Y, Z}, {DX, DY, DZ}, {_, {_, _, Zmax}}) when Z > Zmax ->
1380%%     {X + DX * (Zmax - Z) / DZ, Y + DY * (Zmax - Z) / DZ, Zmax};
1381to_boundary({X, Y, Z}, {DX, DY, DZ}, Zlim) when is_float(Zlim) ->
1382    {X + DX * (Zlim - Z) / DZ, Y + DY * (Zlim - Z) / DZ, Zlim};
1383to_boundary({X, Y}, {DX, DY}, {{Xmin, _}, _}) when X < Xmin ->
1384    {Xmin, Y + DY * (Xmin - X) / DX};
1385to_boundary({X, Y}, {DX, DY}, {_, {Xmax, _}}) when X > Xmax ->
1386    {Xmax, Y + DY * (Xmax - X) / DX};
1387to_boundary({X, Y}, {DX, DY}, {{_, Ymin}, _}) when Y < Ymin ->
1388    {X + DX * (Ymin - Y) / DY, Ymin};
1389to_boundary({X, Y}, {DX, DY}, {_, {_, Ymax}}) when Y > Ymax ->
1390    {X + DX * (Ymax - Y) / DY, Ymax}.
1391
1392clip({LC1, LC2}, Box) ->
1393    C1 = outcode(LC1, Box),
1394    C2 = outcode(LC2, Box),
1395    if
1396        C1 band C2 /= 0 ->
1397            nil
1398		;
1399        C1 bor C2 /= 0 ->
1400            D = sub(LC2, LC1),
1401            case C1 /= 0 of
1402                true  ->
1403                    clip({to_boundary(LC1, D, Box), LC2}, Box)
1404			;
1405                false ->
1406                    clip({LC1, to_boundary(LC2, D, Box)}, Box)
1407            end
1408		;
1409        true ->
1410            {LC1, LC2}
1411    end.
1412
1413clip_z({LC1, LC2}, View_port) when size(View_port) =:= 2 ->
1414    {{_, _, Z1}, {_, _, Z2}} = {LC1, LC2},
1415    {{_, _, Zvp_min}, {_, _, Zvp_max}} = View_port,
1416    if
1417        Z1 >= Zvp_min, Z1 =< Zvp_max, Z2 >= Zvp_min, Z2 =< Zvp_max ->
1418            {LC1, LC2}
1419		;
1420        ((Z1 < Zvp_min) and (Z2 < Zvp_min))
1421	or ((Z1 > Zvp_max) and (Z2 > Zvp_max)) ->
1422            nil
1423		;
1424        true ->
1425            {if
1426		 Z1 < Zvp_min ->
1427		     to_boundary(LC1, sub(LC2, LC1), Zvp_min)
1428			 ;
1429		 Z1 > Zvp_max ->
1430		     to_boundary(LC1, sub(LC2, LC1), Zvp_max)
1431			 ;
1432		 true ->
1433		     LC1
1434	     end,
1435	     if
1436		 Z2 < Zvp_min ->
1437		     to_boundary(LC2, sub(LC2, LC1), Zvp_min)
1438			 ;
1439		 Z2 > Zvp_max ->
1440		     to_boundary(LC2, sub(LC2, LC1), Zvp_max)
1441			 ;
1442		 true ->
1443		     LC2
1444	     end}
1445    end.
1446
1447
1448outside_bb({{Xmin1, Ymin1}, {Xmax1, Ymax1}},
1449	   {{Xmin2, Ymin2}, {Xmax2, Ymax2}}) ->
1450    Xmax1 =< Xmin2 orelse Xmax2 =< Xmin1
1451        orelse Ymax1 =< Ymin2 orelse Ymax2 =< Ymin1.
1452
1453
1454%%% Should it be a BB test?
1455nearer({{_, _, LZ1}, {_, _, LZ2}}, FVCs) when is_list(FVCs) ->
1456    LZ_min = get_min(LZ1, LZ2),
1457    lists:all(fun({_, _, FZ} = _FVC) -> LZ_min >= FZ end, FVCs).
1458
1459
1460edge_of_poly(LVIt, FVIs) ->
1461%%% LVIt must be normalized
1462    lists:any(fun(FVIt) -> FVIt =:= LVIt end, npair(FVIs)).
1463
1464
1465outside_poly({LVC1, LVC2}, FVCs, Eps) ->
1466    lists:any(fun({EVC1, EVC2}) ->
1467		      (area(LVC1, EVC1, EVC2) < Eps) andalso (area(LVC2, EVC1, EVC2) < Eps)
1468	      end, pair(FVCs)).
1469
1470
1471inside_poly(C, FVCs, Eps) ->
1472    lists:all(fun({EVC1, EVC2}) -> area(C, EVC2, EVC1) < Eps end, pair(FVCs)).
1473
1474
1475outside_line(FVCs, {LVC1, LVC2}, Eps) ->
1476%%% No need to calculate all areas, the last three should be enough
1477%%% to determine if the poly is a tangent
1478    As = lists:map(fun(FVC) -> area(FVC, LVC1, LVC2) end, FVCs),
1479    lists:all(fun(A) -> A < Eps end, As)
1480        orelse lists:all(fun(A) -> A > -Eps end, As).
1481
1482line_segment(_LVIt, nil, _Fs, _VC_tree,
1483	     _Proj, _Is_ortho, _Eps) -> [];
1484line_segment(LVIt, LVCt, Fs, VC_tree, Proj, Is_ortho, Eps) ->
1485    line_segment(LVIt, LVCt, Proj(LVCt), Fs, VC_tree,
1486		 Proj, Is_ortho, Eps).
1487
1488line_segment(_, _, LV2Ct, [], _, _, _, _) -> [LV2Ct];
1489line_segment(LVIt, {LVC1, LVC2} = LVCt, {LV2C1, LV2C2} = LV2Ct,
1490	     [{{FVIs, FP}, FBB} | T], VC_tree, Proj, Is_ortho, Eps) ->
1491    FVCs = ctree__get(FVIs, VC_tree),
1492    FV2Cs = ctree__get2d(FVIs, VC_tree),
1493    case
1494
1495        outside_bb(bbox(LV2Ct), FBB)
1496	orelse edge_of_poly(LVIt, FVCs)
1497	orelse nearer(LVCt, FVCs)
1498    of
1499        true  ->
1500            line_segment(LVIt, LVCt, LV2Ct, T, VC_tree,
1501			 Proj, Is_ortho, Eps)
1502		;
1503        false ->
1504            {FN, FD} = FP,
1505            L1D = dot(LVC1, FN),
1506            L2D = dot(LVC2, FN),
1507            EpsD = ?EPS1 * abs(FD),
1508            FDm = FD - EpsD,
1509            case
1510                (L1D >= FDm) andalso (L2D >= FDm)
1511		orelse outside_poly(LV2Ct, FV2Cs, Eps)
1512		orelse outside_line(FV2Cs, LV2Ct, Eps)
1513            of
1514                true  ->
1515                    line_segment(LVIt, LVCt, LV2Ct, T, VC_tree,
1516				 Proj, Is_ortho, Eps)
1517			;
1518                false ->
1519                    FDp = FD + EpsD,
1520                    L1_behind = L1D =< FDp,
1521                    L2_behind = L2D =< FDp,
1522                    if
1523                        L1_behind; L2_behind ->
1524                            L1_in = inside_poly(LV2C1, FV2Cs, Eps),
1525                            L2_in = inside_poly(LV2C2, FV2Cs, Eps),
1526                            if
1527                                L1_behind, L2_behind, L1_in, L2_in ->
1528                                    []
1529					;
1530                                true ->
1531                                    case
1532                                        divide_segment(LVCt, LV2Ct,
1533						       FV2Cs, FP, FDp,
1534						       L1_behind, L2_behind,
1535						       L1_in, L2_in, Is_ortho)
1536                                    of
1537                                        {LA, LA} ->
1538                                            line_segment(LVIt, LA, T,
1539							 VC_tree,
1540							 Proj, Is_ortho, Eps)
1541						;
1542                                        {LA, LB} ->
1543                                            line_segment(LVIt, LA, T,
1544							 VC_tree,
1545							 Proj, Is_ortho, Eps)
1546                                                ++ line_segment(LVIt, LB, T,
1547								VC_tree,
1548								Proj, Is_ortho, Eps)
1549                                    end
1550                            end
1551				;
1552                        true ->
1553                            line_segment(LVIt, LVCt, LV2Ct, T,
1554					 VC_tree,
1555					 Proj, Is_ortho, Eps)
1556                    end
1557            end
1558    end.
1559
1560divide_segment({{_X1, _Y1, Z1} = LVC1, {_X2, _Y2, Z2} = LVC2} = LVCt,
1561	       LV2Ct, FV2Cs, {FN, _FD} = FP, FDp,
1562	       L1_behind, L2_behind, L1_in, L2_in, Is_ortho) when L1_in /= L2_in ->
1563    Del = delta(LVCt),
1564    Del2 = delta(LV2Ct),
1565    {_, S} = find_section(true, Del2, FV2Cs),
1566    L = param(Del, if
1567		       Is_ortho -> S;
1568		       true -> R = Z1 * S / Z2, R / (1.0 + R - S)
1569		   end),
1570    L_behind = dot(L, FN) =< FDp,
1571    if
1572        L_behind ->
1573            {if
1574		 L2_in, S > 0.0 + ?EPS2 ->
1575		     {LVC1, L}
1576			 ;
1577		 true ->
1578		     if
1579			 L1_behind -> nil;
1580			 true -> {LVC1, ip(Del, FP)}
1581		     end
1582	     end,
1583	     if
1584		 L1_in, S < 1.0 - ?EPS2 ->
1585		     {L, LVC2}
1586			 ;
1587		 true ->
1588		     if
1589			 L2_behind -> nil;
1590			 true -> {ip(Del, FP), LVC2}
1591		     end
1592	     end}
1593		;
1594        true ->
1595            {if
1596		 L2_in, S > 0.0 + ?EPS2 ->
1597		     if
1598			 L2_behind -> {LVC1, ip(Del, FP)};
1599			 true -> {LVC1, LVC2}
1600		     end
1601			 ;
1602		 true ->
1603		     nil
1604	     end,
1605	     if
1606		 L1_in, S < 1.0 - ?EPS2 ->
1607		     if
1608			 L1_behind -> {ip(Del, FP), LVC2};
1609			 true -> {LVC1, LVC2}
1610		     end
1611			 ;
1612		 true ->
1613		     nil
1614	     end}
1615    end;
1616divide_segment({LVC1, LVC2} = LVCt, _LV2Ct,
1617	       _FV2Cs, FP, _FDp,
1618	       L1_behind, L2_behind, L1_in, L2_in, _Is_ortho) when L1_in, L2_in ->
1619    Del = delta(LVCt),
1620    {if
1621	 L1_behind -> nil;
1622	 true -> {LVC1, ip(Del, FP)}
1623     end,
1624     if
1625	 L2_behind -> nil;
1626	 true -> {ip(Del, FP), LVC2}
1627     end};
1628divide_segment({{_X1, _Y1, Z1} = LVC1, {_X2, _Y2, Z2} = LVC2} = LVCt,
1629	       LV2Ct, FV2Cs, {FN, _FD} = FP, FDp,
1630	       _L1_behind, _L2_behind, _L1_in, _L2_in, Is_ortho) ->
1631    Del = delta(LVCt),
1632    Del2 = delta(LV2Ct),
1633    {Smin, Smax} = find_section(false, Del2, FV2Cs),
1634    {Lmin, Lmax} = if
1635		       Is_ortho ->
1636			   {param(Del, Smin), param(Del, Smax)}
1637			       ;
1638		       true ->
1639			   Rz = Z1 / Z2,
1640			   Rmin = Rz * Smin,
1641			   Rmax = Rz * Smax,
1642			   {param(Del, Rmin / (1.0 + Rmin - Smin)),
1643			    param(Del, Rmax / (1.0 + Rmax - Smax))}
1644		   end,
1645    {Lmin_behind, Lmax_behind} = {dot(Lmin, FN) =< FDp, dot(Lmax, FN) =< FDp},
1646    if
1647        Lmin_behind; Lmax_behind ->
1648            {if
1649		 Lmin_behind ->
1650		     if
1651			 Smin > 0.0 + ?EPS2 -> {LVC1, Lmin};
1652			 true -> nil
1653		     end
1654			 ;
1655		 true ->
1656		     {LVC1, ip(Del, FP)}
1657	     end,
1658	     if
1659		 Lmax_behind ->
1660		     if
1661			 Smax < 1.0 - ?EPS2 -> {Lmax, LVC2};
1662			 true -> nil
1663		     end
1664			 ;
1665		 true ->
1666		     {ip(Del, FP), LVC2}
1667	     end}
1668		;
1669        true ->
1670            {LVCt, LVCt}
1671    end.
1672
1673find_section(Find_one, Del, FV2Cs) ->
1674    find_section(1.0, 0.0, Find_one, Del, pair(FV2Cs)).
1675
1676find_section(Smin, Smax, _, _, []) -> {Smin, Smax};
1677find_section(Smin, Smax, Find_one, {LVC1, DL} = Del, [{EVC1, EVC2} | T]) ->
1678    D = sub(LVC1, EVC1),
1679    DE = sub(EVC2, EVC1),
1680    A = cross(DE, DL),
1681    case catch cross(D, DL) / A of
1682        R when is_float(R), R > 0.0 - ?EPS2, R < 1.0 + ?EPS2 ->
1683            case cross(D, DE) / A of
1684                S when S > 0.0 - ?EPS2, S < 1.0 + ?EPS2 ->
1685                    if
1686                        Find_one ->
1687                            if
1688                                S > 0.0 + ?EPS2, S < 1.0 - ?EPS2 ->
1689                                    {S, S}
1690					;
1691                                true ->
1692                                    find_section(get_min(S, Smin), get_max(S, Smax),
1693						 Find_one, Del, T)
1694                            end
1695				;
1696                        true ->
1697                            find_section(get_min(S, Smin), get_max(S, Smax),
1698					 Find_one, Del, T)
1699                    end
1700			;
1701                _ ->
1702                    find_section(Smin, Smax, Find_one, Del, T)
1703            end
1704		;
1705        _ ->
1706            find_section(Smin, Smax, Find_one, Del, T)
1707    end.
1708
1709
1710
1711%%
1712%% See http://www.tulrich.com/geekstuff/partitioning.html
1713%%
1714
1715qtree__new(Box) when size(Box) =:= 2 -> qtree__new(qtree__cons(Box));
1716qtree__new(Cons) -> {[], Cons, nil, nil, nil, nil}.
1717
1718qtree__insert({_Obj, OBB, _Ozmax} = QE, Q) ->
1719    qtree__insert(QE, mid(OBB), Q, 0).
1720
1721qtree__insert(false, _QE, _QEmid, _Cons, Q, _Depth) -> Q;
1722qtree__insert(true, QE, QEmid, Cons, nil, Depth) ->
1723    qtree__insert(QE, QEmid, qtree__new(Cons), Depth + 1);
1724qtree__insert(true, QE, QEmid, _Cons, Q, Depth) ->
1725    qtree__insert(QE, QEmid, Q, Depth + 1).
1726
1727qtree__insert(QE, QEmid, {QEs, Cons, Q1, Q2, Q3, Q4}, Depth) ->
1728    C1 = qtree__cons(if Q1 =:= nil -> qtree__quad1(Cons); true -> Q1 end),
1729    C2 = qtree__cons(if Q2 =:= nil -> qtree__quad2(Cons); true -> Q2 end),
1730    C3 = qtree__cons(if Q3 =:= nil -> qtree__quad3(Cons); true -> Q3 end),
1731    C4 = qtree__cons(if Q4 =:= nil -> qtree__quad4(Cons); true -> Q4 end),
1732    F1 = qtree__fits(QE, QEmid, C1),
1733    F2 = qtree__fits(QE, QEmid, C2),
1734    F3 = qtree__fits(QE, QEmid, C3),
1735    F4 = qtree__fits(QE, QEmid, C4),
1736						%io:format("~n"),
1737						%io:format("QE=~w~n", [QE]),
1738						%io:format("QEmid=~w~n", [QEmid]),
1739						%io:format("F1=~w F2=~w F3=~w F4=~w~n", [F1, F2, F3, F4]),
1740						%io:format("C1=~w C2=~w C3=~w C4=~w~n", [C1, C2, C3, C4]),
1741    if
1742        Depth > ?MAXDEPTH - 1; not (F1 or F2 or F3 or F4) ->
1743            {[QE | QEs], Cons, Q1, Q2, Q3, Q4}
1744		;
1745        true ->
1746            N1 = qtree__insert(F1, QE, QEmid, C1, Q1, Depth),
1747            N2 = qtree__insert(F2, QE, QEmid, C2, Q2, Depth),
1748            N3 = qtree__insert(F3, QE, QEmid, C3, Q3, Depth),
1749            N4 = qtree__insert(F4, QE, QEmid, C4, Q4, Depth),
1750            {QEs, Cons, N1, N2, N3, N4}
1751    end.
1752
1753qtree__cons({{Xmin, Ymin}, {Xmax, Ymax}}) ->
1754    Dx = (Xmax - Xmin) / 2.0,
1755    Dy = (Ymax - Ymin) / 2.0,
1756    {{{Xmin, Ymin}, {Xmax, Ymax}},
1757     {{Xmin - Dx, Ymin - Dy}, {Xmax + Dx, Ymax + Dy}},
1758     {Xmin + Dx, Ymin + Dy}};
1759qtree__cons({_QEs, Cons, _Q1, _Q2, _Q3, _Q4}) -> Cons.
1760
1761qtree__quad1({{_, {Xmax, Ymax}}, _, {Xmid, Ymid}}) ->
1762    {{Xmid, Ymid}, {Xmax, Ymax}}.
1763qtree__quad2({{{Xmin, _}, {_, Ymax}}, _, {Xmid, Ymid}}) ->
1764    {{Xmin, Ymid}, {Xmid, Ymax}}.
1765qtree__quad3({{{Xmin, Ymin}, _}, _, {Xmid, Ymid}}) ->
1766    {{Xmin, Ymin}, {Xmid, Ymid}}.
1767qtree__quad4({{{_, Ymin}, {Xmax, _}}, _, {Xmid, Ymid}}) ->
1768    {{Xmid, Ymin}, {Xmax, Ymid}}.
1769
1770qtree__fits({_Obj, {OBBmin, OBBmax}, _Ozmax}, QEmid, {CBB, Clim, _Cmid}) ->
1771    inside(OBBmin, Clim) andalso inside(OBBmax, Clim)
1772        andalso inside(QEmid, CBB).
1773
1774qtree__get_quad_objs(_LBB, []) -> [];
1775qtree__get_quad_objs(LBB, [{_Obj, OBB, _Ozmax} = QE | T]) ->
1776    case outside_bb(LBB, OBB) of
1777        true ->
1778            qtree__get_quad_objs(LBB, T)
1779		;
1780        false ->
1781            [QE | qtree__get_quad_objs(LBB, T)]
1782    end.
1783
1784qtree__do_get_objs(_LBB,  nil) -> [];
1785qtree__do_get_objs(LBB, {QEs, {_CBB, Clim, _Cmid}, Q1, Q2, Q3, Q4}) ->
1786    case outside_bb(LBB, Clim) of
1787        true ->
1788            []
1789		;
1790        false ->
1791            qtree__get_quad_objs(LBB, QEs)
1792                ++ qtree__do_get_objs(LBB, Q1)
1793                ++ qtree__do_get_objs(LBB, Q2)
1794                ++ qtree__do_get_objs(LBB, Q3)
1795                ++ qtree__do_get_objs(LBB, Q4)
1796    end.
1797
1798qtree__get_objs(LS, Q) ->
1799    [{Obj, OBB} || {Obj, OBB, _} <- keysort(3, qtree__do_get_objs(LS, Q))].
1800
1801inside([], _Frustum, Flag) -> Flag;
1802inside([FVC | T], Frustum, true) ->
1803    inside(T, Frustum, inside(FVC, Frustum));
1804inside(_FVCs, _Frustum, Flag) -> Flag.
1805
1806inside([FVC | T], Frustum) ->
1807    inside(T, Frustum, inside(FVC, Frustum));
1808inside({X, Y}, {{Xmin, Ymin}, {Xmax, Ymax}}) ->
1809    (X >= Xmin) andalso (Y >= Ymin) andalso (X =< Xmax) andalso (Y =< Ymax);
1810inside({X, Y, Z}, {{Xmin, Ymin, Zmin}, {Xmax, Ymax, Zmax}}) ->
1811    (X >= Xmin) andalso (Y >= Ymin) andalso (Z >= Zmin)
1812        andalso (X =< Xmax) andalso (Y =< Ymax) andalso (Z =< Zmax);
1813inside({X, Y, Z}, {HSx, HSy, Zmin, Zmax, Zf}) ->
1814    R =  Z / Zf,
1815    Rx = HSx * R,
1816    Ry = HSy * R,
1817    (if
1818	 Z >= 0.0 ->
1819	     (X >= Rx) andalso (Y >= Ry) andalso (X =< -Rx) andalso (Y =< -Ry)
1820		 ;
1821	 true ->
1822	     (X >= -Rx) andalso (Y >= -Ry) andalso (X =< Rx) andalso (Y =< Ry)
1823     end) andalso (Z >= Zmin) andalso (Z =< Zmax).
1824
1825mid({{Xmin, Ymin}, {Xmax, Ymax}}) -> {(Xmin + Xmax) / 2.0, (Ymin + Ymax) / 2.0}.
1826
1827bbox_size({{Xmin, Ymin}, {Xmax, Ymax}}) -> {Xmax - Xmin, Ymax - Ymin}.
1828
1829bbox([VC1, VC2]) -> bbox({VC1, VC2});
1830bbox([VC1, VC2 | T]) -> bbox(T, bbox({VC1, VC2}));
1831bbox({{X1, Y1}, {X2, Y2}}) ->
1832    Xmin = get_min(X1, X2),
1833    Ymin = get_min(Y1, Y2),
1834    Xmax = get_max(X1, X2),
1835    Ymax = get_max(Y1, Y2),
1836    {{Xmin, Ymin}, {Xmax, Ymax}}.
1837
1838bbox([VC], BB) -> bbox(VC, BB);
1839bbox([VC | T], BB) -> bbox(T, bbox(VC, BB));
1840bbox({{Xmin_1, Ymin_1}, {Xmax_1, Ymax_1}},
1841     {{Xmin_2, Ymin_2}, {Xmax_2, Ymax_2}}) ->
1842    Xmin = get_min(Xmin_1, Xmin_2),
1843    Ymin = get_min(Ymin_1, Ymin_2),
1844    Xmax = get_max(Xmax_1, Xmax_2),
1845    Ymax = get_max(Ymax_1, Ymax_2),
1846    {{Xmin, Ymin}, {Xmax, Ymax}};
1847bbox({X, Y}, {{Xmin0, Ymin0}, {Xmax0, Ymax0}}) ->
1848    Xmin = get_min(X, Xmin0),
1849    Ymin = get_min(Y, Ymin0),
1850    Xmax = get_max(X, Xmax0),
1851    Ymax = get_max(Y, Ymax0),
1852    {{Xmin, Ymin}, {Xmax, Ymax}}.
1853
1854bbox_2d({{Xmin, Ymin, _}, {Xmax, Ymax, _}}) -> {{Xmin, Ymin}, {Xmax, Ymax}}.
1855
1856
1857translate(_, _, _, []) -> [];
1858translate(BB, Offset, Scale, [LVCt0 | T]) ->
1859    case clip(LVCt0, BB) of
1860        nil -> translate(BB, Offset, Scale, T);
1861        {LVC1, LVC2} ->
1862            [{mul(add(LVC1, Offset), Scale), mul(add(LVC2, Offset), Scale)}
1863	     | translate(BB, Offset, Scale, T)]
1864    end.
1865
1866
1867transvc(_, _, _, []) -> [];
1868transvc(BB, Offset, Scale, [VCt | T]) ->
1869    [mul(add(VCt, Offset), Scale) | transvc(BB, Offset, Scale, T)].
1870
1871%% A function for debug
1872%% project(_, []) -> [];
1873%% project(Proj, [C | T]) -> [Proj(C) | project(Proj, T)].
1874
1875
1876merge(LS1, _BB1, [], _Dthr) -> [LS1];
1877merge(LS1, {{Xmin_1, Ymin_1}, {Xmax_1, Ymax_1}} = BB1, [LS2 | T], Dthr) ->
1878    {{Xmin_2, Ymin_2}, {Xmax_2, Ymax_2}} = BB2 = bbox(LS2),
1879    {{Xmin, Ymin}, {Xmax, Ymax}} = BB = bbox(BB1, BB2),
1880    case
1881        (get_max(Xmin_1, Xmin_2) =< get_min(Xmax_1, Xmax_2) + Dthr)
1882	andalso (get_max(Ymin_1, Ymin_2) =< get_min(Ymax_1, Ymax_2) + Dthr)
1883    of
1884        true ->
1885            {LS11, _LS12} = LS1,
1886            LS = if
1887		     LS11 =:= {Xmin_1, Ymin_1}; LS11 =:= {Xmax_1, Ymax_1} ->
1888			 {{Xmin, Ymin}, {Xmax, Ymax}}
1889			     ;
1890		     true ->
1891			 {{Xmin, Ymax}, {Xmax, Ymin}}
1892		 end,
1893            merge(LS, BB, T, Dthr)
1894		;
1895        false ->
1896            [LS2 | merge(LS1, BB1, T, Dthr)]
1897    end.
1898
1899merge(LS, [], _Dthr) -> [LS];
1900merge(LS, LSs, Dthr) -> merge(LS, bbox(LS), LSs, Dthr).
1901
1902lstree_empty() -> nil.
1903
1904lstree_insert(LS, LS_tree, Athr, Dthr) ->
1905    lstree_insert(unit_2d(LS, Dthr), LS, LS_tree, Athr, Dthr).
1906
1907lstree_insert(nil, _LS0, LS_tree, _Athr, _Dthr) -> LS_tree;
1908lstree_insert(U0, LS0, nil, _Athr, Dthr)  ->
1909    {lstree_insert1(U0, mid(LS0), LS0, nil, Dthr), nil, nil};
1910lstree_insert(U0, LS0, {{U, _, _, _, _} = LS_tree, Small, Big}, Athr, Dthr) ->
1911    Sin = cross(U0, U),
1912    Cos = dot(U0, U),
1913    if
1914        Sin < -Athr, Cos > Athr; Sin > Athr, Cos < -Athr ->
1915            {LS_tree, lstree_insert(U0, LS0, Small, Athr, Dthr), Big}
1916		;
1917        Sin > Athr, Cos >= -Athr; Sin < -Athr, Cos =< Athr->
1918            {LS_tree, Small, lstree_insert(U0, LS0, Big, Athr, Dthr)}
1919		;
1920        true ->
1921            {lstree_insert1(U0, mid(LS0), LS0, LS_tree, Dthr), Small, Big}
1922    end.
1923
1924lstree_insert1(U0, Mid_LS0, LS0, nil, _Dthr)  ->
1925    {U0, Mid_LS0, [LS0], nil, nil};
1926lstree_insert1(U0, Mid_LS0, LS0, {U, Mid_LS, LSs, Small, Big}, Dthr) ->
1927    V = sub(Mid_LS0, Mid_LS),
1928    D1 = cross(U, V),
1929    D2 = cross(U0, neg(V)),
1930    D0 = get_min(abs(D1), abs(D2)),
1931    D = if D1 >= 0.0 -> D0 ; true -> -D0 end,
1932    if
1933        D < -Dthr ->
1934            {U, Mid_LS, LSs,
1935	     lstree_insert1(U0, Mid_LS0, LS0, Small, Dthr), Big}
1936		;
1937        D > Dthr ->
1938            {U, Mid_LS, LSs,
1939	     Small, lstree_insert1(U0, Mid_LS0, LS0, Big, Dthr)}
1940		;
1941        true ->
1942	    {U, Mid_LS, merge(LS0, LSs, Dthr), Small, Big}
1943    end.
1944
1945lstree_to_list(nil, List) -> List;
1946lstree_to_list({LS_tree, Small, Big}, List) ->
1947    LSs = lstree_to_list(LS_tree),
1948    lstree_to_list(Small, LSs ++ lstree_to_list(Big, List));
1949lstree_to_list({_U, _Mid_LS, LSs, Small, Big}, List) ->
1950    lstree_to_list(Small, LSs ++ lstree_to_list(Big, List)).
1951
1952lstree_to_list(LS_tree) -> lstree_to_list(LS_tree, []).
1953
1954unit_2d({{X1, Y1}, {X2, Y2}}, Dthr) ->
1955    UX0 = X2 - X1,
1956    UY0 = Y2 - Y1,
1957    if
1958        (abs(UX0) < Dthr) and (abs(UY0) < Dthr) ->
1959            nil
1960		;
1961        true ->
1962            D = math:sqrt(UX0 * UX0 + UY0 * UY0),
1963            {UX0 / D, UY0 / D}
1964    end.
1965
1966
1967get_mat_class(F,Mats_dict) ->
1968    io:put_chars(F, "<style type=\"text/css\">\n"),
1969    io:put_chars(F, "<![CDATA[\n"),
1970    foldl(fun( { _MatName, [{ _, { R,G,B,A } }] } , Cnt0 ) ->
1971		  Cnt = Cnt0 + 1,
1972		  Ts = (if A < 1.0 -> "shape-rendering:auto;";true->"" end),
1973		  Dr = trunc(R*255), Dg = trunc(G*255), Db = trunc(B*255),
1974		  FixName = "m",
1975		  io:fwrite(F, "."++"~ts~p { fill:~ts;opacity:~.2f;stroke:none;~ts }~n", [ FixName, Cnt, set_fill_hex_color(Dr,Dg, Db), A, Ts]),
1976		  Cnt
1977	  end, 0 , Mats_dict),
1978    io:put_chars(F, "]]>\n</style>\n").
1979
1980
1981
1982
1983%%
1984%%  EPS
1985%%
1986
1987write_eps_header(F, {Wbb, Hbb}, Line_cap,_Mats_dict,_LSpos) ->
1988    io:put_chars(F, "%!PS-Adobe-2.0 EPSF-2.0\n"),
1989    io:fwrite(F, "%%BoundingBox: 0 0 ~w ~w~n", [round(Wbb), round(Hbb)]),
1990    io:put_chars(F, "/s {newpath moveto lineto stroke} bind def\n"),
1991
1992    io:put_chars(F, "/m {gsave newpath moveto} bind def\n"),
1993    io:put_chars(F, "/l {lineto } bind def\n"),
1994    io:put_chars(F, "/f {setrgbcolor fill grestore} bind def\n" ),
1995    case Line_cap of
1996        0 -> ok;
1997        _ -> io:fwrite(F, "~w setlinecap~n", [Line_cap])
1998    end.
1999
2000write_eps_line_group(_F, _BB_size, [], _Line_code, _Line_width, _Line_color, _Line_pattern, _Group_count,_Line_cap) -> ok;
2001write_eps_line_group(F, _BB_size, Ls, _Line_code, Line_width, Line_color, Line_pattern, Group_count,_Line_cap)
2002  when Group_count > 0; Line_width /= 1.0 ->
2003
2004    %% Proportional Effect
2005    Line_width1 = get_max(Line_width, 0.0) * get_scale_factor(1.168),
2006
2007    if Line_pattern =/= "0" ->
2008	    Dx = Line_pattern,
2009	    Dash ="[" ++ Dx ++ "] 0 setdash\n";
2010       true ->
2011	    Dash ="[] 0 setdash\n"
2012    end,
2013
2014    {R,G,B,_A} = Line_color,
2015    io:fwrite(F, "~.1f setlinewidth~n~ts", [get_max(Line_width1, 0.0),Dash]),
2016    io:fwrite(F, "~.2f ~.2f ~.2f setrgbcolor~n", [R, G, B]),
2017
2018    write_eps_line_group(F, Ls);
2019write_eps_line_group(F, _BB_size, Ls, _Line_code,_Line_width, _Line_color, _Line_pattern, _Group_count,_Line_cap) ->
2020    write_eps_line_group(F, Ls).
2021
2022write_eps_line_group(F, Ls) ->
2023    foreach(fun({{X1, Y1}, {X2, Y2}}) ->
2024		    io:fwrite(F, "~.1f ~.1f ~.1f ~.1f s~n", [X2, Y2, X1, Y1])
2025	    end, Ls).
2026
2027write_eps_footer(F) -> io:put_chars(F, "%%EOF\n\n").
2028
2029
2030write_eps_polygons(_F, _BB_size, [],  _Mats_dict, _LSpos) -> ok;
2031write_eps_polygons(F, {_Wbb, _Hbb} = _BB_size, Fs, Mats_dict,{Shade,Lpos} ) ->
2032    Shade = get_pref(fill_shade_type, ?DEF_FILL_SHADE_TYPE),
2033    Lpos  = get_pref(light_pos, ?DEF_LIGHT_POS),
2034    foldl(fun({FV2Cs, FP, Mat},Cnt0) ->
2035		  {_Is_transparent, {R, G, B, _A}} = hd(dict__fetch(hd(Mat), Mats_dict)),
2036		  {SR, SG, SB} = set_face_shade(eps, FP, {R,G,B},Shade,Lpos), % shading(pseudo)
2037		  [{Hx,Hy} | FV2CsT] = FV2Cs,
2038		  io:fwrite(F, "~.2f ~.2f m ", [Hx,Hy]),
2039		  foreach(fun({X, Y}) ->
2040				  io:fwrite(F, "~.2f ~.2f l ", [X, Y])
2041			  end, FV2CsT),
2042		  io:fwrite(F, "~.2f ~.2f ~.2f f~n", [SR, SG, SB]),
2043		  poly_pb(Cnt0, length(Fs)),
2044		  Cnt0 + 1
2045	  end, 0, Fs).
2046
2047
2048
2049%%
2050%%  Dot lines / SVG NPR Setting
2051%%
2052
2053
2054get_scale_factor(Factor) ->
2055    Scale_flag = get_pref( scale_prop, ?DEF_SCALE_PROP),
2056    Wbb = get_max(get_pref(bb_width, ?DEF_WIDTH), 800),
2057    Hbb = get_max(get_pref(bb_height, ?DEF_HEIGHT), 600),
2058    if  Scale_flag  =:= true -> abs(((Wbb + Hbb) * 0.75) / 1400)*Factor;true -> 1  end.
2059get_scale_factor()->
2060    Scale_flag = get_pref( scale_prop, ?DEF_SCALE_PROP),
2061    Wbb = get_max(get_pref(bb_width, ?DEF_WIDTH), 800),
2062    Hbb = get_max(get_pref(bb_height, ?DEF_HEIGHT), 600),
2063    if  Scale_flag  =:= true -> abs(((Wbb + Hbb) * 0.75) / 1400);true -> 1  end.
2064
2065
2066s_r(Val)->
2067    io_lib:format("~.2f",[Val]).
2068
2069%% RGB=( 0, 255 ,0 ) -> #00FF00  ~F.P.Pad (F=2 P=16 PadChar=0)
2070set_fill_hex_color(R,G,B)->io_lib:format("#~2.16.0B~2.16.0B~2.16.0B",[get_min(255,R), get_min(255,G), get_min(255,B)]).
2071%% set_fill_rgb_color(R,G,B)-> io_lib:format("~B,~B,~B",[R,G,B]).
2072
2073%% Shade 0: None
2074set_face_shade( Mode, _FP, {R,G,B}, Shade, _Lpos ) when Shade == 0  ->
2075    case Mode of
2076	eps -> {R,G,B};
2077	svg_class -> "";
2078	svg_attrb -> "fill:"++ set_fill_hex_color(round(R*255),round(G*255),round(B*255)) ++";"
2079    end;
2080%% Shade 1: Normal
2081set_face_shade( Mode, {FN, _FD}, {R,G,B}, Shade ,_Lpos ) when Shade == 1  ->
2082    {_Nx, _Ny, Nz0} = FN, Nz = abs(Nz0),
2083    %% io:format("RGB: ~p,~p,~p~n",[round(R*Nz*255),round(G*Nz*255),round(B*Nz*255)]),
2084    case Mode of
2085	eps -> {R*Nz,G*Nz,B*Nz};
2086	svg_class -> " style=\"fill:"++ set_fill_hex_color(round(R*Nz*255),round(G*Nz*255),round(B*Nz*255)) ++"\"";
2087	svg_attrb -> "fill:"++ set_fill_hex_color(round(R*Nz*255),round(G*Nz*255),round(B*Nz*255)) ++";"
2088    end;
2089
2090%% Shade 6: PatID= [1,2,3,4]    [5,6,7,8]
2091set_face_shade( Mode, {FN, _FD}, {_R,_G,_B}, Shade,Lpos ) when Shade >= 6 andalso Mode =/= eps ->
2092    {Lx,Ly,Lz,Fact}=Lpos, PatId = (Shade - 6) * 4 + 1,
2093    Nz0 = abs(1-(dot(FN, {Lx,Ly,Lz})*Fact)),
2094						%io:format("dot:~p  FN ~p ~n",[Nz0,FN]),
2095    Nz = case Shade >= 6  of
2096	     true when Nz0*0.5 < 0.2 -> io_lib:format("url(#pt~p)",[PatId+3]); % dark
2097	     true when Nz0*0.5 < 0.4 -> io_lib:format("url(#pt~p)",[PatId+2]); % middle
2098	     true when Nz0*0.5 < 0.6 -> io_lib:format("url(#pt~p)",[PatId+1]); % light
2099	     true when Nz0*0.5 < 0.8 -> io_lib:format("url(#pt~p)",[PatId]);
2100	     true when Nz0*0.5 < 0.95 -> "#FFFFFF";
2101	     _ -> "#FFFFFF"
2102	 end,
2103
2104    case Mode of
2105	svg_class -> io_lib:format(" style=\"fill:~ts\"",[Nz]);
2106	svg_attrb -> io_lib:format("fill:~ts;",[Nz])
2107    end;
2108
2109
2110
2111
2112%% Shade 2: Shade
2113set_face_shade( Mode, {FN, _FD}, {R,G,B}, Shade,Lpos ) ->
2114    {Lx,Ly,Lz,Fact}=Lpos,
2115    Nz0 = abs(1-(dot(FN, {Lx,Ly,Lz})*Fact)),
2116						%io:format("dot:~p  FN ~p ~n",[Nz0,FN]),
2117    Nz = case Shade of
2118	     2 -> Nz0;
2119	     3 when Nz0 < 0.88 -> 0.8;
2120	     4 when Nz0 < 0.3  -> 0.3; % dark
2121	     4 when Nz0 < 0.6  -> 0.6; % middle
2122	     4 when Nz0 < 0.88 -> 0.8; % light
2123	     5 when Nz0 < 0.88 -> 0.25;
2124	     _ when Nz0 > 0.9 -> 1;
2125	     _ -> Nz0
2126	 end,
2127
2128    case Mode of
2129	eps -> {R*Nz,G*Nz,B*Nz};
2130	svg_attrb -> "fill:"++ set_fill_hex_color(round(R*Nz*255),round(G*Nz*255),round(B*Nz*255)) ++";";
2131	_ -> " style=\"fill:"++ set_fill_hex_color(round(R*Nz*255),round(G*Nz*255),round(B*Nz*255)) ++"\""
2132    end.
2133
2134%% Shade
2135define_fill_type() ->
2136    [
2137     { ?__(1,"None")    ,0},
2138     { ?__(2,"Normal")  ,1},
2139     { ?__(3,"Shade")   ,2},
2140     { ?__(4,"2-tone")  ,3},
2141     { ?__(5,"3-tone")  ,4},
2142     { ?__(6,"Contrast"),5},
2143     { ?__(7,"HalfTone"),6},
2144     { ?__(8,"Hatching"),7}
2145    ].
2146
2147%% Light_positon            Name {Lx, Ly, Lz, Illuminence Factor}
2148define_light_pos() ->
2149    [
2150      { ?__(1,"Up to Down") , {-480,-512, 500,0.00096} },
2151      { ?__(2,"Down to Up") , {-480, 512, 500,0.00096} },
2152      { ?__(3,"Down to Up2"), {   1, 512, 256,0.00108} },
2153      { ?__(4,"Contrast")   , {-480,-512, 512,0.00108} },
2154      { ?__(5,"Overlight")  , {   1,-999, 300,0.00186} },
2155      { ?__(6,"From Right") , {-700,-400, 600,0.0005} },
2156      { ?__(7,"From Left")  , { 600,-400, 700,0.0005} },
2157      { ?__(8,"Center")     , {-400,-400,-400,0.0001} }
2158
2159    ].
2160
2161%% Line style  ( dotted pattern )
2162define_dot_styles() ->
2163    [
2164     { "None"  , "0"},
2165     { "Dash-1", "1 1"},
2166     { "Dash-2", "2 2"},
2167     { "Dash-4", "4 4"},
2168     { "Dash-8", "8 8"},
2169     { "Dot-2" , "0.1 2"},
2170     { "Dot-4" , "0.1 4"},
2171     { "Dot-6" , "0.1 6"},
2172     { "_-__"  , "6 3 1 3"},
2173     { "_--_"  , "15 5 1 3 1 5"},
2174     { "_.__"  , "6 3 0.1 3"},
2175     { "_.._"  , "16 2 0.1 2 0.1 2"},
2176     { "_._x2" , "20 5 0.1 5"},
2177     { "_..x2" , "20 5 0.1 5 0.1 5"},
2178     { "Fibonacci", "1 1 2 3 5 8 13"},
2179     { "Irregular", "13 3 1 1 3 5"},
2180     { "Irregular2", "0 6 1 0 3 7"},
2181     { "Irregular3", "0 7 80 0 18 0"}
2182    ].
2183
2184define_svg_filter( Mode, Type) ->
2185    %% ui: return Name for UI build,  getp_aram: return Other Parameters
2186    %%  Freq , Octave, Scale, Blur  ,  Morph, fact,  Opt,  Name     , Type#   Opt -1 turbulance
2187    FList0 =[
2188	     { "0",          0,     0,    0,   0,     0,    0, "Not apply"},        %%  1
2189	     { "0.013",      2,     5,    0,   0,     0,    0, "Drawn" },           %%  2
2190	     { "0.089",      4,     3,    0,   0,     0,    0, "Pencil"},           %%  3
2191	     { "0.030",     10,    14,    0,   0,     0,    0, "Brush" },           %%  4
2192	     { "0.15 0.007", 1,     6,    0,   0,     0,   -1, "Paint" },           %%  5
2193	     { "0.0053 0.03",10,    6,    0,   0,     0,   -1, "Doodle"},           %%  6
2194	     { "0.03 0.018", 3,    12,    0,   0,     0,    1, "Twice Draw" },      %%  7
2195	     { "0.15 0.038", 3,     7,    0,   0,     0,    2, "Twice Draw2"},      %%  8
2196	     { "0.05 0.018", 2,     9,  1.0,   0,     0,    1, "Twice (Wet)"},      %%  9
2197	     { "-0.17320",   6,     2,    0,   0,     0,    0, "Grunge Brush" },    %% 10
2198	     { "0.9",        2,     3,    0,   0,     0,    0, "Chalk" },           %% 11
2199	     { "0.9",        2,    10,    0,   0,     0,    0, "Chalk(Rough)" },    %% 12
2200	     { "0.5",        5,     7,  3.0,   0,     0,    2, "Crayon" },          %% 13
2201	     { "0.9",        2,    20,    0,   0,     0,    0, "Sand Art"},         %% 14
2202	     { "0.019",     14,    18,    0,   0,     0,    0, "Distortion" },      %% 15
2203	     { "0.01 0.008", 3,    12,    0,  "D","0.9",    3, "Shadow Blur"},      %% 16
2204	     { "0.002 0.002",5,    12,    0,  "D","0.9",    3, "Shadow Blur2"},     %% 17
2205	     { "0.01 0.008" ,5,    12,    0,  "D",  "2",    3, "Shadow Blur3"},     %% 18
2206	     { "0.9 0.9"   , 5,    12,    0,  "D",  "2",    3, "Shadow Blur4"},     %% 19
2207	     { "0.05 0.05", 2,     9,   0.5,   0,     0,    6, "Triple Lines"},     %% 20
2208	     { "0.005 0.0045",8,   12,    0,   0,     0,    5, "Wet Blur"},         %% 21
2209	     { "0.0015 0.0045",8,  12,    0,   0,     0,    5, "Wet Blur2"},        %% 22
2210	     { "0.005 0.42" ,5,    12,    0,   0,     0,    5, "Graffiti(H)"},      %% 23
2211	     { "0.42 0.005" ,5,    12,    0,   0,     0,    5, "Graffiti(V)"},      %% 24
2212	     { "0.0006 0.0078",7,   6,  1.0, "E",   "0.1",  5, "Wind Blur(H)" },    %% 25
2213	     { "0.0078 0.0006",7,   6,  1.0, "E",   "0.1",  5, "Wind Blur(V)" },    %% 26
2214	     { "0.53 0.3",   4,    12,    0, "E",   "0.1",  5, "DrawingPaper" },    %% 27
2215	     { "0.4 0.95",   7,    12,    0, "E",   "0.1",  5, "DrawingPaper2" },   %% 28
2216	     { "0.9 0.9",    5,     8,    0, "E",   "2",    5, "DrawingPaper3"},    %% 29
2217	     {   "1",        1,     1,    0,   0,     0,    4, "Silhouette"},       %% 30
2218	     { "0",          0,     0, 0.25,   0,     0,    0, "Blur 0.25"  },      %% 31
2219	     { "0",          0,     0,  1.0,   0,     0,    0, "Blur   1"   },      %% 32
2220	     { "0",          0,     0,  2.5,   0,     0,    0, "Blur 2.5"   },      %% 33
2221	     { "0",          0,     0,  5.0,   0,     0,    0, "Blur   5"   },      %% 34
2222	     { "0",          0,     0, 10.0,   0,     0,    0, "Blur  10"   },      %% 35
2223	     { "0.089",      1,     3,    0,   0,     0,    0, "OverSketch"}        %% 36
2224	    ],
2225
2226    case Mode of
2227	ui ->{ AList, _Total } = foldl(fun(X,  { Acc0 ,Cnt0 } ) ->
2228					       { _, _, _, _, _,_,_, Name } = X,
2229					       Cnt = Cnt0 + 1,
2230					       {Acc0 ++ [{ Name, Cnt }] , Cnt }
2231				       end, {[],0} , FList0 ),
2232	     AList;
2233        get_param -> { Freq, Octave, Scale, Blur, Morph, Mfactor, Option ,Name }  = lists:nth( Type , FList0),
2234                     { Freq, Octave, Scale, Blur, Morph, Mfactor, Option ,Name };
2235	get_length -> length(FList0)
2236    end.
2237
2238
2239
2240add_svg_filter(Mode,Type) when Type > 1 ->
2241    case Mode of
2242	fill -> " filter=\"url(#fill_style"++ integer_to_list(Type)++")\" ";
2243	line -> " filter=\"url(#line_style"++ integer_to_list(Type)++")\" "
2244    end;
2245add_svg_filter(_, _) -> "".
2246
2247
2248
2249turbulence_type(Flag) when Flag == -1  ->"turbulence";
2250turbulence_type(_)  ->"fractalNoise".
2251
2252
2253get_svg_color_int()->
2254    case get_pref(svg_color_int, ?DEF_SVG_COLOR_INT) of
2255	0 -> "sRGB";
2256	1 -> "linearRGB";
2257	2 -> "auto";
2258	_ -> "sRGB"
2259    end.
2260
2261
2262
2263add_svg_filter_tag(F, Mode, Type) when Type > 1 ->
2264    Id_name = case Mode of
2265                  fill -> "fill";
2266                  line -> "line"
2267	      end,
2268    { Freq, Octave, Scale, Blur, Morph0 ,Mfactor0 ,Option, Name } = define_svg_filter(get_param, Type),
2269
2270    { Morph,Mfactor} = case Morph0    of
2271			   "E" -> {"erode" ,[Mfactor0] };
2272			   "D" -> {"dilate",[Mfactor0] };
2273			   _ -> {"dilate","0.9 0.25" }
2274		       end,
2275
2276    %% Color interpolution filters
2277    Svg_color_int = get_svg_color_int(),
2278
2279    %% Proportional Effect
2280    Scf =  get_scale_factor(),
2281
2282    %%  Set color space to sRGB for Blur, because only Safari use linearRGB default
2283    io:fwrite(F, "<!-- Wings3D SVG Style library ~p: ~ts -->~n",[Type ,Name ]),
2284    io:fwrite(F, "<filter id=\"~ts" ++ "_style~p\" >~n",[Id_name,Type]),
2285
2286    if Freq =/= "0" -> io:fwrite(F, "\t<feTurbulence type=\"~ts\" baseFrequency=\"~ts\"  numOctaves=\"~p\" result=\"filpr1\" filterRes=\"720 720\" />~n", [ turbulence_type(Option), Freq, Octave]); true -> ok  end,
2287    if Scale > 0 -> io:fwrite(F, "\t<feDisplacementMap xChannelSelector=\"R\" yChannelSelector=\"G\" in=\"SourceGraphic\" in2=\"filpr1\"  result=\"filpr2\"  scale=\"~ts\" />~n",[s_r(Scale*Scf*1.0)]); true -> ok  end,
2288    if Blur > 0 -> io:fwrite(F, "\t<feGaussianBlur color-interpolation-filters=\"~ts\"  stdDeviation=\"~ts\"/>~n", [Svg_color_int, s_r(Blur*Scf*1.0)] ); true -> ok  end,
2289
2290    case Option of
2291	0 -> ok;
2292	1 -> % TwiceDrawn
2293	    io:fwrite(F, "\t<feTurbulence baseFrequency=\"0.0735\" type=\"fractalNoise\" result=\"filpr3\" numOctaves=\"4\"/>\n"
2294		      "\t<feDisplacementMap scale=\"6\" xChannelSelector=\"R\" in2=\"filpr3\" yChannelSelector=\"G\" result=\"filpr4\" in=\"SourceGraphic\"/>\n"
2295		      "\t<feComposite operator=\"over\" in2=\"filpr4\" color-interpolation-filters=\"~ts\" result=\"filpr5\" in=\"filpr2\"/>\n",
2296		      [Svg_color_int]);
2297	2 -> % TwiceDraw(strong)
2298	    io:put_chars(F, "\t<feTurbulence baseFrequency=\"0.02 0.14\" type=\"fractalNoise\" result=\"filpr3\" numOctaves=\"2\"/>\n"
2299			 "\t<feDisplacementMap scale=\"15\" xChannelSelector=\"R\" in2=\"filpr3\" yChannelSelector=\"G\" result=\"filpr4\" in=\"SourceGraphic\"/>\n"
2300			 "\t<feBlend in2=\"filpr4\" mode=\"lighten\" result=\"filpr5\" in=\"filpr2\"/>\n"
2301			);
2302	3 -> %color
2303	    io:put_chars(F, "\t<feDisplacementMap scale=\"8\" xChannelSelector=\"R\" in2=\"filpr2\" yChannelSelector=\"G\" result=\"filpr3\" in=\"SourceGraphic\"/>\n" ),
2304	    io:fwrite(F,    "\t<feMorphology operator=\"~ts\"  in=\"filpr2\" result=\"filpr6\" radius=\"~ts\" />\n", [Morph, Mfactor ] ),
2305	    io:fwrite(F,   "\t<feGaussianBlur in=\"filpr6\" stdDeviation=\"~ts\"  result=\"filpr6\" color-interpolation-filters=\"~ts\" />\n", [ Mfactor, Svg_color_int] ),
2306	    io:put_chars(F, "\t<feColorMatrix type=\"matrix\" values=\"0 0 1 0 0, 0 0 1 0 0, 0 0 1 0 0, 0 0 0 0.67 0.0\" in2=\"filpr6\" result=\"filpr7\"   /> \n"
2307			 "\t<feBlend mode=\"screen\"  in=\"filpr7\" in2=\"filpr2\"/>\n"
2308			);
2309	4 -> % Silhouette
2310	    io:put_chars(F, "\t<feBlend in2=\"filpr2\" mode=\"over\" result=\"filpr5\" in=\"SourceAlpha\"/>\n"
2311			);
2312
2313	5 -> % WetPaint
2314	    io:put_chars(F,
2315			 "\t<feDisplacementMap scale=\"8\" xChannelSelector=\"R\" in2=\"filpr2\" yChannelSelector=\"G\" result=\"filpr3\" in=\"SourceGraphic\"/>\n"),
2316	    io:fwrite(F, "\t<feMorphology operator=\"~ts\"  in=\"filpr2\" result=\"filpr6\" radius=\"~ts\" />\n" ,[Morph,Mfactor]),
2317	    io:fwrite(F,"\t<feTurbulence type=\"turbulence\" baseFrequency=\"~ts\" in=\"SourceGraphic\" numOctaves=\"~p\" result=\"filpr9\" />~n",[Freq,Octave]),
2318	    io:put_chars(F, "\t<feColorMatrix type=\"matrix\" values=\".2 .2 .2 0 0, .2 .2 .2 0 0, .4 .4 .2 0 0, 0 0 0 0.2 0\" in=\"filpr6\" result=\"filpr7\" />\n"
2319			 "\t<feComposite operator=\"xor\" in=\"filpr6\" in2=\"filpr9\" result=\"filpr10\" />\n"
2320			 "\t<feBlend mode=\"darken\"  in=\"filpr10\" in2=\"filpr7\" result=\"filpr11\"/>\n"
2321			 "\t<feComposite operator=\"out\" in=\"filpr2\" in2=\"filpr11\"  />"
2322			);
2323
2324	6 -> % Triple Line
2325	    io:put_chars(F,
2326			 "\t<feTurbulence type=\"fractalNoise\" baseFrequency=\"0.05\"  numOctaves=\"5\" result=\"filpr2\" filterRes=\"720 720\" />\n"
2327			 "\t<feDisplacementMap xChannelSelector=\"R\" yChannelSelector=\"G\" in=\"SourceGraphic\" in2=\"filpr2\"  result=\"filpr2\"  scale=\"7\" />\n"
2328			 "\t<feColorMatrix type=\"matrix\" values=\"0 0 1 0 0, 0 0 1 0.7 0, 0 0 1 0 0, 0 0 0 0.65 0.0\" in=\"filpr2\" result=\"filpr3\"   /> \n"
2329			 "\t<feTurbulence type=\"fractalNoise\" baseFrequency=\"0.030\"  numOctaves=\"5\" result=\"filpr11\" filterRes=\"720 720\" />\n"
2330			 "\t<feDisplacementMap scale=\"7\" xChannelSelector=\"R\"  yChannelSelector=\"G\" result=\"filpr12\" in2=\"filpr11\" in=\"SourceGraphic\"/>\n"
2331			 "\t<feColorMatrix type=\"matrix\" values=\"0 0 1 0.7 0, 0 0 1 0 0, 0 0 1 0 0, 0 0 0 0.45 0.0\" in=\"filpr12\" result=\"filpr13\"   />\n"
2332			 "\t<feTurbulence type=\"fractalNoise\" baseFrequency=\"0.03 0.08\"  numOctaves=\"5\" result=\"filpr21\" filterRes=\"720 720\" />\n"
2333			 "\t<feColorMatrix type=\"matrix\" values=\"0 0 1 0 0, 0 0 1 0 0, 0 0 1 0.5 0, 0 0 0 0.55 0.0\" in=\"filpr21\" result=\"filpr22\"   />\n"
2334			 "\t<feDisplacementMap scale=\"5\" xChannelSelector=\"R\"  yChannelSelector=\"G\" result=\"filpr23\"  in2=\"filpr22\" in=\"SourceGraphic\"/>\n"
2335			 "\t<feComposite operator=\"in\" in=\"filpr1\" in2=\"filpr2\" result=\"filpr103\" />\n"
2336			 "\t<feMerge>\n"
2337			 "\t\t<feMergeNode in=\"filpr3\"/>\n"
2338			 "\t\t<feMergeNode in=\"filpr13\"/>\n"
2339			 "\t\t<feMergeNode in=\"filpr23\"/>\n"
2340			 "\t\t<feMergeNode in=\"filpr103\"/>\n"
2341			 "\t</feMerge>\n"
2342			 "\t<feComposite operator=\"out\" in2=\"filpr11\"  />\n"
2343			);
2344
2345	_ -> ok
2346    end,
2347    io:put_chars(F, "</filter>\n" );
2348add_svg_filter_tag(_, _, _) -> " ".
2349
2350
2351add_svg_pattern(F,Shade)->
2352    {R,G,B,_A}= get_pref( regl_color, ?DEF_REGL_COLOR),
2353    Color = set_fill_hex_color(round(R*255),round(G*255),round(B*255)),
2354    Scf=get_scale_factor(),
2355
2356    case Shade of
2357	6 ->
2358	    io:put_chars(F, "<pattern id=\"pt1\" viewBox=\"-30 -30 30 30\" x=\"0\" y=\"8\" width=\"8\" height=\"8\" patternUnits=\"userSpaceOnUse\" patternTransform=\"scale("++ s_r(0.8*Scf) ++") rotate(35)\"><rect x=\"-30\" y=\"-30\" width=\"35\" height=\"35\" stroke=\"none\" fill=\"white\" /><circle stroke=\"none\" cx=\"-10\" cy=\"-10\" r=\"3\" fill=\""++ Color ++"\" /></pattern>\n"
2359			 "<pattern id=\"pt2\" viewBox=\"-30 -30 30 30\" x=\"0\" y=\"8\" width=\"8\" height=\"8\" patternUnits=\"userSpaceOnUse\" patternTransform=\"scale("++ s_r(0.5*Scf) ++") rotate(35)\"><rect x=\"-30\" y=\"-30\" width=\"35\" height=\"35\" stroke=\"none\" fill=\"white\" /><circle stroke=\"none\" cx=\"-10\" cy=\"-10\" r=\"5\" fill=\""++ Color ++"\" /></pattern>\n"
2360			 "<pattern id=\"pt3\" viewBox=\"-30 -30 30 30\" x=\"0\" y=\"10\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\" patternTransform=\"scale("++ s_r(0.5*Scf) ++") rotate(35)\"><rect x=\"-30\" y=\"-30\" width=\"35\" height=\"35\" stroke=\"none\" fill=\"white\" /><circle stroke=\"none\" cx=\"-10\" cy=\"-10\" r=\"10\" fill=\""++ Color ++"\" /></pattern>\n"
2361			 "<pattern id=\"pt4\" viewBox=\"0 0 40 40\" x=\"10\" y=\"10\" width=\"20\" height=\"20\" patternUnits=\"userSpaceOnUse\" patternTransform=\"scale("++ s_r(0.3*Scf) ++") rotate(35)\"><rect x=\"0\" y=\"0\" width=\"41\" height=\"41\" stroke=\"none\" fill=\"white\" /><circle stroke=\"none\" cx=\"18\" cy=\"18\" r=\"20\" fill=\""++ Color ++"\" /></pattern>\n");
2362	7 ->
2363	    io:put_chars(F,
2364			 "<pattern id=\"pt5\" width=\"20\" height=\"20\" patternUnits=\"userSpaceOnUse\" patternTransform=\"scale("++ s_r(0.6*Scf) ++") rotate(-15)\"><rect x=\"0\" y=\"0\" width=\"40\" height=\"40\" stroke=\"none\" fill=\"white\" /><g stroke=\""++ Color ++"\" stroke-width=\"0.3\"  ><path d=\"M0,0 40,40z\"/></g></pattern>\n"
2365			 "<pattern id=\"pt6\" width=\"20\" height=\"20\" patternUnits=\"userSpaceOnUse\" patternTransform=\"scale("++ s_r(0.6*Scf) ++") rotate(15)\"><rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" stroke=\"none\" fill=\"white\" /><g stroke=\""++ Color ++"\" stroke-width=\"0.5\"  ><path d=\"M0,0 20,20z\"/></g></pattern>\n"
2366			 "<pattern id=\"pt7\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\" patternTransform=\"scale("++ s_r(0.6*Scf) ++") rotate(15)\"><rect x=\"0\" y=\"0\" width=\"10\" height=\"10\" stroke=\"none\" fill=\"white\" /><g stroke=\""++ Color ++"\" stroke-width=\"1\"  ><path d=\"M0,0 20,20z\"/></g></pattern>\n"
2367			 "<pattern id=\"pt8\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\" patternTransform=\"scale("++ s_r(0.6*Scf) ++") rotate(15)\"><rect x=\"0\" y=\"0\" width=\"10\" height=\"10\" stroke=\"none\" fill=\"white\" /><g stroke=\""++ Color ++"\" stroke-width=\"1.5\"  ><path d=\"M0,0 10,10M 0,10 10,0z\"/></g></pattern>\n");
2368	_ ->ok
2369    end.
2370
2371
2372add_svg_marker(F)->
2373    io:put_chars(F, "<marker id=\"mkr1\" viewBox=\"0 0 380 60\" refX=\"82\" refY=\"10\"\n"
2374		 "\tmarkerUnits=\"strokeWidth\" orient=\"auto\"\n"
2375		 "\tmarkerWidth=\"60\" markerHeight=\"32\">\n"
2376		 "\t<path fill=\"none\" stroke=\"#000\" stroke-width=\"0.65\" d=\"M14.899,16.505c0,0,63.636-14.141,92.929,2.021\"/>\n"
2377		 "</marker>\n").
2378
2379
2380get_svg_prefs()->
2381    { get_pref(svg_art_fill_type, ?DEF_SVG_ART_FILL_TYPE),
2382      get_pref(svg_art_line_type, ?DEF_SVG_ART_LINE_TYPE),
2383      define_svg_filter( get_length, 0) }.
2384
2385%% SVG Filter export
2386add_svg_filters(F,Is_style_catalog,Fill_type, Line_type) ->
2387
2388    %% Make for Style Catalog (This mode doesn't export 3D data)
2389    if  Is_style_catalog =:= true ->
2390	    lists:foldl(fun(_A,Cnt) -> add_svg_filter_tag( F, line,Cnt) ,  Cnt+1 end, 1,  define_svg_filter(ui, 0)),   % Export All Filter
2391	    add_svg_pattern(F,7);
2392	true ->
2393	    add_svg_filter_tag( F, fill,  Fill_type ),
2394	    add_svg_filter_tag( F, line,  Line_type )
2395    end.
2396
2397%% For Catalog  mode (All styles are display, Non 3D)
2398add_svg_catalog_base(F) ->
2399
2400    Svg_color_int = get_svg_color_int(),
2401
2402    io:put_chars(F, "<g id=\"portfolio\" transform=\"matrix(1 0 0 1 15 15)\" >\n"),
2403    io:fwrite(   F, "<text transform=\"matrix(1 0 0 1 2 25)\" stroke=\"none\" font-family=\"Arial Black\" font-size=\"16\">Wings3D<tspan font-family=\"Arial\" font-size=\"8\" >" ++ wpa:version() ++ "</tspan> SVG Setting Help <tspan font-family=\"Arial\" x=\"0\" dy=\"20\">The Style/Filter preset catalog.(~ts)</tspan></text>\n",[Svg_color_int]),
2404    io:put_chars(F,"\t<text transform=\"matrix(1 0 0 1 220 70)\" stroke=\"none\" font-family=\"Arial Black\" font-size=\"9\">SVG Artistic styles<tspan x=\"0\" dy=\"10\">for a fill and a line</tspan></text>\n"),
2405
2406    %% Filter Style
2407    lists:foldl(fun( A, { Col0, Row0, Cnt }) ->
2408			{Col, Row} = if Cnt rem 7 == 0 -> {Col0 + 1, 0}; true -> {Col0, Row0 + 1} end,
2409			{Name, _No} = A, add_svg_style_catalog(F,Name, Row, Col, Cnt),
2410			{Col, Row, Cnt + 1 }
2411		end, {0, 0, 1}, define_svg_filter(ui, 0)),  % Export All style
2412
2413    %% Dot Line Style
2414    io:put_chars(F,"\t<text transform=\"matrix(1 0 0 1 100 70)\" stroke=\"none\" font-family=\"Arial Black\" font-size=\"9\">The line styles <tspan x=\"0\" dy=\"10\">in the lucent Material</tspan></text>\n"),
2415    lists:foldl(fun({Name, Dot_style},Cnt0 ) ->
2416			io:put_chars(F,
2417				     "\t<line transform=\"matrix(1 0 0 1 105 "++ integer_to_list( 88 + 16 * Cnt0 ) ++")\" fill=\"none\" stroke=\"#000\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-dasharray=\""++ Dot_style ++"\" x1=\"0\" y1=\"0\" x2=\"40\" y2=\"0\"/>\n"
2418				     "\t<text transform=\"matrix(1 0 0 1 150 "++ integer_to_list( 88 + 2 + 16 * Cnt0 ) ++")\" stroke=\"none\" font-weight=\"lighter\"  font-size=\"9\">"++ Name ++"</text>\n"
2419				    ),
2420			Cnt0 + 1
2421		end, 0, define_dot_styles()),
2422
2423    %% Line width
2424    Pos = 70, % length(define_dot_styles()) + 1,
2425    lists:foldl(
2426      fun( _Width, Cnt0 ) when Cnt0 == 0 ->
2427	      io:put_chars(F, "\t<text transform=\"matrix(1 0 0 1 5 "++ integer_to_list(  Pos + 2 + 18 * Cnt0 ) ++")\" stroke=\"none\" font-family=\"Arial Black\" font-size=\"9\">Line width(pt)</text>\n"),
2428	      Cnt0 + 1;
2429	 ( Width, Cnt0 ) ->
2430	      io:put_chars(F, "\t<line transform=\"matrix(1 0 0 1 5 "++ integer_to_list( Pos + 18 * Cnt0 ) ++")\" fill=\"none\" stroke=\"#000\" stroke-width="),
2431	      io:fwrite(   F, "\"~.1fpt\"", [Width]),
2432	      io:put_chars(F, " stroke-linecap=\"round\" stroke-linejoin=\"round\"  x1=\"0\" y1=\"0\" x2=\"40\" y2=\"0\"/>\n"
2433			   "\t<text transform=\"matrix(1 0 0 1 55 "++ integer_to_list(  Pos + 2 + 18 * Cnt0 ) ++")\" stroke=\"none\" font-size=\"9\">"),
2434	      io:fwrite(   F, "~.1fpt", [Width]),
2435	      io:put_chars(F, "</text>\n"),
2436	      Cnt0 + 1
2437      end, 0, [0.0,0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 1.35, 1.5, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0,10.0]),
2438    io:put_chars(F,"\t<text transform=\"matrix(1 0 0 1 0 370)\" stroke=\"none\" font-size=\"9\">Note:"
2439		 "\t\t<tspan x=\"0\" dy=\"12\">*To using lightweight data for Website.</tspan>\n"
2440		 "\t\t<tspan x=\"0\" dy=\"12\">Results of the 3D rendering is actually</tspan>\n"
2441		 "\t\t<tspan x=\"0\" dy=\"12\">dividing many polygons.</tspan>\n"
2442		 "\t\t<tspan x=\"0\" dy=\"12\">(In particular face polygons)</tspan>\n"
2443		 "\t\t<tspan x=\"0\" dy=\"12\">If you want to use the result for web,</tspan>\n"
2444		 "\t\t<tspan x=\"0\" dy=\"12\">you should be more reduce Data size and </tspan>\n"
2445		 "\t\t<tspan x=\"0\" dy=\"12\">merge polygons to same colors. </tspan>\n"
2446		 "\t\t<tspan x=\"0\" dy=\"12\">(Ex.Illustrator: Use PathFinder &gt; Merge)</tspan>\n"
2447		 "\t\t<tspan x=\"0\" dy=\"20\">*Note2:</tspan>\n"
2448		 "\t\t<tspan x=\"0\" dy=\"12\">Advise for Illustrator CS,LibreOffice Draw User:</tspan>\n"
2449		 "\t\t<tspan x=\"0\" dy=\"12\">In these software,SVG importing is very slowly.</tspan>\n"
2450		 "\t\t<tspan x=\"0\" dy=\"12\">If SVG file size is over than 2MB,avoid reading</tspan>\n"
2451		 "\t\t<tspan x=\"0\" dy=\"12\">by these.</tspan>\n"
2452		 "\t\t<tspan x=\"0\" dy=\"12\">There is no problem to read in Inkscape.</tspan>\n"
2453		 "\t\t<tspan x=\"0\" dy=\"20\">*SVG CSS Property:</tspan>\n"
2454		 "\t\t<tspan x=\"0\" dy=\"12\">To UseClasName to make smaller file size.</tspan>\n"
2455		 "\t\t<tspan x=\"0\" dy=\"12\">Use Attribute_Compound option,Adobe Illustrator SVG importing speed is better than other type.</tspan>\n"
2456		 "\t\t<tspan x=\"0\" dy=\"12\">this option saving file size too, but Shade/Transparent display is not good.</tspan>\n"
2457		 "\t\t<tspan x=\"0\" dy=\"20\">File size Comparison:  </tspan>\n"
2458		 "\t\t<tspan x=\"0\" dy=\"15\">Attribute: 707Kb (--- Standard size --) </tspan>\n"
2459		 "\t\t<tspan x=\"0\" dy=\"12\">ClassName: 518Kb (smaller than 27-30% ) </tspan>\n"
2460		 "\t\t<tspan x=\"0\" dy=\"12\">Attrcompd: 564Kb (smaller than 21-42% ) </tspan>\n"
2461		 "\t</text>\n"),
2462    io:put_chars(F, "</g>\n").
2463
2464add_svg_style_catalog(F, Name, Col, Row, Cnt)->
2465    io:put_chars(F,
2466		 "<g transform=\"matrix(1 0 0 1 "++integer_to_list( 220 + 140 * Row )++" "++integer_to_list( 8 + 78 * Col )++")\">\n"
2467		 "\t<rect x=\"0\" y=\"0\" stroke-width=\"0.5\"  stroke=\"#abc\"  fill=\"none\" width=\"115\" height=\"72\"/>\n" % frame
2468		 "\t<rect x=\"3\" y=\"27\"  stroke=\"none\"  fill=\"#dee\" width=\"68\" height=\"32\"/>\n"
2469		 "\t<g id=\"sample_fill"++ integer_to_list(Cnt) ++"\" "++ add_svg_filter(line,Cnt) ++" >\n"
2470		 "\t\t<rect x=\"36\" y=\"30\"  stroke=\"none\"  fill=\"#E72243\" width=\"26\" height=\"26\"/><rect x=\"36\" y=\"4\" stroke=\"none\" fill=\"#43A722\" width=\"26\" height=\"26\"/>\n" %% fill red
2471		 "\t</g>\n"
2472		 "\t<g id=\"sample_line"++ integer_to_list(Cnt) ++"\" "++ add_svg_filter(line,Cnt) ++" >\n"
2473		 "\t\t<rect x=\"7\" y=\"7\" fill=\"none\" stroke=\"#000\" stroke-width=\"2\" stroke-miterlimit=\"10\" width=\"26\" height=\"26\"/>\n"  % outline
2474		 "\t\t<rect x=\"20\" y=\"20\" fill=\"url(#pt8)\" stroke=\"#000\" stroke-miterlimit=\"10\" width=\"26\" height=\"26\"/>\n"
2475		 "\t\t<line fill=\"none\" stroke=\"#000\" stroke-width=\"2\" stroke-miterlimit=\"10\" x1=\"70\" y1=\"16.5\" x2=\"110\" y2=\"16\"/>\n"
2476		 "\t\t<line fill=\"none\" stroke=\"#000\" stroke-width=\"1\" stroke-miterlimit=\"10\" x1=\"70\" y1=\"29\" x2=\"110\" y2=\"29\"/>\n"
2477		 "\t\t<line fill=\"none\" stroke=\"#000\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-dasharray=\"6,6\" x1=\"70\" y1=\"40\" x2=\"110\" y2=\"40\"/>\n"
2478		 "\t</g>\n"
2479		 "\t<text transform=\"matrix(1 0 0 1 5 68)\" stroke=\"none\" font-weight=\"lighter\"  font-size=\"9\">"++ Name ++"</text>\n"
2480		 "</g>\n"
2481		).
2482
2483
2484
2485%%
2486%%  SVG
2487%%
2488
2489
2490write_svg_header(F, {Wbb, Hbb}, Line_cap, Mats_dict, {Shade,_Lpos}) ->
2491    io:put_chars(F, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
2492		 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
2493		 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"),
2494    io:put_chars(F, "<!-- Generated by Wings3D " ++ wpa:version() ++ " Hidden Lines Renderer -->\n"),
2495    io:put_chars(F, "<svg version=\"1.2\" baseProfile=\"tiny\"   id=\"Layer1\" \n  x=\"0px\" y=\"0px\" "),
2496
2497    case get_pref( svg_render_type, ?DEF_SVG_RENDER_TYPE) of jaggy_line -> io:put_chars(F,"shape-rendering=\"crispEdges\""); _ -> ok end,
2498    case get_pref(responsive, ?DEF_RESPONSIVE) of true -> ok;false -> io:fwrite(F, " width=\"~w" ++ "px\" height=\"~w" ++ "px\" " , [ round(Wbb),round(Hbb)]) end,
2499
2500    io:fwrite(F, " viewBox=\"~w ~w ~w ~w\" style=\"enable-background:new 0 0 ~w ~w;\"~n" , [0, 0, round(Wbb), round(Hbb), round(Wbb), round(Hbb)]),
2501    io:put_chars(F, " stroke=\"black\""), % storoke = "currentColor" can't work in LibreOfficeDraw
2502    case Line_cap of
2503	0 -> ok;
2504	_ ->
2505	    io:put_chars(F, lists:flatten([" stroke-linecap=\"",
2506					   case Line_cap of 1 -> "round"; 2 -> "square" end, "\""]))
2507    end,
2508    io:put_chars(F, " xmlns=\"http://www.w3.org/2000/svg\">\n"),
2509
2510    Is_style_catalog = (file_test_svg =:= get_pref(file_type, ?DEF_FILE_TYPE)),
2511    { Fill_type, Line_type, Style_length } =get_svg_prefs(),
2512
2513    io:put_chars(F, "\n" ++ "<defs>\n" ),
2514    add_svg_filters(F,Is_style_catalog,Fill_type, Line_type ),
2515    if Line_type == Style_length -> add_svg_marker(F);true ->ok end,
2516    if Shade >= 6 -> add_svg_pattern(F,Shade);true->ok end,
2517    io:put_chars(F,  "</defs>\n" ),
2518    if  Is_style_catalog =:= true -> add_svg_catalog_base(F);true -> ok end,
2519
2520    case get_pref(file_type, ?DEF_FILE_TYPE) == file_svg andalso get_pref(svg_attb_type, ?DEF_SVG_ATTB_TYPE) of
2521        css_class -> get_mat_class(F,Mats_dict);
2522        attribute -> ok;
2523        compound -> ok;
2524        _ -> ok
2525    end.
2526
2527svg_line_cap(Line_cap) when Line_cap > 0 ->
2528    Type = case Line_cap of 1 -> "round"; 2 -> "square"; _-> "butt" end,
2529    "stroke-linejoin=\""++ Type ++"\" ";
2530svg_line_cap(_)->"".
2531
2532
2533write_svg_line_group(_F, _BB_size, [], _Line_code, _Line_width, _Line_color, _Line_pattern, _Group_count,_Line_cap) -> ok;
2534write_svg_line_group(F, BB_size, Ls, Line_code, Line_width, Line_color, Line_pattern, Group_count,Line_cap)
2535  when Group_count > -1  -> %% Line_width /= 1.0
2536    { _Fill_type, Line_type, Style_length } =get_svg_prefs(),
2537
2538    %% Proportional Effect
2539    Line_width1 = get_max(Line_width, 0.0) * get_scale_factor(1.168),
2540    if Line_pattern =/= "0" ->
2541	    Dx = Line_pattern,
2542	    Dash ="stroke-dasharray=\"" ++ Dx ++ "\"";
2543       true ->
2544	    Dash =""
2545    end,
2546
2547    Overline = if Line_type == Style_length andalso Line_code=:="Outline"  -> "marker-start=\"url(#mkr1)\" marker-end=\"url(#mkr1)\"";true ->"" end,
2548
2549    FixName = trunc(Line_width1 * 10),
2550    {R,G,B,A} = Line_color,
2551    {SR, SG, SB, SA} = {round(R * 255.0), round(G * 255.0), round(B * 255.0),A },
2552    Line_color_hex = set_fill_hex_color(SR,SG,SB),
2553    io:fwrite(F,
2554	      "\t<g  id=\"Line~p_~p\" inkscape:label=\"Line~p_~p\" inkscape:groupmode=\"layer\" stroke=\"~ts\" opacity=\"~.1f\"  stroke-width=\"~.1f\" ~ts "
2555	      "xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"  fill=\"none\" ~ts ~ts>\n",
2556	      [  FixName, Group_count, FixName, Group_count, set_fill_hex_color(SR,SG,SB), SA, Line_width1, Dash, Overline,svg_line_cap(Line_cap)]
2557	     ),
2558    Var_stroke = get_pref(var_stroke, ?DEF_VAR_STROKE),
2559    write_svg_line_group(F, BB_size, Ls, Var_stroke, Line_color_hex),
2560    io:put_chars(F, "\t</g>\n");
2561
2562write_svg_line_group(F, BB_size, Ls, _Line_code, _Line_width, _Line_color, _Line_pattern, _Group_count,_Line_cap) ->
2563    io:put_chars(F, "\t<g id=\"Line-base\" inkscape:label=\"Line-base\" inkscape:groupmode=\"layer\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" fill=\"none\">\n"),
2564    Var_stroke = get_pref(var_stroke, ?DEF_VAR_STROKE),
2565    write_svg_line_group(F, BB_size, Ls, Var_stroke, "#000000"),
2566    io:put_chars(F, "\t</g>\n").
2567
2568
2569
2570%% Use Var strole
2571write_svg_line_group(F, {_Wbb, Hbb}, Ls ,Var_stroke, Line_color_hex) when Var_stroke =:= true ->
2572    foreach(fun({{X1, Y1}, {X2, Y2}}) ->
2573		    {Sx,Sy} = sub({X1, Y1}, {X2, Y2}),
2574		    Dist = math:sqrt(Sx*Sx+Sy*Sy),
2575		    Qx= X1-Sx*0.07+8, Qy= Y1-Sy*0.25,
2576
2577		    %% io:format("Dist ~p,~p ~n", [Sx,Sy]),
2578
2579		    if Dist < 11 ->
2580			    io:fwrite(F, "\t<path"
2581				      " d=\"M~.1f,~.1fL~.1f,~.1f\"/>\n",
2582				      [X2, Hbb - Y2, X1, Hbb - Y1]);
2583		       true->
2584			    io:fwrite(F, "\t<path stroke=\"none\" fill=\"~ts\""
2585				      " d=\"M~.1f,~.1f Q~.1f,~.1f ~.1f,~.1f s~f,~f ~.1f,~.1f z\"/>\n",
2586				      [Line_color_hex,X2, Hbb - Y2, Qx, Hbb- Qy, X1, Hbb - Y1, Sx*0.1, Sy*0, Sx*-0.15, Sy*0.15])
2587		    end
2588	    end, Ls);
2589
2590%% Use Path element
2591write_svg_line_group(F, {_Wbb, Hbb}, Ls,_Var_stroke,_Line_color_hex) ->
2592    foreach(fun({{X1, Y1}, {X2, Y2}}) ->
2593		    io:fwrite(F, "\t<path"
2594			      " d=\"M~.1f,~.1fL~.1f,~.1f\"/>\n",
2595			      [X2, Hbb - Y2, X1, Hbb - Y1])
2596	    end, Ls).
2597
2598%% Use Line element
2599%% write_svg_line_group(F, {_Wbb, Hbb}, Ls) ->
2600%%    foreach(fun({{X1, Y1}, {X2, Y2}}) ->
2601%%        io:fwrite(F, "\t<line"
2602%%            " x1=\"~.1f\" y1=\"~.1f\" x2=\"~.1f\" y2=\"~.1f\"/>\n",
2603%%            [X2, Hbb - Y2, X1, Hbb - Y1])
2604%%    end, Ls).
2605
2606
2607write_svg_footer(F) -> io:put_chars(F, "</svg>\n").
2608
2609
2610%% {FV2Cs,FP,Mat} = Fs
2611write_svg_polygons(_F, _BB_size, [],  _Mats_dict,_LSpos) -> ok;
2612write_svg_polygons(F, {_Wbb, Hbb} = _BB_size, Fs,  Mats_dict, {Shade,Lpos}) ->
2613
2614    Svg_render_style = case get_pref( svg_render_type, ?DEF_SVG_RENDER_TYPE) of
2615			   smooth_fill -> " shape-rendering=\"crispEdges\" ";
2616			   _ -> ""
2617		       end,
2618    Stype = get_pref(svg_art_fill_type, ?DEF_SVG_ART_FILL_TYPE),
2619    io:put_chars(F,"\n<g id=\"faces\" inkscape:label=\"faces\" inkscape:groupmode=\"layer\" stroke=\"none\" " ++ Svg_render_style ++ add_svg_filter(fill,Stype) ++ " xmlns:inkscape=\"http://"++"www.inkscape.org/namespaces/inkscape\">\n"),
2620
2621    case get_pref(svg_attb_type, ?DEF_SVG_ATTB_TYPE) of
2622	css_class ->
2623	    {Class_dict, _Cnt } = foldl(fun( { MatName, T_RGBA }, { Acc_list0, Cnt0 } ) ->
2624						Cnt = Cnt0 + 1,
2625						FixName =  "m"++integer_to_list(Cnt),
2626						Acc_list  = Acc_list0 ++ [{ MatName, FixName, T_RGBA }],
2627						{Acc_list, Cnt }
2628					end, {[],0}, Mats_dict),
2629
2630	    foldl(fun({FV2Cs, FP, Mat},Cnt0) ->
2631			  Ref_Name = hd(Mat),
2632			  {  _value, { _Ref, Class_Name ,[{ _bool ,{R,G,B,_A}}] }} = lists:keysearch( Ref_Name, 1, Class_dict ),
2633			  Option_style = set_face_shade(svg_class, FP, {R,G,B}, Shade,Lpos),
2634						%io:fwrite(F, "\t<polygon class=\"~ts\"~ts points=\"", [Class_Name,Option_style]),
2635
2636			  io:fwrite(F, "\t<path class=\"~ts\"~ts d=\"M", [Class_Name,Option_style]),
2637			  foldl(fun({X, Y},VCnt) ->
2638					Zend = if VCnt >= length(FV2Cs) - 1 -> "z";true ->" " end,
2639					io:fwrite(F, "~.2f,~.2f~ts", [X, Hbb - Y, Zend]),
2640					VCnt + 1
2641				end, 0, FV2Cs),
2642			  io:put_chars(F, "\"/>\n"),
2643			  poly_pb(Cnt0, length(Fs)),
2644			  Cnt0+1
2645		  end, 0, Fs);
2646
2647	compound ->
2648
2649	    foldl(fun({FV2Cs, FP, Mat0},{PrevMat,PrevNorm,Cnt0}) ->
2650			  {Norm,_FD}=FP,
2651			  Mat=hd(Mat0),
2652			  {_Is_transparent, {R, G, B, A}} = hd(dict__fetch(Mat, Mats_dict)),
2653			  Colors = set_face_shade(svg_attrb, FP, {R,G,B}, Shade,Lpos),  % shading(pseudo)
2654			  Ts = (if A < 1.0 -> "shape-rendering:auto;";true->"" end),
2655
2656			  if Mat =/= PrevMat;Norm =/= PrevNorm ->
2657				  if Cnt0 > 0 -> io:put_chars(F, "\"/>\n"); true->ok end,
2658				  if A < 1.0 -> io:fwrite(F, "\t<path style=\"~tsopacity:~.2f;stroke:none;~ts\" d=\"", [Colors, A, Ts]);
2659				     true;Shade == 0-> io:fwrite(F, "\t<path style=\"~tsstroke:none;\" d=\"", [Colors])
2660				  end;
2661			     true ->
2662				  if A < 1.0 -> io:fwrite(F, "\"/>\n\t<path style=\"~tsopacity:~.2f;stroke:none;~ts\" d=\"", [Colors, A, Ts]); true->"" end
2663			  end,
2664
2665			  {_VCnt,Vtxs} = foldl(fun({X, Y},{VCnt,Acc0}) ->
2666						       Mov = if VCnt == 0 -> "M";true ->"" end,
2667						       Zend = if VCnt >= length(FV2Cs) - 1 -> "z";true ->"L" end,
2668						       {VCnt + 1, Acc0 ++ [io_lib:format("~ts~.2f,~.2f~ts", [Mov,X, Hbb - Y,Zend])] }
2669					       end, {0,[]}, FV2Cs),
2670			  io:put_chars(F, Vtxs),
2671			  if Cnt0 >= length(Fs)-1 -> io:put_chars(F, "\"/>\n"); true->ok end,
2672			  %% io:format("~p,~p ~n",[ Mat,PrevMat]),
2673			  poly_pb(Cnt0, length(Fs)),
2674			  CurrentMat = Mat, CurrentNorm = Norm, Cnt = Cnt0+1,
2675			  {CurrentMat,CurrentNorm,Cnt}
2676		  end, {"_",{0,0,0},0}, Fs);
2677
2678	attribute ->
2679	    foldl(fun({FV2Cs, FP, Mat},Cnt0) ->
2680
2681			  {_Is_transparent, {R, G, B, A}} = hd(dict__fetch(hd(Mat), Mats_dict)),
2682			  Colors = set_face_shade(svg_attrb, FP, {R,G,B}, Shade,Lpos),  % shading(pseudo)
2683			  Ts = (if A < 1.0 -> "shape-rendering:auto;";true->"" end),
2684			  Opacity = if A < 1.0 -> io_lib:format("opacity:~.2f;",[A]);true->" " end,
2685			  io:fwrite(F, "\t<path style=\"~ts"++ Opacity ++"stroke:none;~ts\" d=\"M", [Colors, Ts]),
2686
2687			  foldl(fun({X, Y},VCnt) ->
2688					Zend = if VCnt >= length(FV2Cs) - 1 -> "z";true ->" " end,
2689					io:fwrite(F, "~.2f,~.2f~ts", [X, Hbb - Y, Zend]),
2690					VCnt + 1
2691				end, 0, FV2Cs),
2692			  io:put_chars(F, "\"/>\n"),
2693			  poly_pb(Cnt0, length(Fs)),
2694			  Cnt0 + 1
2695		  end,0, Fs);
2696
2697        _ -> ok
2698    end,
2699    io:put_chars(F,"</g>\n").
2700
2701
2702
2703%% Progress bar
2704poly_pb(Cnt, Total) when round(Total - Cnt + 1 / 100) rem 100 == 0 ->
2705    Percent = (Total - (Cnt + 1)) ,
2706    Percent2 = integer_to_list(round(Percent)),
2707    Total2 = integer_to_list(Total),
2708    wings_pb:update(0.5 + Percent * 0.01 * 0.5, "Left " ++ Percent2 ++ " of " ++ Total2 ++" objects"),
2709    wings_pb:pause(),
2710    ok;
2711poly_pb(_, _)->ok.
2712
2713
2714
2715dict__new() -> [].
2716
2717dict__append_list(Key0, Values0, KVs) ->
2718    case keysearch(Key0, 1, KVs) of
2719        {value, {_Key, Values}} ->
2720            keyreplace(Key0, 1, KVs, {Key0, Values ++ Values0})
2721		;
2722        false ->
2723            [{Key0, Values0} | KVs]
2724    end.
2725
2726dict__append(Key0, Value, KVs) ->
2727    case keysearch(Key0, 1, KVs) of
2728        {value, {_Key, Values}} ->
2729            keyreplace(Key0, 1, KVs, {Key0, [Value | Values]})
2730		;
2731        false ->
2732            [{Key0, [Value]} | KVs]
2733    end.
2734
2735dict__store(Key0, Value, KVs) ->
2736    case keymember(Key0, 1, KVs) of
2737        true ->
2738            keyreplace(Key0, 1, KVs, {Key0, [Value]})
2739		;
2740        false ->
2741            [{Key0, [Value]} | KVs]
2742    end.
2743
2744dict__fetch(Key, KVs) ->
2745    {value, {_Key, Values}} = keysearch(Key, 1, KVs),
2746    Values.
2747
2748tree__empty() -> {0, gb_trees:empty()}.
2749
2750%% tree__insert([], Tree_acc) -> Tree_acc;
2751%% tree__insert([Value | T], Tree_acc) ->
2752%%     tree__insert(T, tree__insert(Value, Tree_acc));
2753tree__insert(Value, {Next_key, GBtree} = _Tree) ->
2754    {Next_key + 1, gb_trees:insert(Next_key, Value, GBtree)}.
2755
2756tree__get([], _Tree, Values_acc) -> lists:reverse(Values_acc);
2757tree__get([Key | T], Tree, Values_acc) ->
2758    tree__get(T, Tree, [tree__get(Key, Tree) | Values_acc]).
2759
2760tree__get(Keys, Tree) when is_list(Keys) -> tree__get(Keys, Tree, []);
2761tree__get(Key, {_Next_key, GBtree} = _Tree) -> gb_trees:get(Key, GBtree).
2762
2763%% tree__last_key({Next_key, _GBtree} = _Tree) -> Next_key - 1.
2764
2765tree__next_key({Next_key, _GBtree} = _Tree) -> Next_key.
2766
2767%% tree__from_list(Values) when is_list(Values) ->
2768%%    tree__insert(Values, tree__empty()).
2769
2770%% tree__to_list({_Next_key, GBtree} = _Tree) -> gb_trees:values(GBtree).
2771
2772%% tree__do_map(_Fun, none, GBtree_acc0) -> GBtree_acc0;
2773%% tree__do_map(Fun, {Key, Value, GBtree_iter0}, GBtree_acc0) ->
2774%%    GBtree_acc = gb_trees:insert(Key, Fun(Value), GBtree_acc0),
2775%%    tree__do_map(Fun, gb_trees:next(GBtree_iter0), GBtree_acc).
2776
2777%% tree__map(Fun, {Next_key, GBtree} = _Tree) ->
2778%%    Iter = gb_trees:iterator(GBtree),
2779%%    {Next_key, tree__do_map(Fun, gb_trees:next(Iter), gb_trees:empty())}.
2780
2781tree__do_fold(_Fun, Acc0, none) -> Acc0;
2782tree__do_fold(Fun, Acc0, {Key, Value, GBtree_iter0}) ->
2783    tree__do_fold(Fun, Fun(Key, Value, Acc0), gb_trees:next(GBtree_iter0)).
2784
2785tree__fold(Fun, Acc, {_Next_key, GBtree} = _Tree) ->
2786    Iter = gb_trees:iterator(GBtree),
2787    tree__do_fold(Fun, Acc, gb_trees:next(Iter)).
2788
2789frustum_planes({{Xmin, Ymin, Zmin}, {Xmax, Ymax, Zmax}}) ->
2790    Left = {{1.0, 0.0, 0.0}, Xmin},
2791    Bottom = {{0.0, 1.0, 0.0}, Ymin},
2792    Far = {{0.0, 0.0, 1.0}, Zmin},
2793    Right = {{-1.0, 0.0, 0.0}, -Xmax},
2794    Top = {{0.0, -1.0, 0.0}, -Ymax},
2795    Near = {{0.0, 0.0, -1.0}, -Zmax},
2796    [Left, Bottom, Far, Right, Top, Near];
2797frustum_planes({HSx, HSy, Zfar, Znear, Zf}) ->
2798    Zf2 = Zf * Zf,
2799    Dx = math:sqrt(HSx * HSx + Zf2),
2800    Xrl = Zf / Dx,
2801    Zrl = -HSx / Dx,
2802    Dy = math:sqrt(HSy * HSy + Zf2),
2803    Ytb = Zf / Dy,
2804    Ztb = -HSy / Dy,
2805    Left = {{-Xrl, 0.0, Zrl}, 0.0},
2806    Bottom = {{0.0, -Ytb, Ztb}, 0.0},
2807    Far = {{0.0, 0.0, 1.0}, Zfar},
2808    Right = {{Xrl, 0.0, Zrl}, 0.0},
2809    Top = {{0.0, Ytb, Ztb}, 0.0},
2810    Near = {{0.0, 0.0, -1.0}, -Znear},
2811    [Left, Bottom, Far, Right, Top, Near].
2812
2813splitpoly__position(VD_prev, VD_cur, VD_next, {PDm, _PDp})
2814  when VD_cur >= PDm, VD_prev >= VD_cur, VD_next >= VD_cur -> front;
2815splitpoly__position(VD_prev, VD_cur, VD_next, {_PDm, PDp})
2816  when VD_cur =< PDp, VD_prev =< VD_cur, VD_next =< VD_cur -> back;
2817splitpoly__position(_VD_prev, VD_cur, VD_next, {PDm, PDp})
2818  when VD_cur >= PDp, VD_next =< PDm -> {point, front};
2819splitpoly__position(_VD_prev, VD_cur, VD_next, {PDm, PDp})
2820  when VD_cur =< PDm, VD_next >= PDp -> {point, back};
2821splitpoly__position(VD_prev, VD_cur, VD_next, {PDm, PDp})
2822  when VD_cur >= PDm, VD_cur =< PDp ->
2823    {vertex,
2824     if
2825	 VD_prev >= VD_cur; VD_next =< VD_cur -> front;
2826	 true -> back
2827     end};
2828splitpoly__position(_VD_prev, _VD_cur, _VD_next, _PDt) -> undef.
2829
2830splitpoly__get_vc(VC, _VC_tree) when is_tuple(VC) -> VC;
2831splitpoly__get_vc(VI, VC_tree) -> ctree__get(VI, VC_tree).
2832
2833splitpoly__insert_vc(Vs, VC_tree0) when is_list(Vs) ->
2834    {VIs, VC_tree} = lists:foldl(fun(V, {VIs_acc0, VC_tree_acc0}) ->
2835					 {VI, VC_tree_acc} = splitpoly__insert_vc(V, VC_tree_acc0),
2836					 {[VI | VIs_acc0], VC_tree_acc}
2837				 end, {[], VC_tree0}, Vs),
2838    {lists:reverse(VIs), VC_tree};
2839splitpoly__insert_vc(VC, VC_tree0) when is_tuple(VC)->
2840    {ctree__next_key(VC_tree0), ctree__insert(VC, VC_tree0)};
2841splitpoly__insert_vc(VI, VC_tree0)->
2842    {VI, VC_tree0}.
2843
2844splitpoly__do_find(_P, _PDt, [V_last], SVs, VDt, _VC_tree) ->
2845    {V_last, SVs, VDt};
2846splitpoly__do_find({PN, _PD} = P, PDt,
2847		   [V_cur | T] = _Vs, SVs0, {VD_prev, VD_cur} = _VDt0, VC_tree0) ->
2848    V_next = hd(T),
2849    VC_next = splitpoly__get_vc(V_next, VC_tree0),
2850    VD_next = dot(VC_next, PN),
2851    VDt = {VD_cur, VD_next},
2852						%io:format("\tV_cur=~w~n\tV_next=~w~n\tT=~w~n\tSVs0=~w~n\tVDp=~w~n\tVDc=~w~n\tVDn=~w~n\tPDt=~w~n~n", [V_cur, V_next, T, SVs0, VD_prev, VD_cur, VD_next, PDt]),
2853    case splitpoly__position(VD_prev, VD_cur, VD_next, PDt) of
2854        undef ->
2855            splitpoly__do_find(P, PDt, T, [V_cur | SVs0], VDt, VC_tree0)
2856		;
2857        {vertex, Pos} ->
2858            {Pos, T, V_cur, SVs0, VDt}
2859		;
2860        {point, Pos} ->
2861            VC_cur = splitpoly__get_vc(V_cur, VC_tree0),
2862            VC_new = ip(VC_cur, VC_next, P),
2863            {Pos, T, VC_new, [V_cur | SVs0], VDt}
2864		;
2865        Pos -> Pos
2866    end.
2867
2868splitpoly__find(P, PDt, Hs, Vs0, VDt0, VC_tree) ->
2869    case splitpoly__do_find(P, PDt, Vs0, [], VDt0, VC_tree) of
2870        {V_last, SVs, VDt} ->
2871            splitpoly__do_find(P, PDt, [V_last | Hs], SVs, VDt, VC_tree)
2872		;
2873        R -> R
2874    end.
2875
2876splitpoly__combine([], SVs2, Vs2) ->
2877    {Vs2, lists:reverse(SVs2)};
2878splitpoly__combine(SVs1, SVs2, Vs2) ->
2879    {Vs2 ++ tl(lists:reverse(SVs1)), lists:reverse(SVs2)}.
2880
2881splitpoly__combine(SVs1, SVs2, [_V1, V2] = Vs2, [_H1, H2] = _Hs)
2882  when V2 =:= H2 ->
2883    splitpoly__combine(SVs1, SVs2, Vs2);
2884splitpoly__combine(SVs1, SVs2, [V1] = Vs2, [_H1, H2] = _Hs) when V1 =:= H2 ->
2885    splitpoly__combine(SVs1, SVs2, Vs2);
2886splitpoly__combine(SVs1, SVs2, Vs2, Hs) ->
2887    splitpoly__combine(SVs1, SVs2, Vs2 ++ Hs).
2888
2889splitpoly__combine(front, P1, SVs1, back, P2, SVs2, Vs2, Hs) ->
2890    {FFVs, BFVs} = splitpoly__combine(SVs1, SVs2, Vs2, Hs),
2891    {P2, P1, FFVs, BFVs};
2892splitpoly__combine(_Pos1, P1, SVs1, _Pos2, P2, SVs2, Vs2, Hs) ->
2893    {FFVs, BFVs} = splitpoly__combine(SVs1, SVs2, Vs2, Hs),
2894    {P1, P2, BFVs, FFVs}.
2895
2896splitpoly__do_split({PN, PD} = P, [V1, V2 | _] = Vs0, VC_tree) ->
2897    EpsD = ?EPS1 * (1.0 + abs(PD)), % ?????????????????????????????????????????????????????????????
2898    PDt = {PD - EpsD, PD + EpsD},
2899    VC1 = splitpoly__get_vc(V1, VC_tree),
2900    VD1 = dot(VC1, PN),
2901    VC2 = splitpoly__get_vc(V2, VC_tree),
2902    VD2 = dot(VC2, PN),
2903    Hs = [V1, V2],
2904    case splitpoly__find(P, PDt, Hs, tl(Vs0), {VD1, VD2}, VC_tree) of
2905        {Pos1, Vs1, IP1, SVs1, VDt1} ->
2906						%io:format("Pos1=~w~nHs=~w~nVs1=~w~nIP1=~w~nSVs1=~w~nVDt1=~w~n~n", [Pos1, Hs, Vs1, IP1, SVs1, VDt1]),
2907            case splitpoly__find(P, PDt, Hs, Vs1, VDt1, VC_tree) of
2908                {Pos2, Vs2, IP2, SVs2, _VDt2} ->
2909						%io:format("Pos2=~w~nHs=~w~nVs2=~w~nIP2=~w~nSVs2=~w~n~n~n", [Pos2, Hs, Vs2, IP2, SVs2]),
2910                    splitpoly__combine(Pos1, IP1, SVs1, Pos2, IP2, SVs2, Vs2, Hs)
2911			;
2912                Pos2 ->
2913						%io:format("miss2=~w~n~n", [Pos2]),
2914                    Pos2
2915            end
2916		;
2917        Pos1 ->
2918						%io:format("miss1=~w~n~n", [Pos1]),
2919            Pos1
2920    end.
2921
2922splitpoly__split(P, VIs0, VC_tree0) ->
2923    case splitpoly__do_split(P, VIs0, VC_tree0) of
2924        {IP1, IP2, FFVs, BFVs} ->
2925            {IPI1, VC_tree1} = splitpoly__insert_vc(IP1, VC_tree0),
2926            {IPI2, VC_tree} = splitpoly__insert_vc(IP2, VC_tree1),
2927            {[IPI1 | FFVs] ++ [IPI2], [IPI2 | BFVs] ++ [IPI1], VC_tree}
2928		;
2929        R -> R
2930    end.
2931
2932splitpoly__do_clip([] = _BPs, Vs0, _VC_tree) -> Vs0;
2933splitpoly__do_clip([BP | T] = _BPs, Vs0, VC_tree) ->
2934						%io:format("BP=~w~n~n", [BP]),
2935						%io:format("Vs0=~w~n~n", [Vs0]),
2936    case splitpoly__do_clip(BP, Vs0, VC_tree) of
2937        Vs when is_list(Vs) -> splitpoly__do_clip(T, Vs, VC_tree);
2938        R -> R
2939    end;
2940splitpoly__do_clip(BP, Vs0, VC_tree) ->
2941    case splitpoly__do_split(BP, Vs0, VC_tree) of
2942        {IP1, IP2, FFVs, _BFVs} -> [IP1 | FFVs] ++ [IP2];
2943        front -> Vs0;
2944        _ -> nil
2945    end.
2946
2947splitpoly__clip(BPs, VIs0, VC_tree0) ->
2948    case splitpoly__do_clip(BPs, VIs0, VC_tree0) of
2949        Vs when is_list(Vs) -> splitpoly__insert_vc(Vs, VC_tree0);
2950        R -> R
2951    end.
2952
2953%% ztree__empty() -> nil.
2954
2955						%ztree__insert(Obj, Obj_zlim, nil) -> {[Obj], Obj_zlim, nil, nil};
2956						%ztree__insert(Obj, {Obj_zmin, Obj_zmax} = Obj_zlim,
2957%%    {Objs, {Node_zmin, Node_zmax} = Node_zlim, Near, Far}) ->
2958%%    if
2959%%        Obj_zmin >= Node_zmax ->
2960%%            {Objs, Node_zlim, ztree__insert(Obj, Obj_zlim, Near), Far}
2961%%            ;
2962%%        Obj_zmax =< Node_zmin ->
2963%%            {Objs, Node_zlim, Near, ztree__insert(Obj, Obj_zlim, Far)}
2964%%            ;
2965%%        true ->
2966%%% min max
2967%%            {[Obj | Objs],
2968%%                Node_zlim,
2969%%                {get_min(Obj_zmin, Node_zmin), get_max(Obj_zmax, Node_zmax)},
2970%%                Near, Far}
2971%%    end.
2972
2973						%ztree__to_list(nil, List) -> List;
2974						%ztree__to_list({Objs, _Node_zlim, Near, Far}, List) ->
2975%%    ztree__to_list(Far, Objs ++ ztree__to_list(Near, List)).
2976						%ztree__to_list(Ztree) -> ztree__to_list(Ztree, []).
2977
2978
2979ctree__empty(Proj) -> {Proj, tree__empty()}.
2980
2981ctree__insert(VCs, {Proj, Tree0}=_VC_tree0) when is_list(VCs) ->
2982    {Proj, lists:foldl(fun(VC, Tree_acc0) ->
2983			       tree__insert({VC, Proj(VC)}, Tree_acc0)
2984		       end, Tree0, VCs)};
2985ctree__insert(VC, {Proj, Tree0}=_VC_tree0) ->
2986    {Proj, tree__insert({VC, Proj(VC)}, Tree0)}.
2987
2988ctree__get(VIs, {_Proj, Tree}=_VC_tree) when is_list(VIs) ->
2989    [VC || {VC, _V2C} <- tree__get(VIs, Tree)];
2990ctree__get(VI, {_Proj, Tree}=_VC_tree) ->
2991    {VC, _V2C} = tree__get(VI, Tree), VC.
2992
2993ctree__get2d(VIs, {_Proj, Tree}=_VC_tree) when is_list(VIs) ->
2994    [V2C || {_VC, V2C} <- tree__get(VIs, Tree)];
2995ctree__get2d(VI, {_Proj, Tree}=_VC_tree) ->
2996    {_VC, V2C} = tree__get(VI, Tree), V2C.
2997
2998%% ctree__last_key({_Proj, Tree}=_VC_tree) -> tree__last_key(Tree).
2999
3000ctree__next_key({_Proj, Tree}=_VC_tree) -> tree__next_key(Tree).
3001
3002ctree__from_list(Proj, Values) when is_list(Values) ->
3003    ctree__insert(Values, ctree__empty(Proj)).
3004
3005
3006
3007%%  This function return true / false
3008%%  AVCs && BVCs  =  [ true or false ]   AVCs is Front etc...
3009intersect(AVCs, BVCs, Eps) ->
3010    lists:all(fun({AVC1, AVC2}) ->
3011		      DA = sub(AVC2, AVC1),
3012		      lists:any(fun(BVC) -> cross(DA, sub(BVC, AVC2)) >= Eps end, BVCs)
3013	      end, pair(AVCs)) andalso
3014	lists:all(fun({BVC1, BVC2}) ->
3015			  DB = sub(BVC2, BVC1),
3016			  lists:any(fun(AVC) -> cross(DB, sub(AVC, BVC2)) =< Eps end, AVCs)
3017		  end, pair(BVCs)).
3018
3019
3020
3021
3022
3023bspt__empty() -> nil.
3024
3025bspt__insert(FI0, F0, FZt0, nil, VC_tree0) -> {{FI0, F0, FZt0, nil, nil}, VC_tree0};
3026bspt__insert(FI0, {FVIs0, FP0, Mat0} = F0, FZt0,
3027	     {FI, {FVIs, FP, _Mat} = F, FZt, Back_bspt0, Front_bspt0}, VC_tree0) ->
3028    case splitpoly__split(FP, FVIs0, VC_tree0) of
3029        {Front_VIs, Back_VIs, VC_tree} ->
3030            FV2Cs0 = ctree__get2d(FVIs0, VC_tree),
3031            FV2Cs = ctree__get2d(FVIs, VC_tree),
3032
3033
3034
3035
3036
3037	    %% Output Priority
3038	    %% Scene  (Output for scene mainly made many sliced polygons,and file size larger.)
3039	    %%   intersect() true  false -> BAD Z order(for complex compotision ), but circular object OK  Default
3040
3041	    %% Path (Divide nice shapes and small size,but messed if it has overlapped objects.)
3042	    %%    intersect() false true -> Good Z order, but circular object NG
3043
3044	    Result = intersect(FV2Cs0, FV2Cs, ?EPS2 * ?EPS2),
3045
3046	    Division_priority = get_pref(division_priority, ?DEF_DIVISION_PRIORITY),
3047	    Overlapped =  case Division_priority of
3048			      scene -> not Result;
3049			      path -> Result;
3050			      _ -> false
3051			  end,
3052
3053	    %%  io:format("Overlap: ~p ~n",[Overlap_priority]),
3054
3055	    %% Front_VCs = ctree__get(Front_VIs, VC_tree),
3056	    %% Back_VCs = ctree__get(Back_VIs, VC_tree),
3057	    %% {Front_zmin, Front_zmax} = Front_Zt = face_zlim(Front_VCs),
3058	    %% {Back_zmin, Back_zmax} = Back_Zt = face_zlim(Back_VCs),
3059
3060	    %% {FZmin0, FZmax0} = FZt0,
3061	    %% {FZmin, FZmax} = FZt,
3062
3063            case Overlapped of
3064                true ->
3065
3066		    {Back_bspt, VC_tree1} =
3067			bspt__insert(nil, {Back_VIs, FP0, Mat0}, {0.0, 0.0}, Back_bspt0, VC_tree),
3068		    {Front_bspt, VC_tree2} =
3069			bspt__insert(nil, {Front_VIs, FP0, Mat0}, {0.0, 0.0}, Front_bspt0, VC_tree1),
3070		    {{FI, F, FZt, Back_bspt, Front_bspt}, VC_tree2}
3071			;
3072                false ->
3073                    {Back_bspt, VC_tree1} =
3074                        bspt__insert(FI0, {Back_VIs, FP0, Mat0}, {0.0, 0.0}, Back_bspt0, VC_tree),
3075                    {Front_bspt, VC_tree2} =
3076                        bspt__insert(FI0, {Front_VIs, FP0, Mat0}, {0.0, 0.0}, Front_bspt0, VC_tree1),
3077                    {{FI, F, FZt, Back_bspt, Front_bspt}, VC_tree2}
3078            end
3079
3080		;
3081        back ->
3082            {Back_bspt, VC_tree} =
3083                bspt__insert(FI0, F0, FZt0, Back_bspt0, VC_tree0),
3084            {{FI, F, FZt, Back_bspt, Front_bspt0}, VC_tree}
3085		;
3086        front ->
3087            {Front_bspt, VC_tree} =
3088                bspt__insert(FI0, F0, FZt0, Front_bspt0, VC_tree0),
3089            {{FI, F, FZt, Back_bspt0, Front_bspt}, VC_tree}
3090    end.
3091
3092get_fis(nil, FVIs,_Face_tree) -> FVIs;
3093get_fis(FI, _FVIs, Face_tree) ->
3094    {FVIs, _FP, _Mat} = tree__get(FI, Face_tree), FVIs.
3095
3096bspt__to_list(nil, List) -> List;
3097bspt__to_list({FI, F, _FZt, Back_bspt, Front_bspt}, List) ->
3098    bspt__to_list(Back_bspt, [{FI, F} | bspt__to_list(Front_bspt, List)]).
3099
3100bspt__to_list(Bspt) ->
3101    FEs = bspt__to_list(Bspt, []),
3102    {FEs_rev, _Dup_set} =
3103	lists:foldl(fun
3104			({nil, _F} = FE, {FEs_acc0, Dup_set_acc0}) ->
3105			   {[FE | FEs_acc0], Dup_set_acc0};
3106			({FI, _F} = FE, {FEs_acc0, Dup_set_acc0}) ->
3107			   case gb_sets:is_member(FI, Dup_set_acc0) of
3108			       true ->
3109						%io:format("Removed: FI=~w~n", [FI]),
3110				   {FEs_acc0, Dup_set_acc0}
3111				       ;
3112			       false ->
3113				   Dup_set_acc = gb_sets:insert(FI, Dup_set_acc0),
3114				   {[FE | FEs_acc0], Dup_set_acc}
3115			   end
3116		   end, {[], gb_sets:empty()}, FEs),
3117    lists:reverse(FEs_rev).
3118
3119
3120sign(V) when V > 0.0 -> 1.0;
3121sign(V) when V < 0.0 -> -1.0;
3122sign(V) when V =:= 0.0 -> 0.0.
3123
3124is_convex(FVCs, _Is_FF) when length(FVCs) =:= 3 -> true;
3125is_convex([FVC1 | T], Is_FF) ->
3126    FVC2 = hd(T),
3127
3128    Dir = case Is_FF of true -> 1.0; false -> -1.0 end,
3129    is_convex(sub(FVC2, FVC1), T ++ [FVC1, FVC2], Dir).
3130
3131is_convex(_V12, [_FVC2], _Dir0) -> true;
3132is_convex(V12, [FVC2 | T], Dir0) ->
3133    V23 = sub(hd(T), FVC2),
3134    Dir = sign(cross(V12, V23)),
3135
3136    if
3137        Dir =/= Dir0 ->
3138            false
3139		;
3140        true ->
3141            is_convex(V23, T, Dir)
3142    end.
3143
3144is_flat({FN, FD}, FVCs) ->
3145    EpsD = ?EPS1 * abs(FD),
3146    lists:all(fun(FVC) -> abs(FD - dot(FN, FVC)) < EpsD end, FVCs).
3147