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