var timestart = performance.now(); //var timenow = performance.now(); //console.log("Runtime "+(Math.round( (performance.now()-timenow)*100)/100)+" ms"); /* [ ] beim ansicht wechseln von slab auf plan scale und position beibehalten [ ] wenn slab galerie öffnet scale und position beibehalten [ ] Bei Export den Blending namen mit in den Dateinamen schreiben usage.slab = slab area usage.tickets = alle angeschnittenen slabs mit used area track changes */ // var debug_intersection = false; var debug_snap = false; var small_image = 1000; var large_image = 5000; var hold_shift = true; // enable snapping by default var thickness = true; // check thickness of targets var undosteps = 32; var stroke_width_tickets = 1; // strioke width for tickest in gallery var stroke_width_slab = 1; // strioke width for slab contour var rotax_tolernace = 10; // rotation snap tolerance in degrees var highlight_color = "rgba(20, 115, 230, 0.25)"; var offset_distance_target = (obj.params.offset) / 10 / 2; //cm var offset_distance_contour = 20 / 10; //mm var delete_unused_targets = true; // delete old targets by default var loading = true; var max_scale = 100; var min_scale = 0.001; var color = {white:"#ffffff",red:"#ff342c",yellow:"#febe40",orange:"#ff9933",green:"#12c40c",blue:"#1473e6",gray:"#cccccc",back:"#1d1d1d"}; //green:"#30ca48" // 1d1d1d var slab_padding = 20; var plan_padding = 20; var range_set_regex = new RegExp( ['range','set'].join( "|" ), "i"); var slabs_width = 0; var sources = {}; var user = {clones:{}, layer:{}}; var global_scale = 1; var last_target = null; var params = obj.params;//{h:obj.params.h, mode:obj.params.mode}; var available_targets = []; var plan; var blending_ids = []; var current_plan = $("#load_plan").length>0?parseFloat($("#load_plan").val()):Object.keys(obj["plans"])[0]; //PID var gallery_height = 0; var cart_width = $(".row.cart.set-selector:visible").length>0?158:0; var head_height = 48; var foot_height = 35; var usage = {slab:{}, target:{}, used:{}, plans:{}, tickets:{} }; var gallery_init = false; //delete obj.params; var preloading_complete = false; Konva.showWarnings = false; //Konva.pixelRatio = 1 var lastCenter = null; var lastDist = 0; var rotax_angles = []; var rotationoffset = null; var used_targets = []; var current_target = null; var dropbox; var fakesnapmask = []; var last_dropbox; var last_plan = 0; var lastsave = null; var planstosave = []; var loop_index = 0; var undo_last = 0; var images_to_load = 0; var console_timeout; // disable slow doubletab var doubleTouchStartTimestamp = 0; var touchscreen = false; var mouse_is_down = false; var keep_target = false; var keep_plan = false; var hold_rotation = 0; var hold_rotation_pressed = false; var last_rotationoffset = 0; var undo_user; var snap_source_position = {}; var slab_mode = false; var targetmask = []; var current_set_id = undefined; var back_rotate = {}; var selected_targets = {}; var exchange_slab_pressed = false; // Canvas & Stage $(".loader").show(); $(".loader_info").html("Loading"); $(".loader").css("top",head_height+"px"); $(".loader").css("bottom","0px"); $(".blender_space_"+params.mode).css("margin-left",cart_width+"px"); $(".blender_space_"+params.mode).css("height",window.innerHeight-head_height-foot_height+"px"); if(hold_shift){$("#toggle_snap").css('color', color.red);} // collect available targets for(var plan_id in obj["plans"]) { // clone plans by factor for (var i = 0; i < obj["plans"][plan_id]["factor"]; i++) { if(obj["plans"][plan_id]["factor"] != 1) { var plobject = Object.assign({}, obj["plans"][plan_id]); var inc = "-"+(i+1); plobject.name = plobject.name+inc; obj["plans"][plan_id+inc] = plobject; } } usage["plans"][plan_id] = []; if(obj["plans"][plan_id]["targets"]) { for(var target_id in obj["plans"][plan_id]["targets"]) { if(obj["plans"][plan_id]["targets"][target_id].ref && obj["plans"][plan_id]["targets"][target_id].ref.t) { var target_name = obj["plans"][plan_id]["targets"][target_id].ref.t.replace(/ /g,"_"); available_targets.push( target_name ); usage["plans"][plan_id].push(target_name); if(obj["plans"][plan_id]["targets"][target_id].ref.hasOwnProperty("a")){ usage["target"][obj["plans"][plan_id]["targets"][target_id].ref.t.split(' ').join('_')] = obj["plans"][plan_id]["targets"][target_id].ref.a; } usage["used"][obj["plans"][plan_id]["targets"][target_id].ref.t.split(' ').join('_')] = obj["plans"][plan_id]["targets"][target_id].l; } } } } var stage = new Konva.Stage({ container: 'blender', width: window.innerWidth, height: window.innerHeight-head_height-foot_height, scaleX: 1, scaleY: 1, id: "stage", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); var clip = new Konva.Layer({ scaleX: 1, scaleY: 1, x:0, y:0, id: "clip", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); var transformer = new Konva.Transformer({ resizeEnabled: false, rotateEnabled: false, id: "transformer", borderStroke: color.orange, visible: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); var slabs = new Konva.Layer({ visible: true, id: "slabs", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); var slabs_bg = new Konva.Rect({ width: window.innerWidth-150, height: 0, x: 0, y: 0, fill: "rgba(35, 35, 35, 0.9)", id: "slabs", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, //listening: false }); var slabs_border = new Konva.Rect({ width: window.innerWidth-150,// - 2*slab_padding, height: 4, fill: "#161616", x: 0, y: 3*slab_padding+4, listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); var highlight = new Konva.Line({ // highlight in plan to find selected slab //strokeWidth: 2, //stroke: color.blue, closed: true, id: "highlight", fill: highlight_color, listening: false, visible: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); var scrollLayer = new Konva.Layer({ id: "scrollayer", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); var scrollBar = new Konva.Rect({ width: 120, height: 18, fill: "#888", x: slab_padding, y: 0, draggable: true, id: "scrollbar", cornerRadius: 3, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, dragBoundFunc: function(pos) { pos.x = Math.max( Math.min(pos.x, stage.width()-150 - this.width() - slab_padding) , slab_padding); pos.y = scrollBar.attrs.y; return pos; } }); stage.add(slabs); slabs.add(slabs_bg); slabs.add(slabs_border); slabs_bg.moveToBottom(); stage.add(scrollLayer); scrollLayer.add(scrollBar); stage.add(clip); clip.add(highlight); clip.add(transformer); /*if(params.mode == "view") { slabs.listening(false); }*/ // Init create_slabs(); load_plan(current_plan); var last_plan_position = plan.position(); var last_plan_scale = plan.scale(); //gallery_open(false); slabs_bg.visible(false); slabs_border.visible(false); scrollLayer.visible(false); load_images(); var slab_view_last_target = null; function slab_view_grid(clone) { var px = clone.attrs.x+clone.attrs.clipX; var py = clone.attrs.y+clone.attrs.clipY; var pw = clone.attrs.clipWidth+4; var ph = -clone.attrs.clipHeight+4; for (var h = 0; h < ph; h+=10) { var gridline = new Konva.Line({ points: [px-4,py-h,px+pw+4,py-h], stroke: color.orange, strokeWidth: 0.1, closed: true, listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); plan.add(gridline); var label = new Konva.Text({ x: px-4-2, y: py-h-2, text: h,//slabname, fontSize: 4, fontFamily: 'Verdana', fill: color.orange, align: "right", listening: false, letterSpacing: 0.0001, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); plan.add(label); label.x( label.x()-label.width() ); } for (var w = 0; w < pw; w+=10) { var gridline = new Konva.Line({ points: [px+w,py+4,px+w,py-ph-4], stroke: color.orange, strokeWidth: 0.1, closed: true, listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); plan.add(gridline); var label = new Konva.Text({ x: px+w, y: py+4+2, text: w,//slabname, fontSize: 4, fontFamily: 'Verdana', fill: color.orange, align: "center", listening: false, letterSpacing: 0.0001, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); plan.add(label); label.x( label.x()-label.width()/2 ); } var frame = new Konva.Line({ points: [px-15,py+10,px,py+10], stroke: color.blue, strokeWidth: 0.1, closed: true, visible:false, listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); plan.add(frame); var label = new Konva.Text({ x: px-15, y: py+4+2, text: "", name: "recycle_amount", fontSize: 4, fontFamily: 'Verdana', fill: color.orange, align: "right", //listening: false, letterSpacing: 0.0001, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); plan.add(label); if(params.mode == "recycle") { var ruler_size = 40; var ruler_offset = 20; var ruler_stroke = 0; var ruler = new Konva.Line({ points: [ px, py+ruler_offset, px+pw-4, py+ruler_offset ], stroke: color.orange, strokeWidth: ruler_stroke, name: "recycle_h", closed: true, //listening: false, //perfectDrawEnabled: false, //shadowForStrokeEnabled: false, hitStrokeWidth: ruler_size, }); plan.add(ruler); var ruler = new Konva.Line({ points: [ px, py-ph-ruler_offset+4, px+pw-4, py-ph-ruler_offset+4 ], stroke: color.orange, strokeWidth: ruler_stroke, name: "recycle_h", closed: true, //listening: false, //perfectDrawEnabled: false, //shadowForStrokeEnabled: false, hitStrokeWidth: ruler_size, }); plan.add(ruler); var ruler = new Konva.Line({ points: [ px-ruler_offset, py, px-ruler_offset, py-ph+4], stroke: color.orange, strokeWidth: ruler_stroke, name: "recycle_v", closed: true, //listening: false, //perfectDrawEnabled: false, //shadowForStrokeEnabled: false, hitStrokeWidth: ruler_size, }); plan.add(ruler); var ruler = new Konva.Line({ points: [ px+pw+ruler_offset-4, py, px+pw+ruler_offset-4, py-ph+4], stroke: color.orange, strokeWidth: ruler_stroke, name: "recycle_v", closed: true, //listening: false, //perfectDrawEnabled: false, //shadowForStrokeEnabled: false, hitStrokeWidth: ruler_size, }); plan.add(ruler); } } function slab_view_create(e, selected_slab) { if(!selected_slab.findOne(".used_area")) return; slabs.find(node => { return node.attrs.name == "slabnumber";}).fill(color.white); e.target.fill(color.orange); e.target = selected_slab; slab_mode = true; // TODO $("#toggle_undo").hide(); $("#toggle_redo").hide(); $("#toggle_download").hide(); $("#toggle_layers").hide(); $("#toggle_hide").hide(); $("#toggle_rotation").hide(); show_transformer(false); plan.destroy(); plan = new Konva.Layer({ id: "plan", visible: true, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); // reset selected / grouped targets when switching slabs selected_targets = {} // enable slabs in gallery slabs.find(node => { return node.attrs.name != undefined && node.attrs.name.includes("_group");}).listening(true); stage.add(plan); plan.moveToBottom(); e.target.stopDrag(); var clone = e.target.clone({ instance: 1, rotation: 0, scaleX: 1, scaleY: 1, }); // disable user input on used_areas var used_area_gallery = slabs.find(".used_area"); used_area_gallery.listening(false); // disable slabs in gallery slabs.find(node => { return node.attrs.name != undefined && node.attrs.name.includes("_group");}).listening(false); // mark current slab //var poly = e.target.findOne(node => { return node.attrs.id != undefined && node.attrs.id.includes("_polyline");}); //poly.visible(true); // var used_area = clone.find(".used_area"); used_area.listening(true); used_area.strokeWidth(0.5); used_area.stroke(color.green); used_area.draggable(true); // fires on rotate //used_area.on("transform", function(e) { // slab_view_drag(e); //}); slab_view_grid(clone); fit_to_screen(); clone.draggable(false); if(params.mode == "view") { clone.listening(false); used_area.listening(false); used_area.draggable(false); } plan.add(clone); load_image(clone); // load user info into children for(var i in clone.children) { if(clone.children[i].attrs && clone.children[i].attrs.name == "used_area") { var usc = user.clones[clone.attrs.id][clone.children[i].attrs.inst-1]; clone.children[i].attrs.tmp_offset = {x:usc.source.x-usc.origin.x, y:usc.source.y-usc.origin.y}; if(usc.locked) { clone.children[i].attrs.locked = true; } if(usc.rotation != 0) { usc.slab_set = clone.attrs.id; usc.instance = clone.children[i].attrs.inst-1; back_rotate[usc.target] = usc; //console.log("Add target ",usc); } // trigger user update for each child var child = {}; child.target = clone.children[i]; slab_view_drag(child); } } slab_view_last_target = clone; if(used_area.length>0) { // assign instance for intersection check slab_view_last_target.attrs.instance = used_area[0].attrs.inst; } slab_view_info(clone); } function slab_view_select(e) { if(!slab_view_last_target || e.target.attrs.locked) { // stop dragging locked slabs e.target.stopDrag(); //e.target = e.target.parent; //slab_view_create(e); return; } if(control_down) { if(selected_targets[e.target.attrs.inst]) { delete selected_targets[e.target.attrs.inst]; e.target.fill(null); } else { // update all existing positions on select var all_used = plan.find(node => { return node.attrs.name == "used_area";}); for(var i=0; i { return node.attrs.name == "used_area";}).fill(null); } } batchDraw(plan); // assign instance for intersection check slab_view_last_target.attrs.instance = e.target.attrs.inst; } function slab_view_rotate(e) { //if()id: "transformer_handle" /*var mp = screenpos(stage.getPointerPosition()); var center = transformer.findOne(node => {return node.attrs.id === "transformer_center"}); console.log(center); var angle = Math.atan2(mp.x-center.attrs.x,- (mp.y-center.attrs.y) )*(180/Math.PI); var xxx = new Konva.Circle({ x: mp.x, y: mp.y, radius: 1, fill: color.orange, id: "transformer_center", name: "transformer" }); plan.add(xxx);*/ //console.log(mp,angle); /* var handle = clip.findOne(node => {return node.attrs.name === "rotax"}); // handle knows attrs.target and attrs.clone if(angle_input != null) angle = angle_input; $("#toggle_rotation").css('color', "#eee");// fix hold_rotation hold_rotation = 0; hold_rotation_pressed = false; // rotate handle var rotaxgroup = clip.findOne(node => {return node.attrs.name === "rotaxgroup"}); rotaxgroup.rotation(angle);//transformer.rotation()); // rubberband if(angle_input == null) { var line = clip.findOne(node => {return node.attrs.name === "rotax_line"}); var mp_dist = dist(mp, handle.attrs.target.attrs.centroid); handle.y(-mp_dist); line.attrs.points = [0,0,0,-mp_dist]; }*/ } function slab_view_recycle(e, direction) { plan.find(".split_line").destroy(); batchDraw(plan) var slab = plan.findOne(node => { return node.attrs.name && node.attrs.name.includes("_group") && node.parent.attrs.id == "plan";}); var polyline = []; var tickets = []; for (var ch = 0; ch < slab.children.length; ch++) { if(slab.children[ch].attrs.id && slab.children[ch].attrs.id.includes("_polyline") ) { console.log(slab.children[ch].attrs) //correct child position based on parent position var cp = slab.children[ch].position(); var pp = slab.children[ch].parent.position(); var relative_position = {x:cp.x+pp.x,y:cp.y+pp.y}; var relative_points = rotate_points(slab.children[ch].attrs.points, 0, relative_position); relative_points = skip_points(relative_points); polyline = relative_points; } if(slab.children[ch].attrs.name == "used_area") { //correct child position based on parent position var cp = slab.children[ch].position(); var pp = slab.children[ch].parent.position(); var relative_position = {x:cp.x+pp.x,y:cp.y+pp.y}; var relative_points = rotate_points(slab.children[ch].attrs.points, -slab.children[ch].attrs.rotation, relative_position); relative_points = skip_points(relative_points); tickets.push( relative_points ); } } var px = slab.attrs.x+slab.attrs.clipX; var py = slab.attrs.y+slab.attrs.clipY; var pw = slab.attrs.clipWidth; var ph = -slab.attrs.clipHeight; var mouse_plan = relative_pointer_position(plan); if(direction == "h") { var splitline = new Konva.Line({ points: [px-4,mouse_plan.y,px+pw+4,mouse_plan.y], stroke: color.orange, strokeWidth: 0.5, closed: true, name: "split_line", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 10, draggable: true, opacity:0, polyline: polyline, tickets: tickets, dragBoundFunc: function(pos) { pos.x = splitline.absolutePosition().x; return pos; } }); plan.add(splitline); splitline.startDrag(); } if(direction == "v") { var splitline = new Konva.Line({ points: [mouse_plan.x,py+4,mouse_plan.x,py-ph-4], stroke: color.orange, strokeWidth: 0.5, closed: true, opacity:0, name: "split_line", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 10, draggable: true, polyline: polyline, tickets: tickets, dragBoundFunc: function(pos) { pos.y = splitline.absolutePosition().y; return pos; } }); plan.add(splitline); splitline.startDrag(); } } function slab_view_drag_recycle_line(e) { var slab = plan.findOne(node => { return node.attrs.name && node.attrs.name.includes("_group") && node.parent.attrs.id == "plan";}); //console.log(e) var line = [ e.target.attrs.points[0]+e.target.attrs.x, e.target.attrs.points[1]+e.target.attrs.y, e.target.attrs.points[2]+e.target.attrs.x, e.target.attrs.points[3]+e.target.attrs.y] ; var polyline = e.target.attrs.polyline; var tickets = e.target.attrs.tickets; let recycle_poly = slab_view_split_polygon(line, polyline, tickets, slab); if(recycle_poly) { // find insance in user obj let recycle_instance = 0; for (var i = 0; i < user.clones[slab.attrs.id].length; i++) { if(user.clones[slab.attrs.id][i] == null || user.clones[slab.attrs.id][i].recycle) { recycle_instance = i; break; } } if(!user.recycle) { user.recycle = {}; } if(!user.recycle[slab.attrs.id]) { user.recycle[slab.attrs.id] = { mask:[], area:0 } } user.recycle[slab.attrs.id].mask = recycle_poly.points; user.recycle[slab.attrs.id].area = recycle_poly.area; draw_recycle_polygons(slab.attrs.id) } else { if(user.recycle && user.recycle[slab.attrs.id]) { delete(user.recycle[slab.attrs.id]) } } } function slab_view_split_polygon(line, polyline, tickets, slab) { plan.find(".recycle_area").destroy(); // intersection with polyline polyline = close_polygon(polyline); let poly1 = []; let poly2 = []; let new_poly = []; let intersection_index = 0; for (var i = 0; i < polyline.length; i+=2) { var intersection = segment_intersect(line, [ polyline[i+0], polyline[i+1], polyline[i+2], polyline[i+3] ]); if(intersection_index % 2 != 0){ poly1.push(polyline[i+0]) poly1.push(polyline[i+1]) }else{ poly2.push(polyline[i+0]) poly2.push(polyline[i+1]) } if(intersection){ intersection_index++; } if(intersection){ poly1.push(intersection.x) poly1.push(intersection.y) poly2.push(intersection.x) poly2.push(intersection.y) } } var poly1_offset = offset_points(poly1, offset_distance_target*2); var poly2_offset = offset_points(poly2, offset_distance_target*2); if(intersection_index == 0) plan.find(".split_line").opacity(0) else plan.find(".split_line").opacity(1) // break if more than 2 intersectoions with polyline is found if(intersection_index > 2) return false; // get best polygon let poly1_valid = true; let poly2_valid = true; // intersection with tickets for (var k = 0; k < tickets.length; k++) { tickets[k] = close_polygon(tickets[k]); for (var i = 0; i < tickets[k].length; i+=2) { if(point_in_polygon( tickets[k][i+0], tickets[k][i+1] , poly1_offset) == "inside") poly1_valid = false; if(point_in_polygon( tickets[k][i+0], tickets[k][i+1] , poly2_offset) == "inside") poly2_valid = false; } } let recycle_poly = []; if(poly1_valid) { recycle_poly = poly1; } if(poly2_valid) { recycle_poly = poly2; } let info1 = shapeinfo(recycle_poly); if(recycle_poly.length > 0) { let slabname_node = slabs.findOne(node => { return node.attrs.name != undefined && node.attrs.name == "slabnumber" && node.attrs.id == slab.attrs.id;}); if(slabname_node) { let output = slabname_node.text().substring(0, slabname_node.text().indexOf('\n') + 1); output += "+"+(info1.area/10000).toFixed(1)+"m²"; slabname_node.text(output); } var xxx = new Konva.Line({points: recycle_poly, fill: color.green, opacity: 0.5, stroke: color.orange, strokeWidth: 0.5, listening: false, name: "recycle_area", closed: true}); plan.add(xxx); } //var slab = slabs.findOne("."+e.target.parent.attrs.name); var gallery_slab = slabs.findOne("#"+slab.attrs.id); var prototype1 = gallery_slab.findOne(".used_area"); var prototype2 = plan.findOne(".used_area"); //correct child position based on parent position var cp = slab.children[1].position(); var pp = slab.children[1].parent.position(); var relative_position = {x:cp.x-pp.x,y:cp.y-pp.y}; var relative_points = rotate_points(recycle_poly, 0, relative_position); if(relative_points.length > 0) { relative_points = skip_points(relative_points); //rclone.points(recycle_poly); batchDraw(slabs) return {points:relative_points, area:info1.area}; } return false; } function reload_recycle_polygons() { for(var sid in user.recycle) { draw_recycle_polygons(sid) } } function draw_recycle_polygons(slab_id = null) { plan.find(".recycle_"+slab_id).destroy(); slabs.find(".recycle_"+slab_id).destroy(); batchDraw(plan) // draw poly on gallery slab var gallery_slab = slabs.findOne("#"+slab_id); if(gallery_slab) { var recycle_poly = user.recycle[slab_id].mask; var recycle_ticket = new Konva.Line({ points: user.recycle[slab_id].mask, stroke: color.orange, opacity: 0.5, fill: color.green, strokeWidth: 0.5, name: "recycle_"+slab_id, closed: true }); gallery_slab.add(recycle_ticket); } } function slab_view_mousedown(e) { if(params.mode == "recycle") { if(e.target.attrs.name && e.target.attrs.name == "recycle_h") { slab_view_recycle(e, "h"); return; } if(e.target.attrs.name && e.target.attrs.name == "recycle_v") { slab_view_recycle(e, "v"); return; } } // load big slab image to plan if(e.target.attrs.name && e.target.attrs.name.includes("slab_") && e.target.attrs.instance == 0) // remove instance to click on slab in plan { //selected_targets = {}; //plan.find(node => { return node.attrs.name == "used_area";}).fill(null); //slab_view_create(e); } else if(e.target.attrs.name && e.target.attrs.name == "used_area") { slab_view_select(e); } else if( e.target.attrs.id == "stage" || (e.target.attrs.name && e.target.attrs.name.includes("slab_")) )// drag plan { selected_targets = {}; plan.find(node => { return node.attrs.name == "used_area";}).fill(null); plan.startDrag(); } } function slab_view_mouseup(e) { plan.find(node => {return node.attrs.name === "snap_line"}).destroy(); last_target = slab_view_last_target; } function slab_view_drag(e) { var position = e.target.position(); var rotation = e.target.rotation(); var slab = slabs.findOne("."+e.target.parent.attrs.name); var used = slab.findOne("#"+e.target.attrs.id); //move together if(selected_targets[e.target.attrs.inst]) { var self_position = selected_targets[e.target.attrs.inst].attrs.tmp_position; for(var i in selected_targets) { // skip self if(e.target.attrs.inst == selected_targets[i].attrs.inst) { self_position = selected_targets[i].attrs.tmp_position; continue; } var other = plan.findOne(node => {return node.attrs.inst === selected_targets[i].attrs.inst}); var other_position = { x:position.x+selected_targets[i].attrs.tmp_position.x-selected_targets[e.target.attrs.inst].attrs.tmp_position.x, y:position.y+selected_targets[i].attrs.tmp_position.y-selected_targets[e.target.attrs.inst].attrs.tmp_position.y }; var other_rotation = selected_targets[i].attrs.rotation; other.position(other_position); var other_used = slab.findOne("#"+selected_targets[i].attrs.id); other_used.position(other_position); other_used.rotation(other_rotation); var other_rotated_position = rotate_points([other_position.x, other_position.y], other_rotation); var other_object = user.clones[e.target.attrs.slab][selected_targets[i].attrs.inst-1]; other_object.position.x = selected_targets[i].attrs.tmp_offset.x - other_rotated_position[0]; other_object.position.y = selected_targets[i].attrs.tmp_offset.y - other_rotated_position[1]; var other_mask = rotate_points(other_used.attrs.points, -other_used.attrs.rotation, other_used.position()); other_mask_offset = offset_points(other_mask, offset_distance_target); other_object.mask = other_mask; other_object.maskoffset = other_mask_offset; other_object.centroid = polygon_centroid(other_mask);//e.target.attrs.tmp_offset.y - rotated_position[1]; } } used.position(position); used.rotation(rotation); var rotated_position = rotate_points([position.x, position.y], rotation); var object = user.clones[e.target.attrs.slab][e.target.attrs.inst-1]; object.position.x = e.target.attrs.tmp_offset.x - rotated_position[0]; object.position.y = e.target.attrs.tmp_offset.y - rotated_position[1]; var mask = rotate_points(used.attrs.points, -used.attrs.rotation, used.position()); mask_offset = offset_points(mask, offset_distance_target); object.mask = mask; object.maskoffset = mask_offset; object.centroid = polygon_centroid(mask);//e.target.attrs.tmp_offset.y - rotated_position[1]; //e.target.attrs.mask_offset = mask_offset; //plan.find(node => {return node.attrs.name === "snap_line"}).destroy(); /*var simple = mask; simple = move_points(mask, e.target.parent.position()); simple = offset_points(simple, offset_distance_target); simple = skip_points(simple); for (var i = 0; i < simple.length; i+=2) { var xxx = new Konva.Circle({ x: simple[i], y: simple[i+1], radius: 0.4, fill: color.blue, name: "snap_line", }); plan.add(xxx); } var xxx = new Konva.Line({ points: simple, stroke: color.blue, strokeWidth: 0.2, name: "snap_line", });plan.add(xxx);*/ if(hold_shift) { if(e.type == "dragmove") { slab_view_snap(e, slab, used, object); } if(e.type == "transform") { console.log("slab_view_drag transform"); } } batchDraw(slabs); batchDraw(plan); //batchDraw(clip); } function slab_view_snap(e, slab, used, object) { plan.find(node => {return node.attrs.name === "snap_line"}).destroy(); var children = []; for (var ch = 0; ch < slab.children.length; ch++) { if(slab.children[ch].attrs.name == "used_area") { // exclude self if(slab.children[ch].attrs.id == used.attrs.id) continue; if(selected_targets[slab.children[ch].attrs.inst]) continue; // correct child position based on parent position var cp = slab.children[ch].position(); var pp = slab.children[ch].parent.position(); var child_position = {x:cp.x+pp.x,y:cp.y+pp.y}; var child = rotate_points(slab.children[ch].attrs.points, -slab.children[ch].attrs.rotation, child_position); child = skip_points(child); if(debug_snap){ var xxx = new Konva.Line({points: child,stroke: color.blue,strokeWidth: 0.1,name: "snap_line",closed: true,});plan.add(xxx); } child = offset_points(child, offset_distance_target); if(debug_snap){ var xxx = new Konva.Line({points: child,stroke: color.blue,strokeWidth: 0.1,name: "snap_line",closed: true,});plan.add(xxx); } children.push( child ); } } var active = object.mask;//e.target.attrs.mask_offset; active = skip_points(active); active = move_points(active, e.target.parent.position()); if(debug_snap){ var xxx = new Konva.Line({points: active,stroke: color.blue,strokeWidth: 0.1,name: "snap_line",closed: true,});plan.add(xxx); } active = offset_points(active, offset_distance_target); if(debug_snap){ var xxx = new Konva.Line({points: active,stroke: color.blue,strokeWidth: 0.1,name: "snap_line",closed: true,});plan.add(xxx); } smallest = snap_lines(active, children, 3); if(smallest.delta) { if(smallest.line1) { var xxx = new Konva.Line({ points: smallest.line1, stroke: color.blue, strokeWidth: 0.2, name: "snap_line", listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, });plan.add(xxx); } if(smallest.line2) { var xxx = new Konva.Line({ points: smallest.line2, stroke: color.blue, strokeWidth: 0.2, name: "snap_line", listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, });plan.add(xxx); var xxx = new Konva.Circle({ x: smallest.cross.x, y: smallest.cross.y, radius: 0.4, fill: color.blue, name: "snap_line", listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); plan.add(xxx); } var position = {"x":used.attrs.x - smallest.delta.x,"y":used.attrs.y - smallest.delta.y,}; var rotation = e.target.rotation(); // snap together if(selected_targets[e.target.attrs.inst]) { var self_position = selected_targets[e.target.attrs.inst].attrs.tmp_position; for(var i in selected_targets) { // skip self if(e.target.attrs.inst == selected_targets[i].attrs.inst) { self_position = selected_targets[i].attrs.tmp_position; continue; } var other = plan.findOne(node => {return node.attrs.inst === selected_targets[i].attrs.inst}); var other_position = { x:position.x+selected_targets[i].attrs.tmp_position.x-selected_targets[e.target.attrs.inst].attrs.tmp_position.x, y:position.y+selected_targets[i].attrs.tmp_position.y-selected_targets[e.target.attrs.inst].attrs.tmp_position.y }; var other_rotation = selected_targets[i].attrs.rotation; other.position(other_position); var other_used = slab.findOne("#"+selected_targets[i].attrs.id); other_used.position(other_position); other_used.rotation(other_rotation); var other_rotated_position = rotate_points([other_position.x, other_position.y], other_rotation); var other_object = user.clones[e.target.attrs.slab][selected_targets[i].attrs.inst-1]; other_object.position.x = selected_targets[i].attrs.tmp_offset.x - other_rotated_position[0]; other_object.position.y = selected_targets[i].attrs.tmp_offset.y - other_rotated_position[1]; var other_mask = rotate_points(other_used.attrs.points, -other_used.attrs.rotation, other_used.position()); other_mask_offset = offset_points(other_mask, offset_distance_target); other_object.mask = other_mask; other_object.maskoffset = other_mask_offset; other_object.centroid = polygon_centroid(other_mask);//e.target.attrs.tmp_offset.y - rotated_position[1]; } } used.position(position); e.target.position(position); var rotated_position = rotate_points([position.x, position.y], rotation); var object = user.clones[e.target.attrs.slab][e.target.attrs.inst-1]; object.position.x = e.target.attrs.tmp_offset.x - rotated_position[0]; object.position.y = e.target.attrs.tmp_offset.y - rotated_position[1]; var mask = rotate_points(used.attrs.points, -used.attrs.rotation, used.position()); var mask_offset = offset_points(sort_polygon(mask), offset_distance_target); object.mask = mask; object.maskoffset = mask_offset; object.centroid = polygon_centroid(mask);//e.target.attrs.tmp_offset.y - rotated_position[1]; batchDraw(slabs); batchDraw(plan); //batchDraw(clip); } } function snap_lines(active, children, snap_dist) { var lines = []; var smallest = {}; for (var i = 0; i < active.length; i+=2) { for (var ch = 0; ch < children.length; ch++) { var child = children[ch]; // loop all child points for (var tp = 0; tp < child.length; tp+=2) { var p1 = {"x":child[tp+0],"y":child[tp+1]}; if(child[tp+3]){ var p2 = {"x":child[tp+2],"y":child[tp+3]}; } else{ var p2 = {"x":child[0],"y":child[1]}; } var snap_line = extend_line(p1, p2, 4); var pro = project( {x:active[i+0],y:active[i+1]} , snap_line ); if(pro.dist < snap_dist) { var in_array = lines.some(element => { if (element[0] === snap_line[0] && element[1] === snap_line[1] && element[2] === snap_line[2] && element[3] === snap_line[3]) { return true; }return false; }); if(!in_array) { lines.push(snap_line); } if(!smallest.line1) { smallest.line1 = snap_line; smallest.delta = {"x":active[i+0]-pro.point.x, "y":active[i+1]-pro.point.y}; } else { // find closest target point to cross var min_dist = null; for (var il = 0; il < lines.length; il++) { var cross = line_intersect(lines[il], snap_line); var angle = line_angle(lines[il], snap_line); if(cross!=null && angle%180 > 15) { for (var pn = 0; pn < active.length; pn+=2) { // get closest snapping point if in snapping distance var dist = distance(cross.x, cross.y, active[pn+0], active[pn+1]); if( dist < snap_dist && (dist { return node.attrs.name != undefined && node.attrs.name.includes("_group");}).listening(true); $(".loader").show(); $(".loader_info").html("Loading Plan"); slab_view_rotate_back(); // TODO $("#toggle_undo").show(); $("#toggle_redo").show(); $("#toggle_download").show(); $("#toggle_layers").show(); $("#toggle_hide").show(); $("#toggle_rotation").show(); selected_targets = []; slab_mode = false; mouse_is_down = false; slabs.find(node => { return node.attrs.name == "slabnumber";}).fill(color.white); slab_view_info(); slab_view_last_target = null; if(reload) { load_plan(current_plan); reload_clones(); } } function create_clone(e) { // overlapping tickets if(e.target.attrs && e.target.attrs.draggable == false) return; e.target.stopDrag(); // create user clone if(!user.clones[e.target.attrs.id]) user.clones[e.target.attrs.id] = Array(); user.clones[e.target.attrs.id].push({ target: undefined, rotation:hold_rotation, position: {}, mask: Array() }); var clon_instance = user.clones[e.target.attrs.id].length; // increment instance of clone var clone = e.target.clone({ instance: clon_instance, rotation: hold_rotation, scaleX: 1, scaleY: 1, //opacity: 0.5 }); // set opacity of range sets to 1 if(range_set_regex.test( e.target.attrs.set_id )) { clone.opacity(1); } // grab clone var mouse_target = relative_pointer_position(e.target); var mouse_plan = relative_pointer_position(plan); // fix grab position if clone is rotated var rotated = rotate_point( mouse_target, Konva.getAngle(hold_rotation)); var dx = rotated.x - mouse_target.x; var dy = rotated.y - mouse_target.y; // increment instance of children clone.find(node => { //if(node.attrs.instance != clon_instance) node.attrs.instance = clon_instance; clone.attrs.layer = "100-BLENDING"; clone.attrs.x = mouse_plan.x-mouse_target.x-dx; clone.attrs.y = mouse_plan.y-mouse_target.y-dy; return false; }); // used areas on plan must always be red var used_area = clone.find(".used_area"); used_area.stroke(color.red); used_area.listening(false); used_area.draggable(false); clip.add(clone); load_image(clone); /*if(mirror) { var scale = -clone.children[0].attrs.fillPatternScaleX; var width = scale<0?-clone.attrs.clipWidth:0; clone.children[0].setAttr("fillPatternScaleX", scale); clone.children[0].setAttr("fillPatternOffsetX", width); clone.draw(); }*/ clone.startDrag(); clone.moveToTop(); last_target = clone; } function intersection_check_all() { /* BUG: Tickest deleted by intersection check because centroid changes when reloading plan var used_slabs = plan.find(node => { return node.attrs.name && node.attrs.name.includes("_group");}); console.log(used_slabs); var used_slabs_array = []; for (var i = 0; i < used_slabs.length; i++) { if(!used_slabs_array.includes(used_slabs[i].attrs.id)) { intersection_check(used_slabs[i]); used_slabs_array.push(used_slabs[i].attrs.id); } }*/ } function clean_user_object() { for(var slab_id in user.clones) { for(var instance in user.clones[slab_id]) { if(user.clones[slab_id][instance] && !user.clones[slab_id][instance].target) { console.log("Clean user Object") delete user.clones[slab_id][instance]; } } } } function intersection_check(master = null) { clean_user_object() //console.log("intersection_check", intersection_check.caller.name) if(debug_intersection) { plan.find(node => {return node.attrs.name === "debug_intersection"}).destroy(); } // disable intersection check for range_set /* if(last_target && last_target.attrs && last_target.attrs.set_id && range_set_regex.test( source.attrs.set_id )) { return; } */ // only check current slab (last_target) if(master) // initial intersection check after loading complete last_target = master; if(last_target && last_target.attrs && last_target.attrs.name && last_target.attrs.name.includes("slab_")) { var slab_id = last_target.attrs.id; var instance = last_target.attrs.instance-1; if(instance == -1) return; if(!user.clones[slab_id] || !user.clones[slab_id][instance] || user.clones[slab_id][instance].target == undefined) { // destroy clone, when dragged out of target destroy_clone(slab_id, instance, last_target); // find master from last_target after destroy and redo intersection check var tmp_master = plan.findOne(node => { return node.attrs.id && node.attrs.id == slab_id;}); if(tmp_master) { console.log("redo intersection check") intersection_check(tmp_master); } return; } var contour = last_target.findOne(node => { return node.attrs.id != undefined && node.attrs.id.includes("_polyline");}); if(contour && contour.attrs && contour.attrs.margin && user.clones[slab_id] && user.clones[slab_id][instance] && user.clones[slab_id][instance].mask) { contour = contour.attrs.margin; var rotation = last_target.attrs.rotation; var ticket_offsets = {}; var centroids = {}; for(var index in user.clones[slab_id]) { var clone = user.clones[slab_id][index]; if(!clone) continue; clone.ic = {"contour":0,"ticket":0};//,"centroid":0}; var mask_offset = []; var mask_offset_rounded = offset_points_rounded(clone.mask, offset_distance_target-0.05) if(!slab_mode) { for (var i = 0; i < mask_offset_rounded.length; i += 2) { var x = mask_offset_rounded[i]; var y = mask_offset_rounded[i+1]; var r = -clone.rotation * (Math.PI / 180); // ROTI var xr = x*Math.cos(r) - y*Math.sin(r); var yr = x*Math.sin(r) + y*Math.cos(r); // store rotated points mask_offset.push( xr ); mask_offset.push( yr ); } } else { for (var i = 0; i < mask_offset_rounded.length; i += 2) { var x = mask_offset_rounded[i]; var y = mask_offset_rounded[i+1]; // store rotated points mask_offset.push( x ); mask_offset.push( y ); } } if(mask_offset.length == 0) continue; ticket_offsets[index] = mask_offset; centroids[index] = polygon_centroid(mask_offset); intersection = point_in_polygon(centroids[index].x, centroids[index].y, contour); if(intersection == "outside") { //clone.ic.centroid++; if(slab_mode) { var trash = plan.findOne(node => {return node.attrs.inst === Number(index)+1}); } else { var trash = plan.findOne(node => { return node.attrs.instance == Number(index)+1 && node.attrs.name == "slab_"+slab_id+"_group";}); } destroy_clone(slab_id, index, trash); } // check intersection with contour of all tickets for(var i = 0; i < mask_offset.length; i+=2) { intersection = point_in_polygon(mask_offset[i], mask_offset[i+1], contour); if(intersection == "outside") { clone.ic.contour++; } if(debug_intersection) { // contour var xxx = new Konva.Line({ points: contour, stroke: color.white, strokeWidth: 0.2, name: "debug_intersection", }); plan.add(xxx); if(intersection == "outside") { // point outside contour var xxx = new Konva.Circle({ x: mask_offset[i], y: mask_offset[i+1], radius: 1, fill: color.orange, name: "debug_intersection", }); plan.add(xxx); } } } } // ticket intersection check for(var i1 in user.clones[slab_id]) // loop all clones in slab { if(!user.clones[slab_id][i1]) continue; // filter null clones //rotate_points([user.clones[slab_id][i1].centroid.x, user.clones[slab_id][i1].centroid.y], rotation); if(debug_intersection) { var xxx = new Konva.Line({ points: ticket_offsets[i1], stroke: color.blue, strokeWidth: 0.2, name: "debug_intersection", }); plan.add(xxx); /*// cntroid var xxx = new Konva.Circle({ x: centroids[i1].x, y: centroids[i1].y, radius: 1, fill: color.red, name: "debug_intersection", }); plan.add(xxx);*/ } for(var i2 in user.clones[slab_id]) // compare each tickets but self { if(!user.clones[slab_id][i2] || i1 == i2) continue; // filter null clones and self if(centroids[i1].x == centroids[i2].x && centroids[i1].y == centroids[i2].y) { user.clones[slab_id][i1].ic.ticket++; user.clones[slab_id][i2].ic.ticket++; } for(var i = 0; i < ticket_offsets[i1].length; i+=2) { var intersection = point_in_polygon(ticket_offsets[i1][i], ticket_offsets[i1][i+1], ticket_offsets[i2]); if(intersection == "inside") { user.clones[slab_id][i1].ic.ticket++; user.clones[slab_id][i2].ic.ticket++; if(debug_intersection) { var xxx = new Konva.Circle({ x: ticket_offsets[i1][i], y: ticket_offsets[i1][i+1], radius: 1, fill: color.red, name: "debug_intersection", }); plan.add(xxx); } } } } } } } } function intersection_marker() { var tickets = slabs.find(".used_area").fill(null); var used_area = plan.find(".used_area"); if(slab_mode) { used_area.listening(true); for (var i = 0; i < used_area.length; i++) { tickets.push(used_area[i]); } } else { // colorize contour on plan red used_area.listening(false); used_area.stroke(color.red); used_area.fill(null); if(offset_distance_target == 0) { used_area.visible(false); } } for(var ticket of tickets) { if(user.clones[ticket.attrs.slab] && user.clones[ticket.attrs.slab][ticket.attrs.inst-1]) { var intersections = user.clones[ticket.attrs.slab][ticket.attrs.inst-1].ic; if(intersections && (intersections.contour > 0 || intersections.ticket > 0)) { ticket.stroke(color.red); } else { ticket.stroke(color.green); } } else { ticket.destroy(); } } } function snap_object(source) { if(dropbox != null && source != null && hold_shift)//target != null && target.attrs.id == "target") // && more than one clone { var smallest = {}; snap_source_position = {}; if(!source.snap_offset) source.snap_offset = {"x":0, "y":0}; var slab_id = source.attrs.id; var instance = source.attrs.instance-1; // we need self to snap on to (obsolete) // this is needed to snap if targei is the same sitze as the slab var tmp_targetmask = { "mask":targetmask, "rotation":userClone(source).targetrotation }; user.clones[slab_id].push(tmp_targetmask); var children = []; for(var index in user.clones[slab_id]) { if(index == instance) continue;// loop other clones var clone = user.clones[slab_id][index]; if(!clone) continue;// filter null clones var child = clone.mask; child = skip_points(child); child = offset_points(child, offset_distance_target); child = rotate_points(child, clone.rotation); children.push( child ); } var active = user.clones[slab_id][instance].mask; active = skip_points(active); active = offset_points(active, offset_distance_target); active = rotate_points(active, user.clones[slab_id][instance].rotation); smallest = snap_lines(active, children, 10); // if we found a nearest snap target point if(smallest.delta) { var drx = rotate_points([source.attrs.x, source.attrs.y], user.clones[slab_id][instance].rotation); drx[0] += smallest.delta.x; drx[1] += smallest.delta.y; var dr = rotate_points([drx[0],drx[1]], -user.clones[slab_id][instance].rotation); source.position({"x":dr[0], "y":dr[1]}); snap_source_position = source.position(); // fake object to display on slab var sourcemask = rotate_points(user.clones[slab_id][instance].mask, user.clones[slab_id][instance].rotation); fakesnapmask = []; for (var i = 0; i < sourcemask.length; i += 2) { fakesnapmask.push(sourcemask[i]-smallest.delta.x); fakesnapmask.push(sourcemask[i+1]-smallest.delta.y); } fakesnapmask = rotate_points(fakesnapmask, -user.clones[slab_id][instance].rotation); } // pop self (obsolete) //user.clones[slab_id].pop(); } } function rotax_move(angle_input) { if(rotationoffset != null) { var mp = screenpos(stage.getPointerPosition()); var handle = clip.findOne(node => {return node.attrs.name === "rotax"}); // handle knows attrs.target and attrs.clone var angle = Math.atan2(mp.x-handle.attrs.target.attrs.centroid.x,- (mp.y-handle.attrs.target.attrs.centroid.y) )*(180/Math.PI); if(angle_input != null) angle = angle_input; angle = Math.round(angle*100)/100; $("#toggle_rotation").css('color', "#eee");// fix hold_rotation hold_rotation = 0; hold_rotation_pressed = false; // rotate handle var rotaxgroup = clip.findOne(node => {return node.attrs.name === "rotaxgroup"}); rotaxgroup.rotation(angle);//transformer.rotation()); // rubberband if(angle_input == null) { var line = clip.findOne(node => {return node.attrs.name === "rotax_line"}); var mp_dist = dist(mp, handle.attrs.target.attrs.centroid); handle.y(-mp_dist); line.attrs.points = [0,0,0,-mp_dist]; } if(hold_shift) { //transformer.borderStroke("#0f0"); for (var i = 0; i < rotax_angles.length; i++) { //if( angle > rotax_angles[i]-rotax_tolernace && angle < rotax_angles[i]+rotax_tolernace ) // angle = rotax_angles[i]; if(-angle > rotax_angles[i]-rotax_tolernace && -angle < rotax_angles[i]+rotax_tolernace ) angle = -rotax_angles[i]; } } if(slab_mode) { console.log("new"); }else{ rotate_around_center(handle.attrs.clone, handle.attrs.target, angle); } last_target = handle.attrs.clone; last_angle = angle; batchDraw(clip); batchDraw(plan); } } function rotate_around_center(source, target, rotation) { var origin = last_rotationoffset; var current = rotate_point(origin, Konva.getAngle(source.rotation())); var rotated = rotate_point(origin, Konva.getAngle(rotation)); var dx = rotated.x - current.x; var dy = rotated.y - current.y; source.rotation(rotation); source.x(source.x() + dx); source.y(source.y() + dy); userClone(source).position = {x:(source.attrs.x-target.attrs.orig.x), y:(source.attrs.y-target.attrs.orig.y)}; userClone(source).centroid = {x:-(source.attrs.x-target.attrs.centroid.x),y: -(source.attrs.y-target.attrs.centroid.y)}; userClone(source).rotation = source.attrs.rotation; userClone(source).source = {x:source.attrs.x, y:source.attrs.y}; if(rotation > 180) rotation -= 360; print("rotate "+ rotation); } function create_slabs() { for (var set_id in obj["slabs"]) { var names = Array(); for (var slab_id in obj["slabs"][set_id]) { if(slab_id == "setname" || slab_id == "thickness") continue; names.push(obj["slabs"][set_id][slab_id]); } //names.sort(); for (var key = 0; key < names.length; key++) { for (var slab_id in obj["slabs"][set_id]) { if(slab_id == "setname" || slab_id == "thickness") { continue; } if(obj["slabs"][set_id][slab_id] == names[key]){ var slabobj = obj["slabs"][set_id][slab_id]; // create sources rray for all required images sources[slab_id] = { "thumb":obj["slabs"][set_id][slab_id]["thumb"], "image":obj["slabs"][set_id][slab_id]["image"] }; var slab = new Konva.Group({ draggable: true, id: slab_id, set_id: set_id, name: "slab_"+slab_id+"_group", instance: 0, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); if(params.mode == "inspect") slab.draggable(false); for (var k in slabobj) { if (k == "BOX") { var box = new Konva.Rect({ x: slabobj["BOX"][0], y: -slabobj["BOX"][1], width: slabobj["BOX"][2]-slabobj["BOX"][0], height: -(slabobj["BOX"][3]-slabobj["BOX"][1]), id: slab_id, set_id: set_id, name: "slab_"+slab_id+"_imagebox", instance: 0, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); // clip used area => origin of slab slab.attrs.clipX = box.attrs.x; slab.attrs.clipY = box.attrs.y; slab.attrs.clipWidth = box.attrs.width; slab.attrs.clipHeight = box.attrs.height; slab.add(box); // slab area usage["slab"][slab_id] = slabobj["BOX"][4]; } if (k == "POLYLINE") { // loop all available polylines in slab for (var i in slabobj[k]) { if(slabobj[k][i]["kp"] && slabobj[k][i]["kp"].length > 8 && (slabobj[k][i]["l"] == "Contour" || slabobj[k][i]["contour"])) { var margin_polygon = close_polygon(convert_kp(createMarginPolygon(createPolygon(convert_xy(sort_polygon(slabobj[k][i]["kp"]))), offset_distance_contour-offset_distance_target).vertices)); // polyline Contour var polyline = new Konva.Line({ points: slabobj[k][i]["kp"], margin: margin_polygon, strokeWidth: stroke_width_slab, // Stroke red color on plan stroke: color.red, closed: true, id: slab_id+"_polyline", listening: false, visible: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); slab.add(polyline); // draw netto area in Slab if(slabobj.netto && slabobj.netto.rect) { var testrect = new Konva.Line({ points: slabobj.netto.rect, strokeWidth: 1, stroke: "#f00", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); slab.add(testrect); } } if(slabobj[k][i]["l"] == "Defects") { // polyline Contour var defect = new Konva.Line({ points: slabobj[k][i]["kp"], strokeWidth: stroke_width_slab, stroke: color.blue, id: slab_id+"_defect", listening: false, visible: true, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); slab.add(defect); } } if(obj["slabs"][set_id][slab_id].locked) { slab.attrs.locked = true; //polyline.visible(true); slab.opacity(0.5); } //if(params.mode == "view") //{ // slab.listening(false); //} } } // fires on rotate /*slab.on("transform", function(e) { // fix transformer scale bug transformer._nodes[0].attrs.scaleX = 1; transformer._nodes[0].attrs.scaleY = 1; transformer_colorizesnap() //chg(true); var target = plan.findOne("."+userClone(e.currentTarget).target); // e.currentTarget = clone group // userClone(e.currentTarget).target = target name ( B-12 ) // target = target polylinbe on plan // transformer._nodes[0] = clone group var clone_x = e.currentTarget.attrs.x; var clone_y = e.currentTarget.attrs.y; var target_x = target.attrs.orig.x; var target_y = target.attrs.orig.y; userClone(e.currentTarget).position = {x:(clone_x-target_x), y:(clone_y-target_y)}; userClone(transformer._nodes[0]).centroid = [-(transformer._nodes[0].attrs.x-target.attrs.centroid.x), -(transformer._nodes[0].attrs.y-target.attrs.centroid.y)]; batchDraw(plan); });*/ // clip slabs by polyline in gallery slab.clipFunc(function(ctx) { var cp = this.findOne(node => { return node.attrs.id != undefined && node.attrs.id.includes("_polyline") && node.attrs.visible == false;}); if(cp && cp.attrs && cp.attrs.points) { cp = cp.attrs.points; ctx.beginPath(); for (var xxx = 0; xxx < cp.length; xxx += 2) { ctx.lineTo( cp[xxx], cp[xxx+1]); } ctx.closePath(); } }); if(slabobj["BOX"]) { slab.y((slabobj["BOX"][1])+slab_padding/2); } slab.visible(false); slabs.add(slab); } } } } } function gallery_open(set_id, toggle = false) { // dont show gallery in inspect mode if(params.mode == "inspect") { slabs_bg.visible(false); slabs_border.visible(false); scrollLayer.visible(false); return; } $(".cart .slab").removeClass("active"); $(".set_title").hide(); slabs_width = 0; gallery_height = 0; if(current_set_id == set_id && toggle) current_set_id = undefined; else current_set_id = set_id; // reset slabs.find(node => { return node.attrs.name != undefined && node.attrs.name.includes("_group");}).visible(false); slabs.find(node => { return node.attrs.name != undefined && node.attrs.name == "slabnumber";}).destroy(); scrollLayer.visible(false); slabs.visible(false); slabs.x(0); scrollBar.x(slab_padding); if(obj["slabs"] && obj["slabs"].hasOwnProperty(current_set_id)) { var names = Array(); for (var slab_id in obj["slabs"][set_id]) { if(obj["slabs"][set_id][slab_id].name) { /*// Take last numbers and characters for name var old_name = obj["slabs"][set_id][slab_id].name; var name_parts = []; var new_name = ""; for (var i = 0; i < old_name.length; i++) { if(!Boolean(old_name[i].match(/^[A-Za-z0-9]*$/))) { name_parts.push(new_name); new_name = ""; } else { new_name += old_name[i]; } } if(new_name == "") { for(var i = name_parts.length-1; i > 0; i--) { if(name_parts[i] != "") { new_name = name_parts[i]; break; } } } obj["slabs"][set_id][slab_id].name = new_name.replace(/^0+/, '');*/ names.push(obj["slabs"][set_id][slab_id]); } } // SORT APLHANUMERIC BY PROP var reA = /[^a-zA-Z]/g; var reN = /[^0-9]/g; function sortAlphaNum(a,b) { var AInt = parseInt(a.name, 10); var BInt = parseInt(b.name, 10); if(isNaN(AInt) && isNaN(BInt)){ var aA = a.name.replace(reA, ""); var bA = b.name.replace(reA, ""); if(aA === bA) { var aN = parseInt(a.name.replace(reN, ""), 10); var bN = parseInt(b.name.replace(reN, ""), 10); return aN === bN ? 0 : aN > bN ? 1 : -1; } else { return aA > bA ? 1 : -1; } }else if(isNaN(AInt)){//A is not an Int return 1;//to make alphanumeric sort first return -1 here }else if(isNaN(BInt)){//B is not an Int return -1;//to make alphanumeric sort first return 1 here }else{ return AInt > BInt ? 1 : -1; } } names.sort(sortAlphaNum); var gscale = 0; for (var key = 0; key < names.length; key++) { for (var slab_id in obj["slabs"][current_set_id]) { if(slab_id == "setname" || slab_id == "thickness") { continue; } // only display used slabs when opening gallery in recycle mode if(params.mode == "recycle" && !usage.tickets.hasOwnProperty(slab_id)) { continue; } if(obj["slabs"][set_id][slab_id] == names[key]) { var slabobj = obj["slabs"][current_set_id][slab_id]; if(!slabobj["BOX"]) continue; if(gscale == 0) { // scale gallery height to 1/6 of stage height //gscale = stage.height() / (slabobj["BOX"][3]-slabobj["BOX"][1]) / 6; // BUGGY gscale = 1; } let s_width = (slabobj["BOX"][2] - slabobj["BOX"][0]); let s_x = l_x = -slabobj["BOX"][0]*gscale + slabs_width + slab_padding; if(s_width < 200){ s_x += 100-(s_width/2); s_width = 200; } var current_slab = slabs.findOne(".slab_"+slab_id+"_group"); current_slab.x(s_x); current_slab.y( ((slabobj["BOX"][1])+slab_padding/2)*gscale ); current_slab.visible(true); current_slab.scaleX(gscale); current_slab.scaleY(gscale); // Abstand slabs_width += s_width*gscale + slab_padding; if(gallery_height < (slabobj["BOX"][3]-slabobj["BOX"][1])*gscale) gallery_height = (slabobj["BOX"][3]-slabobj["BOX"][1])*gscale; /*var label_back = new Konva.Rect({ width: (slabobj["BOX"][2] - slabobj["BOX"][0])*gscale-60, height: 18, fill: "#888", x: current_slab.x()+current_slab.attrs.clipX+30, y: 20*gscale-4, id: "scrollbar", cornerRadius: 3, name: "slabnumber_back", perfectDrawEnabled: false });slabs.add(label_back); */ var label = new Konva.Text({ x: l_x+current_slab.attrs.clipX, y: 16*gscale, text: "",//slabname, fontSize: 12, fontFamily: 'Verdana', fill: '#fff', width: s_width*gscale, align: "center", //listening: false, letterSpacing: 0.0001, lineHeight: 1.2, name: "slabnumber", id: slab_id, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); slabs.add(label); label.on('mouseover', function (evt) { document.body.style.cursor = 'pointer'; }); label.on('mouseout', function (evt) { document.body.style.cursor = 'default'; }); /*var icon = new Konva.Text({ x: current_slab.x()+current_slab.attrs.clipX, y: 20, text: "\uf247", fontSize: 10, letterSpacing: 0.0001, fontFamily: '"Font Awesome 5 Free"', fill: color.orange }); slabs.add(icon);*/ } } } scrollLayer.visible(true); slabs_bg.visible(true); slabs_border.visible(true); slabs.visible(true); $(".cart .slab[data-id="+current_set_id+"]").addClass("active"); $(".set_title").show(); } slabs_bg.width(slabs_width+2*slab_padding); slabs_bg.height(gallery_height+3*slab_padding+5); slabs_bg.y(-gallery_height); slabs_border.width(slabs_width+3*slab_padding); if(slabs_width+2*slab_padding < window.innerWidth-150) { slabs_bg.width(window.innerWidth-150); slabs_border.width(window.innerWidth-150); scrollLayer.visible(false); } // initial slabs position slabs.y(gallery_height); //slabs_border.y(gallery_height+2*slab_padding) scrollBar.y(gallery_height+2*slab_padding+5); if(!gallery_init && toggle) { fit_to_screen(); gallery_init = true; } reload_recycle_polygons() } function gallery_find(selected_slab) { gallery_open(selected_slab.attrs.set_id, false); var blenderwidth = $("#blender").width(); var selected_slab_width = selected_slab.findOne(".slab_"+selected_slab.attrs.id+"_imagebox").width(); if(slabs.x()*-1 > selected_slab.x() || slabs.x()*-1 < selected_slab.x()+selected_slab_width- blenderwidth) // links aus dem bild || rechts aus dem bild { var new_x = - selected_slab.attrs.x - slab_padding/2 + blenderwidth/2 - selected_slab_width/2; if(new_x > 0) new_x = 0; if(new_x < -slabs_bg.width()+blenderwidth) new_x = -slabs_bg.width()+blenderwidth; slabs.x(new_x); var percent = (-new_x)/(slabs_bg.width()-blenderwidth); var availableWidth = blenderwidth - slab_padding * 2 - scrollBar.width(); scrollBar.x( percent*availableWidth +slab_padding); //batchDraw(scrollLayer); //?? } //batchDraw(slabs); // vip } function load_plan(id) { $("#load_plan").val(id); if(plan) plan.destroy(); plan = new Konva.Layer({ id: "plan", scaleX: 1, scaleY: 1, x: 0, y: 0, visible: true, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); blending_ids = []; for(var k in obj["plans"][id]) { if(k == "name" || k == "box" || k == "overlay") continue; for (var i in obj["plans"][id][k]) { // convert to konva points if(!obj["plans"][id][k][i].kp && obj["plans"][id][k][i].p) { var kp = bulgetopoints(obj["plans"][id][k][i].p); obj["plans"][id][k][i].kp = invertp(kp); } // offset targets once if(k == "targets" && obj["plans"][id][k][i].kp && !obj["plans"][id][k][i].kpo) { // rounded offset /*var kpcw = sort_polygon(obj["plans"][id][k][i].kp); var paddingPolygon = createPaddingPolygon(createPolygon(convert_xy(kpcw)), offset_distance_target); obj["plans"][id][k][i].kpo = convert_kp(paddingPolygon.vertices);*/ // straight offset obj["plans"][id][k][i].kpo = offset_points(obj["plans"][id][k][i].kp, offset_distance_target); } draw_plan_objects(obj["plans"][id][k][i]); if(obj["plans"][id][k][i]["includes"]) { for (var ink = 0; ink < obj["plans"][id][k][i]["includes"].length; ink++) { // convert to konva points if not ready from backend if(!obj["plans"][id][k][i]["includes"][ink].kp && obj["plans"][id][k][i]["includes"][ink].p) { obj["plans"][id][k][i].includes[ink].kp = invertp(bulgetopoints(obj["plans"][id][k][i].includes[ink].p)); delete obj["plans"][id][k][i].includes[ink].p; } if(!obj["plans"][id][k][i].ref || !obj["plans"][id][k][i].ref.t) { console.log("Reference for Target not set"); continue; } draw_plan_objects(obj["plans"][id][k][i].includes[ink], obj["plans"][id][k][i].ref.t); } } } } // Load Overlay Image if(obj["plans"][current_plan] && obj["plans"][current_plan]["overlay"]) { var pw = obj["plans"][current_plan]["box"][2]-obj["plans"][current_plan]["box"][0]; var ph = obj["plans"][current_plan]["box"][3]-obj["plans"][current_plan]["box"][1]; var overlay = new Konva.Rect({ x: obj["plans"][current_plan]["box"][0], y: -obj["plans"][current_plan]["box"][1], width: pw, height: -ph, //strokeWidth:1, //stroke:"#f0f", layer: "900_Overlay", name: "overlay", listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); plan.add(overlay); overlay.moveToTop(); var img = new Image(); img.crossOrigin = 'Anonymous'; img.onload = function(evt) { var scale = pw/1000; overlay.setAttr("fillPatternImage", img); overlay.setAttr("fillPatternRepeat", "no-repeat"); overlay.setAttr("fillPatternScaleX", scale); overlay.setAttr("fillPatternScaleY", scale); overlay.setAttr("fillPatternX", 0); overlay.setAttr("fillPatternY", -ph); img.onload = null;// unbind onload overlay.draw(); }; img.onerror = function(evt) { console.log("Img Overlay Error: ",img,evt); }; img.src = obj["plans"][current_plan]["overlay"]; } stage.add(plan); plan.moveToBottom(); clip.position(plan.position()); clip.scale(plan.scale()); //fit_to_screen(plan.scale(), plan.position()); toggleLayers(); } function draw_plan_objects(obi, part_of_target = null) { if(!obi) return; if(!obi.color) // set default color obi.color = "#ffffff"; if(obi.color == "#000000") obi.color = color.gray; if( obi["type"] == "POLYLINE" || obi["type"] == "CIRCLE" || obi["type"] == "ARC" ) { if( obi.ref && obi.ref.t) // target polyline { if( !blending_ids.includes(obi.ref.t) ) blending_ids.push(obi.ref.t); var polyline = new Konva.Line({ points: remove_duplicates(obi.kp), strokeWidth: 0.1, stroke: "#ccc", closed: true, centroid: polygon_centroid(obi.kp), pointsoffset: obi.kpo, layer: obi.l, thickness: parseInt(obi.l.charAt(1)+""+obi.l.charAt(2)), id: "target", fill: "#444", perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); /*var snapset = new Konva.Line({ points: obi.kpos, strokeWidth: 0.3, stroke: color.red, }); plan.add(snapset);*/ if( obi.ref.hasOwnProperty("t") ) polyline.attrs.name = obi.ref.t.split(' ').join('_'); if(obi.ref.hasOwnProperty("r")) polyline.attrs.rota = obi.ref.r; if(obi.ref.hasOwnProperty("x") && obi.ref.hasOwnProperty("y")) polyline.attrs.orig = { x:obi.ref.x, y:-obi.ref.y }; if( params.mode == "commit") { if( !obi.ref.hasOwnProperty("t") || !obi.ref.hasOwnProperty("r") || !obi.ref.hasOwnProperty("x") || !obi.ref.hasOwnProperty("y") ) { polyline.fill(color.orange); } //polyline.strokeWidth(1); //polyline.stroke(color.orange); } if(part_of_target) polyline.attrs.part = part_of_target; //var reference_point = new Konva.Circle({x: obi.ref.x,y: obi.ref.y,radius: 1,fill: color.blue,name: "reference"});plan.add(reference_point); plan.add(polyline); polyline.moveToBottom(); } else // regular polyline { //if(obi.closed == 1) obi.closed = true; else obi.closed = false; var polyline = new Konva.Line({ points: obi.kp, stroke: obi.color, strokeWidth: 1, closed: obi.closed == 1?true:false,//obi.closed, name: "unset", orig: 0, rota: 0, id: "line", layer: obi.l, listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); if(part_of_target) polyline.attrs.part = part_of_target; plan.add(polyline); polyline.moveToTop(); } } if (obi["type"] == "LINE") { var line = new Konva.Line({ points: obi.kp, stroke: color.gray, // Default Line Color strokeWidth: 0.5, layer: obi.l, id: "line", listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); if(obi.color) line.stroke(obi.color); if(part_of_target) line.attrs.part = part_of_target; plan.add(line); line.moveToTop(); } if (obi["type"] == "SOLID") { var solid = new Konva.Line({ points: obi.kp, closed: true, name: "unset", orig: 0, rota: 0, id: "solid", fill: color.gray, layer: obi.l, listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); if(obi.color) solid.fill(obi.color); if(part_of_target) solid.attrs.part = part_of_target; plan.add(solid); solid.moveToTop(); } if (obi["type"] == "TEXT") { if(!obi.a) obi.a = 0; // minimum font size 1*1.33 if(obi.h < 1) obi.h = 1; var label = new Konva.Text({ x: obi.kp[0], y: obi.kp[1],//-12, text: obi.t, fontSize: obi.h*1.1,//33,//65, fontFamily: 'Arial', fill: color.gray, //Default Text Color letterSpacing: 0.0001, rotation: -obi.a, id: "text", layer: obi.l, listening: false, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); if(obi.color) label.fill(obi.color); // Text Alignment var alignment = []; if(obi.align) alignment = obi.align.split("-"); if(alignment[0] == "top") label.attrs.offsetY = 0; if(alignment[0] == "middle") label.attrs.offsetY = obi.h/2; if(alignment[0] == "bottom") label.attrs.offsetY = obi.h; if(alignment[1] == "left") label.attrs.offsetX = 0; if(alignment[1] == "center") label.attrs.offsetX = label.textWidth/2; if(alignment[1] == "right") label.attrs.offsetX = label.textWidth; /*// Text Align ( R12 adjusts x,y ) if(obi.align_v == 0 || obi.align_v == 1) // Middle label.attrs.offsetY = 0; if(obi.align_v == 2) // Middle label.attrs.offsetY = obi.h/2; if(obi.align_v == 3) // Top label.attrs.offsetY = obi.h; if(obi.align_h == 0) // Left label.attrs.offsetX = 0; if(obi.align_h == 1) // Center label.attrs.offsetX = label.textWidth/2; if(obi.align_h == 2) // Right label.attrs.offsetX = label.textWidth; if(part_of_target) label.attrs.part = part_of_target;*/ plan.add(label); label.moveToTop(); } } function touch_zoomX(e) { show_transformer(false); e.evt.preventDefault(); var f1 = {x:e.evt.touches[0].clientX - cart_width, y:e.evt.touches[0].clientY - head_height}; var f2 = {x:e.evt.touches[1].clientX - cart_width, y:e.evt.touches[1].clientY - head_height}; var oldScale = plan.scaleX(); var p1 = { x: f1.x / oldScale - plan.x() / oldScale, y: f1.y / oldScale - plan.y() / oldScale }; var p2 = { x: f2.x / oldScale - plan.x() / oldScale, y: f2.y / oldScale - plan.y() / oldScale }; if (!lastCenter) { lastCenter = getCenter(p1, p2); //return; } var newCenter = getCenter(p1, p2); var dist = getDistance(f1, f2); if (!lastDist) { lastDist = dist; } // coordinates in konva space var pointTo = { x: (newCenter.x - plan.x()) / plan.scaleX(), y: (newCenter.y - plan.y()) / plan.scaleX(), }; var newScale = plan.scaleX() * (dist / lastDist); //plan.scaleX(scale); //plan.scaleY(scale); // calculate new position of the plan var dx = newCenter.x - lastCenter.x; var dy = newCenter.y - lastCenter.y; var newPos = { x: newCenter.x - pointTo.x * newScale + dx, y: newCenter.y - pointTo.y * newScale + dy, }; sync(newPos, { x: newScale, y: newScale }); //plan.position(newPos); batchDraw(plan); plan.find(".point").destroy(); var point = new Konva.Circle({x: p1.x,y: p1.y,radius: 20,fill: '#fff',name: "point"}); plan.add(point); var point = new Konva.Circle({x: p2.x,y: p2.y,radius: 20,fill: '#fff',name: "point"}); plan.add(point); var point = new Konva.Circle({x: newCenter.x,y: newCenter.y,radius: 20,fill: '#f00',name: "point"}); plan.add(point); lastDist = dist; lastCenter = newCenter; } function touch_zoom(e) { show_transformer(false); var f1 = { x:e.evt.touches[0].clientX - cart_width, y:e.evt.touches[0].clientY - head_height }; var f2 = { x:e.evt.touches[1].clientX - cart_width, y:e.evt.touches[1].clientY - head_height }; var fc = { x:(f1.x+f2.x)/2, y:(f1.y+f2.y)/2 }; var dist = Math.hypot(f2.x - f1.x, f2.y - f1.y); var delta = dist - lastDist;lastDist = dist; var oldScale = plan.scaleX(); var t1 = { x: f1.x / oldScale - plan.x() / oldScale, y: f1.y / oldScale - plan.y() / oldScale }; var t2 = { x: f2.x / oldScale - plan.x() / oldScale, y: f2.y / oldScale - plan.y() / oldScale }; var tc = { // startPos x: fc.x / oldScale - plan.x() / oldScale, y: fc.y / oldScale - plan.y() / oldScale }; var newScale = delta > 0 ? oldScale += 0.01 : oldScale -= 0.01; if(newScale > max_scale)newScale = max_scale; if(newScale < min_scale)newScale = min_scale; var newPos = { x: -(tc.x - fc.x / newScale) * newScale, y: -(tc.y - fc.y / newScale) * newScale }; // x: -(startPos.x - center.x / newScale) * newScale, sync(newPos, { x: newScale, y: newScale }); scale_stroke(newScale); batchDraw(plan); /*plan.find(".point").destroy(); var point = new Konva.Circle({ x: t1.x, y: t1.y, radius: 20, fill: '#fff', name: "point" }); plan.add(point); var point = new Konva.Circle({ x: t2.x, y: t2.y, radius: 20, fill: '#fff', name: "point" }); plan.add(point); var point = new Konva.Circle({ x: tc.x, y: tc.y, radius: 20, fill: '#f00', name: "point" }); plan.add(point); var point = new Konva.Circle({ x: newPos.x, y: newPos.y, radius: 10, fill: '#0f0', name: "point" }); plan.add(point);*/ } function mouse_zoom(e) { // TODO one zoom for mouse, touch and click show_transformer(false); var oldScale = plan.scaleX(); var center = stage.getPointerPosition(); var startPos = { x: center.x / oldScale - plan.x() / oldScale, y: center.y / oldScale - plan.y() / oldScale }; var newScale = e.evt.deltaY < 0 ? oldScale += oldScale/4 : oldScale -= oldScale/4; if(newScale > max_scale)newScale = max_scale; if(newScale < min_scale)newScale = min_scale; var newPos = { x: -(startPos.x - center.x / newScale) * newScale, y: -(startPos.y - center.y / newScale) * newScale }; sync(newPos, { x: newScale, y: newScale }); scale_stroke(newScale); batchDraw(plan); } function sync(newPos, newScale, load_images = true) { clip.position(newPos); clip.scale(newScale); plan.position(newPos); plan.scale(newScale); if(load_images) { for (var i = 0; i < plan.children.length; i++) { if(plan.children[i].attrs && plan.children[i].attrs.layer == "100-BLENDING") { load_image(plan.children[i]); } } } } function click_zoom(val) { show_transformer(false); var oldScale = plan.scaleX(); var center = {x:stage.attrs.width/2,y:stage.attrs.height/2}; var startPos = { x: center.x / oldScale - plan.x() / oldScale, y: center.y / oldScale - plan.y() / oldScale }; //var newScale = oldScale += val; var newScale = val > 0 ? oldScale += oldScale/4 : oldScale -= oldScale/4; if(newScale > max_scale)newScale = max_scale; if(newScale < 0.1)newScale = 0.2; var newPos = { x: -(startPos.x - center.x / newScale) * newScale, y: -(startPos.y - center.y / newScale) * newScale }; sync(newPos, { x: newScale, y: newScale }); scale_stroke(newScale); batchDraw(plan); } function scale_stroke(scale) { global_scale = scale; //plan.find(node => { return node.attrs.strokeWidth != undefined && (node.attrs.id == "line" || node.attrs.id == "unset" || node.attrs.id == "target" || node.attrs.id == "text");}).strokeWidth(1*(1/(scale*2) )); for (var i = 0; i < plan.children.length; i++) { if(plan.children[i].attrs.strokeWidth != undefined && (plan.children[i].attrs.id == "line" || plan.children[i].attrs.id == "unset" || plan.children[i].attrs.id == "target" || plan.children[i].attrs.id == "text")) { plan.children[i].strokeWidth(1*(1/(scale*2) )); } } } function destroy_clone(slab_id, instance, target = null) { //console.error("Delete Clone in user Object", target); if(user.clones[slab_id] && user.clones[slab_id][instance]) delete user.clones[slab_id][instance]; if(user.clones[slab_id] && Object.values(user.clones[slab_id]).length == 0) delete user.clones[slab_id]; if(target && !slab_mode) { show_transformer(false); target.destroy(); console.log("Destroy Clone"); } } function show_transformer(show) { if(show) { // disable transformer for locked slabs if(last_target && last_target.attrs) { transformer.show(); calc_rotation_snaps(); } else { transformer.hide(); } } else if(transformer.attrs.visible) { transformer.hide(); clip.find(node => {return node.attrs.name != undefined && node.attrs.name.includes("_group");}).moveTo(plan); clip.find(node => {return node.attrs.name === "rotaxgroup"}).destroy(); } if(transformer._nodes && transformer._nodes[0] && transformer._nodes[0].attrs) { if(show) last_target = transformer._nodes[0]; else last_target = null; } transformer.rotateEnabled(false); transformer.borderEnabled(true); highlight.hide(); batchDraw(clip); } function mark_active_ticket(e) { // mark slab in gallery if(mouse_is_down && e.target.attrs.id && e.target.attrs.instance) { var active_slab = slabs.findOne("#"+e.target.attrs.id+"_"+(e.target.attrs.instance-1)+"_used"); if(mouse_is_down && active_slab) { active_slab.fill(highlight_color); } } batchDraw(slabs); } function inspect_target(plan_id, target_name, view = false) { console.log("Inspect Target "+view); //plan.scale({x:1,y:1}); // load plan current_plan = plan_id; last_plan_scale = plan.scale(); last_plan_position = plan.position(); load_plan(plan_id); reload_clones(); setTimeout(function(){ // find slab id var slab_id = 0; var inst = 0; for (var sid in user.clones) { for (var instance in user.clones[sid]) { if(user.clones[sid][instance] && user.clones[sid][instance].target == target_name) { inst = instance; slab_id = sid; break; } } if(slab_id != 0) break; } var target_box = {"x":0,"y":0,"w":0,"h":0}; var target_points = {}; for (var num in plan.children) { if(plan.children[num].attrs) { if(!view) plan.children[num].hide(); // show target outline if(plan.children[num].attrs.name == target_name) { plan.children[num].show(); if(view) { highlight.show(); // show active ticket in plan highlight.points(plan.children[num].attrs.points); highlight.position(plan.children[num].position()); batchDraw(clip); } target_points = plan.children[num].attrs.points; } // show parts of target ( includes ) /*if(plan.children[num].attrs.part == target_name) { plan.children[num].show(); plan.children[num].moveToTop(); }*/ //console.log(plan.children[num].attrs); // show slab Image if(plan.children[num].attrs.id == slab_id && plan.children[num].attrs.instance-1 == inst ) { plan.children[num].show(); } } } if(view) return; target_box = get_box(target_points); var px = target_box.x; var py = target_box.y - target_box.y - target_box.h; var pw = target_box.w-target_box.x; var ph = target_box.h-target_box.y; // fit target to screen fit_to_screen([px,py,pw,ph]); var faktor = 10/(plan.scale().x); var hairline = faktor/10; var hairline_color = "#444"; var paper_color = "#fff"; var font_size = faktor*3; px = target_box.x - faktor*15; py = target_box.y - target_box.y - target_box.h - faktor*15; pw = target_box.w-target_box.x + faktor*30; ph = target_box.h-target_box.y + faktor*30; fit_to_screen([px,py,pw,ph]); var target_bg = new Konva.Rect({ x: px, y: py, width: pw, height: ph, fill: paper_color, listening: false, perfectDrawEnabled: false, name: "measurements" }); plan.add(target_bg); target_bg.moveToBottom(); var target_info = new Konva.Text({ x: px+font_size, y: py+ph-font_size*2, text: "TICKET: "+target_name, fontSize: font_size, fontFamily: 'Verdana', fill: hairline_color, listening: false, letterSpacing: 0.0001, name: "measurements" }); plan.add(target_info); // render measurements var paddy = offset_polylines(target_points, font_size); for (var i = 0; i < paddy.length; i+=4) { var dist = distance(paddy[i], paddy[i+1], paddy[i+2], paddy[i+3])*10; if(dist <= 50) continue; var ang = Math.round(angle(paddy[i], paddy[i+1], paddy[i+2], paddy[i+3])); var mid = line_midpoint(paddy[i], paddy[i+1], paddy[i+2], paddy[i+3]); if(ang < -90) ang += 180; if(ang >= 90) ang += 180; var line = new Konva.Arrow({ points: [paddy[i], paddy[i+1], paddy[i+2], paddy[i+3]], stroke: hairline_color, strokeWidth: hairline, pointerLength: faktor*1, pointerWidth: faktor*0.8, fill: hairline_color, pointerAtBeginning: true, closed: false }); // measurement text var txt = new Konva.Text({ x: mid.x, y: mid.y+font_size/10, text: Math.round(dist), fontSize: font_size, padding: font_size/10, fontFamily: 'Verdana', fill: hairline_color, listening: false, letterSpacing: 0.0001, name: "measurements" }); // text background var tbg = new Konva.Rect({ x: mid.x-txt.width()/2, y: mid.y-txt.height()/2, width: txt.width(), height: txt.height(), name: "measurements", fill: paper_color }); // center text txt.x(txt.x()-txt.width()/2); txt.y(txt.y()-txt.height()/2); // rotate text rotate_around_point(tbg, ang, mid); rotate_around_point(txt, ang, mid); plan.add(line); plan.add(tbg); plan.add(txt); } var target_outline = new Konva.Line({ points: target_points, stroke: hairline_color, strokeWidth: hairline, closed: true, name: "measurements", }); plan.add(target_outline); // safari 268436456 // chrome 75000000 // 50000000 // 4k 8294400 // ipad mini 5241810 // 5000000 // 2k 2073600 var ratio = 1036800 / ($("#blender").width()*$("#blender").height()); //if(ratio > 10) // ratio = 10; var dataURL = plan.toDataURL({ x: 0, y: 0, width: $("#blender").width(), height: $("#blender").height(), pixelRatio: ratio, quality: 1, mimeType: "image/png" }); //download_uri(dataURL, "Test.png"); if(typeof on_inspection_image == "function") on_inspection_image(dataURL, target_name); plan.find(node => {return node.attrs.name === "measurements"}).destroy(); // show everything again for (var num in plan.children) { if(plan.children[num].attrs) { plan.children[num].show(); } } sync(last_plan_position, last_plan_scale); //fit_to_screen(); },10); } function calc_rotation_snaps() { if(last_target && last_target.attrs && last_target.attrs.id && last_dropbox)//last_target.attrs.name.includes("slab_")) { var slab_id = last_target.attrs.id; rotax_angles = []; for(var i in user.clones[slab_id]) { if(user.clones[slab_id][i]) { // skip self //if( user.clones[slab_id][i].target == last_dropbox.target) // continue; //user.clones[slab_id][i].mask = sort_polygon(user.clones[slab_id][i].mask); //last_dropbox.mask = sort_polygon(last_dropbox.mask); // calculate rotatin snap angles for (var k = 0; k < user.clones[slab_id][i].mask.length; k += 2) { if(!user.clones[slab_id][i].mask[k+2] || !user.clones[slab_id][i].mask[k+3]) break; var x1 = user.clones[slab_id][i].mask[k]; var y1 = user.clones[slab_id][i].mask[k+1]; var x2 = user.clones[slab_id][i].mask[k+2]; var y2 = user.clones[slab_id][i].mask[k+3]; // calc distance to be long enough to be relevant for rotation snap if(distance(x1,y1, x2,y2) < 10) continue; var ang = angle(x1,y1, x2,y2) - user.clones[slab_id][i].rotation; for (var ai = 0; ai < last_dropbox.mask.length; ai+=2){ if(!last_dropbox.mask[ai+2] || !last_dropbox.mask[ai+3]) break; var x1a = last_dropbox.mask[ai]; var y1a = last_dropbox.mask[ai+1]; var x2a = last_dropbox.mask[ai+2]; var y2a = last_dropbox.mask[ai+3]; if(distance(x1a,y1a, x2a,y2a) < 10) continue; var anga = Math.round(angle(x1a, y1a, x2a, y2a)*100)/100; var snapangle = Math.round( (ang+anga) *100)/100; if(!rotax_angles.includes(snapangle)) rotax_angles.push(snapangle); } } } } } } function fit_to_screen(custom = null) { //console.log("fit_to_screen", fit_to_screen.caller.name) var boxarray = []; for(var i in plan.children) { if(plan.children[i].attrs) { if(plan.children[i].attrs.points) { for (var k = 0; k < plan.children[i].attrs.points.length; k++) { boxarray.push(plan.children[i].attrs.points[k]); } } /*if(!plan.children[i].attrs.clipFunc) { if(plan.children[i].attrs.x) { boxarray.push(plan.children[i].attrs.x); } if(plan.children[i].attrs.y) { boxarray.push(plan.children[i].attrs.y); } }*/ } } var box = get_box(boxarray); var px = box.x-2; var py = -box.h-2; var pw = box.w-box.x+4; var ph = box.h-box.y+4; /*var dbg = new Konva.Rect({ width: pw, height: ph, stroke: color.blue, x: px, y: py, listening: false, }); plan.add(dbg);*/ if(custom != null) // custom = [x,y,w,h] { px = custom[0]; py = custom[1]; pw = custom[2]; ph = custom[3]; } var bh = $("#blender").height(); var bw = $("#blender").width(); var gh = gallery_height; // gallery height if(slabs.visible()) { gh = 2*slab_padding+gh+20; bh = bh-gh; } var scale = bw/pw; if(bw/pw > bh/ph) scale = bh/ph; if(scale > max_scale) scale = max_scale; //min_scale = scale/2; /*plan.scale({x:scale, y:scale}); clip.scale(plan.scale()); plan.position({x:-px*scale +(bw-pw*scale)/2, y:-py*scale +gh +(bh-ph*scale)/2}); clip.position(plan.position());*/ sync({x:-px*scale +(bw-pw*scale)/2, y:-py*scale +gh +(bh-ph*scale)/2}, {x:scale, y:scale}); scale_stroke(scale); batchDraw(stage); } function on_mousedown(e) { mouse_is_down = true; // clicked on clone inside a target if(params.mode == "inspect") { highlight.hide(); batchDraw(clip); if(e.target && e.target.attrs && e.target.attrs.name && e.target.attrs.name.includes("slab_")) { inspect_target(current_plan, user.clones[e.target.attrs.id][e.target.attrs.instance-1].target); return; } } if(exchange_slab_pressed) { if(e.target.attrs.name && e.target.attrs.name.includes("_imagebox") && e.target.parent.parent.attrs.id == "plan") { if(last_target && transformer.visible()) { var o_slab = last_target; var o_user = userClone(last_target); var o_dropbox = plan.findOne(node => { return node.attrs.name && node.attrs.name == o_user.target;}); var t_slab = e.target.parent; var t_user = userClone(e.target.parent); var t_dropbox = plan.findOne(node => { return node.attrs.name && node.attrs.name == t_user.target;}); var tx = t_slab.attrs.x - t_user.position.x + o_user.position.x; var ty = t_slab.attrs.y - t_user.position.y + o_user.position.y; var ox = o_slab.attrs.x - o_user.position.x + t_user.position.x; var oy = o_slab.attrs.y - o_user.position.y + t_user.position.y; o_slab.position({x:tx,y:ty})%360+360; t_slab.position({x:ox,y:oy})%360+360; clip_to_plan(o_slab, t_dropbox); clip_to_plan(t_slab, o_dropbox); stage.batchDraw(); } } return; } if(e.target.attrs.name == "rotax") { // rotationoffset is how much from x an y to center of rotation last_rotationoffset = calculate_rotationoffset(); //mark_active_ticket(); return; } clip.find(node => {return node.attrs.name === "rotaxgroup"}).destroy(); if(keep_plan) { // stop jiggeling slab if(e.target.attrs.name && e.target.attrs.name.includes("_imagebox")) e.target.parent.stopDrag(); e.target = stage; } if(e.target.attrs.name && e.target.attrs.name.includes("_imagebox")) { e.target = e.target.parent; // range_set: disable slabs on plan to place above existing targets if(e.target.attrs.set_id && range_set_regex.test( e.target.attrs.set_id )) { //plan.find(node => { return node.attrs.name && node.attrs.name.includes("_imagebox");}).listening(false); } } last_target = e.target; // click on slab name in gallery if(e.target.attrs.name && e.target.attrs.name == "slabnumber") { // select active ticket in gallery var selected_slab = slabs.findOne(".slab_"+e.target.attrs.id +"_group"); if(selected_slab && (!slab_view_last_target || (slab_view_last_target && selected_slab.attrs.id != slab_view_last_target.attrs.id))) { //e.target = selected_slab; slab_view_create(e, selected_slab); return; } if(selected_slab && slab_view_last_target && selected_slab.attrs.id == slab_view_last_target.attrs.id) { slab_view_reset(); return; } } if(slab_mode) { slab_view_mousedown(e); /*if(!slab_view_last_target) { slab_mode = false; }*/ return; } // click on used area in slabs if(e.target.attrs.name == "used_area") { if(e.target.parent.attrs.instance == 0)// on slabs { e.target.stopDrag(); var clone = plan.findOne(node => {return node.attrs.id === e.target.attrs.slab && node.attrs.instance === e.target.attrs.inst;}); if(clone != undefined) { show_transformer(false); highlight.show(); // show active ticket in plan highlight.points(e.target.attrs.points); highlight.position(clone.position()); batchDraw(clip); } } else // ignore when clicked on plan { e.target = e.target.parent; } } if(e.target.attrs.name && e.target.attrs.name.includes("slab_") ) { if(params.mode == "view" || params.mode == "recycle"|| e.target.attrs.locked) { // we clicked on a licked slab or are in view only mode var selected_slab = slabs.findOne(".slab_"+e.target.attrs.id+"_group"); gallery_find(selected_slab); e.target.stopDrag(); highlight.hide(); mark_active_ticket(e); } else { // create clone if its the original if(e.target.attrs.instance == 0) { // we clicked on a slab in the gallery clip.moveToTop(); show_transformer(false); create_clone(e); } else if(e.target.attrs.instance > 0) { // we clicked on a slab on the plan e.target.moveTo(clip); transformer.moveToTop(); transformer.attachTo(e.target); show_transformer(true); highlight.hide(); mark_active_ticket(e); // select active ticket in gallery var selected_slab = slabs.findOne(".slab_"+e.target.attrs.id+"_group"); gallery_find(selected_slab); batchDraw(clip); } } used_targets = []; for (var slab_id in user.clones) { for (var instance in user.clones[slab_id]) { if( user.clones[slab_id][instance] != null && (instance != e.target.attrs.instance-1 || e.target.attrs.id != slab_id) ) { used_targets.push(user.clones[slab_id][instance].target); } } } } else if( e.target.attrs.id == "stage" || e.target.attrs.id == "target" )// drag plan { // cache plan //plan.cache({pixelRatio:0.5}); //slabs.cache({pixelRatio:0.5}); plan.startDrag(); show_transformer(false); } else if(e.target.attrs.id == "scrollbar") { show_transformer(false); } current_target = e.target; calculate_used_slab_area(); } function rotate_by_angle(angle) { // input angle -180 > 180 last_rotationoffset = calculate_rotationoffset(); rotationoffset = last_rotationoffset; //var handle = clip.findOne(node => {return node.attrs.name === "rotax"}); //if(handle) //{ if(hold_shift) { // disable snap when rotating from console hold_shift = false; rotax_move(angle); hold_shift = true; }else{ rotax_move(angle); } batchDraw(stage); rotationoffset = null; //} } function calculate_rotationoffset() { var rotax = clip.findOne(node => {return node.attrs.name === "rotax"}); // centroid var cp = { // center x:rotax.attrs.target.attrs.centroid.x, y:rotax.attrs.target.attrs.centroid.y }; var l1 = { // end point x:transformer._nodes[0].attrs.x, y:transformer._nodes[0].attrs.y }; var l2 = { x:transformer._nodes[0].attrs.x+transformer._nodes[0].attrs.clipWidth+large_image, y:transformer._nodes[0].attrs.y }; // rotate line under transformer and find normal intersection point var lx2 = translatePoint(l2.x, l2.y, l1.x, l1.y, transformer.rotation()); var np = normalpoint(l1, lx2, cp); // point in between var rad = Math.atan2((l1.x-np.x)*(cp.y-np.y)-(l1.y-np.y)*(cp.x-np.x),(l1.x-np.x)*(cp.x-np.x)+(l1.y-np.y)*(cp.y-np.y)); rad>0?rad=1:rad=-1; rotationoffset = { x:-dist(l1,np), y:dist(np,cp)*rad }; return rotationoffset; } function on_mouseup(e) { mouse_is_down = false; current_target = null; if(e.target.attrs.name && e.target.attrs.name.includes("_imagebox")) e.target = e.target.parent; if(slab_mode) { slab_view_mouseup(e); return; } rotationoffset = null; highlight.hide(); // Show Handle if(transformer.visible() && transformer._nodes[0]) { // set rotaxx center var tid = transformer._nodes[0].attrs.id; var tin = transformer._nodes[0].attrs.instance; var tname; if(dropbox) tname = dropbox.name; if(user.clones[tid][tin-1]) tname = user.clones[tid][tin-1].target; if(tname) { var target = plan.findOne("."+tname); if(target) { clip.find(node => {return node.attrs.name === "rotaxgroup"}).destroy(); var scale = 3/plan.scaleX(); if(scale > 1) { scale = 1; } var rotaxgroup = new Konva.Group({ x: target.attrs.centroid.x, y: target.attrs.centroid.y, name: "rotaxgroup", perfectDrawEnabled: false, }); var handle = new Konva.Circle({ x: 0, y: -30*scale, radius: 6*scale, //stroke: color.orange, fill: color.orange, name: "rotax", clone: transformer._nodes[0], // pass clone info tru rotax point target: target, // pass target info tru rotax point perfectDrawEnabled: false, hitStrokeWidth: 0, shadowForStrokeEnabled: false }); rotaxgroup.add(handle); var deco = new Konva.Circle({ x: 0, y: 0, radius: 2*scale, //stroke: color.orange, fill: color.orange, listening: false, perfectDrawEnabled: false, hitStrokeWidth: 0, shadowForStrokeEnabled: false }); rotaxgroup.add(deco); var deco = new Konva.Line({ points: [0,0,0,-30*scale], name: "rotax_line", stroke: color.orange, strokeWidth: 1*scale, listening: false, perfectDrawEnabled: false, hitStrokeWidth: 0, shadowForStrokeEnabled: false }); rotaxgroup.add(deco); rotaxgroup.rotation(transformer.rotation()); clip.add(rotaxgroup); // get rotationoffset when dropped into target for the first time var cp = {x:handle.attrs.target.attrs.centroid.x, y:handle.attrs.target.attrs.centroid.y}; var l1 = {x:transformer._nodes[0].attrs.x, y:transformer._nodes[0].attrs.y}; var l2 = { x:transformer._nodes[0].attrs.x+transformer._nodes[0].attrs.clipWidth+large_image, y:transformer._nodes[0].attrs.y }; // rotate line under transformer and find normal intersection point var lx2 = translatePoint(l2.x, l2.y, l1.x, l1.y, transformer.rotation()); var np = normalpoint(l1, lx2, cp); last_rotationoffset = { x:-dist(l1,np), y:dist(np,cp) }; if(hold_rotation_pressed) { rotationoffset = last_rotationoffset; rotax_move(hold_rotation); stage.batchDraw(); rotationoffset = null; } } } } } function global_mouseup(e) { sync(plan.position(), plan.scale(), false); plan.stopDrag(); // move all to plan clip.find(node => { return node.attrs.name && node.attrs.name.includes("_group");}).moveTo(plan); // get layer 4XX on top plan.find(node => { return node.attrs.layer && node.attrs.layer.startsWith("4");}).moveToTop(); if(e.target.localName == "canvas") undo(e); lastDist = 0; dropbox = null; var planoverlay = plan.findOne(node => {return node.attrs.name == "overlay";}); if(planoverlay) planoverlay.moveToTop(); // range_set: activate targets after set was active var plan_slabs = plan.find(node => { return node.attrs.name && node.attrs.name.includes("_imagebox");}); for(var i=0; i 0) { var slab_thickness = obj.slabs[source.attrs.set_id].thickness; if(slab_thickness > target.attrs.thickness || slab_thickness <= target.attrs.thickness - 5) // tolerance { if(thickness && params.mode != "commit" && params.mode != "demo") { print("This Slab is "+slab_thickness+"mm and can not be used on "+target.attrs.thickness+"mm"); can_clip = false; } } if(params.mode == "commit") { transformer.find(node => { return node.attrs.name && node.attrs.name == "commitlabel";}).destroy(); var commitlabel = new Konva.Text({ x: 10, y: 10, text: "Detected thickness: "+target.attrs.thickness+ "mm", fontSize: 12, fontFamily: 'Verdana', fill: '#fff', listening: false, letterSpacing: 0.0001, name: "commitlabel", perfectDrawEnabled: false, hitStrokeWidth: 0, }); transformer.add(commitlabel); } } // Surface Check todo /*var salb_id = source.attrs.id; if(user.clones[salb_id]) { var first_clone = user.clones[salb_id][Object.keys(user.clones[salb_id])[0]].target; var first_clone_layer = usage.used[first_clone]; if(first_clone_layer != undefined) { if(first_clone_layer != target.attrs.layer) { print("This Slab can only be used with one surface finish: "+first_clone_layer); //plan.find(node => {return node.attrs.layer != target.attrs.layer}).fill("#666"); can_clip = false; } } }*/ } // unclip if( !keep_target && (userClone(source).target != target.attrs.name && ((target.attrs.name && !target.attrs.name.includes("slab_"))) || target.attrs.id == "stage" /*|| target.attrs.id == "slabs"*/) ) // && (source.attrs.id != target.attrs.id || source.attrs.instance != target.attrs.instance) ) { userClone(source).mask = []; userClone(source).maskoffset = []; userClone(source).target = undefined; // set opacity for non range slabs on drag outside of ticket if(source.attrs.set_id && !range_set_regex.test( source.attrs.set_id )) { source.opacity(0.5); } if(source.attrs.clipFunc) { // hide red contour source.findOne(node => {return node.attrs.id === source.attrs.id+"_polyline"}).hide(); source.clipFunc(function(ctx) { var cp = this.findOne(node => { return node.attrs.id != undefined && node.attrs.id.includes("_polyline");}); if(cp && cp.attrs && cp.attrs.points) { cp = cp.attrs.points; ctx.beginPath(); for (var xxx = 0; xxx < cp.length; xxx += 2) { ctx.lineTo( cp[xxx], cp[xxx+1]); } ctx.closePath(); } }); //source.attrs.clipFunc = null; source.attrs.isClip = false; source.attrs.last_pos = null; // delete used areas once unclipped draw_clipped_areas(source); } } // clip if(target != null && target.attrs.id == "target" && can_clip) { // show red outline when clipped to plan var source_outline = source.findOne(node => {return node.attrs.id === source.attrs.id+"_polyline"}); source_outline.show(); // FIX FOR JAN if(obj.plans[current_plan].name.includes("fix")) source_outline.hide(); // hide red outline for range slabs on plan if(source.attrs.set_id && range_set_regex.test( source.attrs.set_id )) { source.findOne(node => {return node.attrs.id === source.attrs.id+"_polyline"}).hide(); } // only attach clip once if(!source.attrs.isClip) { source.attrs.isClip = true; source.opacity(1); source.clipFunc(function(ctx) { var do_clip = false; //var source_rotation = userClone(source).rotation; // hold_rotation;? var source_pos = source.attrs.x + source.attrs.y + source.attrs.rotation; if(source_pos != source.attrs.last_pos)// || (last_target && last_target.position() != source.position()) ) { do_clip = true; } source.attrs.last_pos = source_pos; if( do_clip ) { // Fix last source jumping from snap position on mouse up if(last_target && !mouse_is_down && Object.keys(snap_source_position).length > 0) { last_target.position(snap_source_position); snap_source_position = {}; } // arrays to store relative points userClone(source).mask = []; userClone(source).maskoffset = []; //var check = []; targetmask = []; } ctx.beginPath(); ctx.rotate( -source.attrs.rotation * Math.PI / 180); for (var i = 0; i < target.attrs.points.length; i += 2) { ctx.lineTo( -(source.attrs.x-target.attrs.points[i]), -(source.attrs.y-target.attrs.points[i+1])); if( do_clip ) { // store points todo dont store masks in clones object var x = -(source.attrs.x-target.attrs.points[i]); var y = -(source.attrs.y-target.attrs.points[i+1]); userClone(source).mask.push(x); userClone(source).mask.push(y); // snap to target polyline and ignore offset var x = (source.attrs.clipX-target.attrs.points[i]+target.attrs.orig.x-offset_distance_target*2); var y = (source.attrs.clipY-target.attrs.points[i+1]+target.attrs.orig.y+offset_distance_target*2); targetmask.push(x); targetmask.push(y); } } ctx.closePath(); ctx.rotate( source.attrs.rotation * Math.PI / 180); if( do_clip ) { for (var i = 0; i < target.attrs.pointsoffset.length; i += 2) { var x = -(source.attrs.x-target.attrs.pointsoffset[i]); var y = -(source.attrs.y-target.attrs.pointsoffset[i+1]); userClone(source).maskoffset.push(x); userClone(source).maskoffset.push(y); } var target_rotation = (-target.attrs.rota%360 +360)%360; var source_rotation = source.attrs.rotation % 360; if(source_rotation < 0) source_rotation += 360; userClone(source).target = target.attrs.name; userClone(source).rotation = source_rotation; userClone(source).targetrotation = target_rotation; userClone(source).origin = {x:target.attrs.orig.x, y:target.attrs.orig.y}; // target position does not change userClone(source).source = {x:source.attrs.x, y:source.attrs.y}; // slab position relative to zero userClone(source).position = {x:(source.attrs.x-target.attrs.orig.x), y:(source.attrs.y-target.attrs.orig.y)}; userClone(source).offset = {x:source.attrs.clipX, y:source.attrs.clipY}; // slab position relative to bounding box does not chnage userClone(source).centroid = {x:-(source.attrs.x-target.attrs.centroid.x), y:-(source.attrs.y-target.attrs.centroid.y)}; userClone(source).diff = source_rotation-target_rotation; draw_clipped_areas(source); } }); } last_dropbox = userClone(source); } } function draw_clipped_areas(source) { // remember last state to find changes snap_object(source); var source_target = userClone(source).target; var source_mask = userClone(source).mask; var source_rotation = userClone(source).rotation; if(fakesnapmask.length > 0) { source_mask = fakesnapmask; } fakesnapmask = []; // draw used areas var b = source.attrs.id; var c = source.attrs.instance-1; // find all places where we used the clone var basearray = []; for (var i = 0; i < plan.children.length; i++) { if(plan.children[i].attrs && plan.children[i].attrs.name == "slab_"+b+"_group") { basearray.push(plan.children[i]); } } for (var i = 0; i < slabs.children.length; i++) { if(slabs.children[i].attrs && slabs.children[i].attrs.name == "slab_"+b+"_group") { basearray.push(slabs.children[i]); break; } } for (var i = 0; i < basearray.length; i++) { // delete old shapes var old = basearray[i].findOne("#"+b+"_"+c+"_used"); var is_clip = (source_target != undefined && basearray[i].attrs.instance != source.attrs.instance); if( old != undefined ) { if(is_clip) { old.rotation(-source_rotation); old.points(source_mask); } else { // remove active area on destroy clone old.destroy(); } } else if(is_clip) { // draw active area on slabs and plan var used = new Konva.Line({ points: source_mask, strokeWidth: stroke_width_tickets, stroke: color.green, draggable: true, closed: true, id: b+"_"+c+"_used", name: "used_area", target: userClone(source).target, slab: b, inst: c+1, rotation: -source_rotation, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, }); // colorize active ticked when dragged into target, on plan and in gallery if(last_target && last_target.attrs.id == b && last_target.attrs.instance-1 == c) { used.fill(highlight_color); } // add Line to plan and slab object basearray[i].add(used); } } if(last_target) // draws alot while saving { batchDraw(slabs); batchDraw(plan); } if(!mouse_is_down) intersection_marker(); //batchDraw(stage); } function batchDraw(node) { node.batchDraw(); } function screenpos(center) { var oldScale = plan.scaleX(); return { x: center.x / oldScale - plan.x() / oldScale, y: center.y / oldScale - plan.y() / oldScale }; } function toggleLayers() { for(var layername in user.layer) { if(user.layer[layername] == false) { $(document.getElementById(layername)).children(".fas").hide(); $(document.getElementById(layername)).children(".far").show(); plan.find(node => { return node.attrs.layer && node.attrs.layer == layername;}).visible(false); } else { $(document.getElementById(layername)).children(".fas").show(); $(document.getElementById(layername)).children(".far").hide(); plan.find(node => { return node.attrs.layer && node.attrs.layer == layername;}).visible(true); } } //if(params.mode == "view") // plan.listening(false); } var user_changed = false; function find_changes() { if(params.mode == "recycle") { if(user.recycle) { let recycle_hash = []; for(let sid in user.recycle) { if(sid != "md5") { recycle_hash.push(user.recycle[sid]) } } if(!user.recycle.md5 || user.recycle.md5 != md5(JSON.stringify(recycle_hash))) { $("#save_layout").addClass("action_required"); $("#save_layout").removeClass("button_gray_disabled"); } else { $("#save_layout").removeClass("action_required"); $("#save_layout").addClass("button_gray_disabled"); } } return; } $("#save_layout").removeClass("action_required"); $("#save_layout").addClass("button_gray_disabled"); var plans_to_save = []; for (var pid in obj.plans) { var hobject = []; hobject.push(pid); // include empty plan for(var slab_id in user.clones) { for(var instance in user.clones[slab_id]) { if(user.clones[slab_id][instance] != null) { if(usage.plans[pid].includes(user.clones[slab_id][instance].target)) { var clone = user.clones[slab_id][instance]; hobject.push(user.clones[slab_id][instance].target); hobject.push(Math.round(clone.position.x*100000000)/100000000); hobject.push(Math.round(clone.position.y*100000000)/100000000); hobject.push(Math.round(clone.rotation*100000000)/100000000); } } } } if(!user.save) { user.save = {}; } var hash = md5(hobject); if(!user.save[pid]) { user.save[pid] = {"plan":pid, "md5":hash}; } else if(user.save[pid].md5 != hash) { // only save changed plans //plans_to_save.push({"plan":pid, "md5":hash}); $("#save_layout").addClass("action_required"); $("#save_layout").removeClass("button_gray_disabled"); } plans_to_save.push({"plan":pid, "md5":hash}); } /*//console.log("Plans to save: "+plans_to_save.length); if(plans_to_save.length > 0) { $("#save_layout").addClass("action_required"); $("#save_layout").removeClass("button_gray_disabled"); } else { $("#save_layout").removeClass("action_required"); $("#save_layout").addClass("button_gray_disabled"); }*/ return plans_to_save; } function save_clones() { if(params.mode == "recycle") { if(user.recycle) { let recycle_hash = []; for(let sid in user.recycle) { if(sid != "md5") { recycle_hash.push(user.recycle[sid]) } } user.recycle.md5 = md5(JSON.stringify(recycle_hash)); } var user_exclude_overlap = JSON.parse(JSON.stringify(user)); $.post( "/async/blender", {job: "autosave", mode: params.mode, user: JSON.stringify(user_exclude_overlap), params: JSON.stringify(params), url: window.location.href}) .done(function( data ) { if(data == md5(JSON.stringify(user_exclude_overlap))) { console.log("Save recycle success"); $("#save_layout").removeClass("action_required"); $("#save_layout").addClass("button_gray_disabled") } else { console.log("Save failed"); //user = JSON.parse(JSON.stringify(user)); } }); return; } if(slab_mode) { slab_view_reset(); } last_target = null; // Confirm Plan if(params.mode == "commit") { $(".loader_info").html("Confirming Plan "+obj["plans"][current_plan].name); plan.find(node => { return node.attrs.name && node.attrs.name.includes("_group");}).destroy(); slabs.find(node => { return node.attrs.name && node.attrs.name == "used_area";}).destroy(); user.clones = {}; user.save = {}; user.save[current_plan] = {"plan":current_plan, "md5":null}; } planstosave = find_changes(); calculate_used_slab_area(); if(planstosave.length > 0) { // Disable Save Button $("#exit_layout").addClass("button_gray_disabled"); $("#save_layout").removeClass("action_required"); $("#save_layout").addClass("button_gray_disabled"); $(".loader").show(); // Remember Last Plan Props last_plan_position = plan.position(); last_plan_scale = plan.scale(); last_plan = current_plan; loop_index = 0; loop_plan(); } } function loop_plan() { if(loop_index == 0) { //var user_backup = JSON.parse(JSON.stringify(user)); // Object.assign({}, user); for(var pid in planstosave) { planstosave[pid].time = Date.now(); user.save[planstosave[pid].plan] = planstosave[pid]; } // exclude overlap when saving var user_exclude_overlap = JSON.parse(JSON.stringify(user)); //Object.assign({}, user); for(var slabid in user_exclude_overlap.clones) { for(var instance in user_exclude_overlap.clones[slabid]) { if(user_exclude_overlap.clones[slabid][instance]) { if(user_exclude_overlap.clones[slabid][instance].maskoffset) { delete user_exclude_overlap.clones[slabid][instance].maskoffset; } if(user_exclude_overlap.clones[slabid][instance].locked) { delete user_exclude_overlap.clones[slabid][instance]; } } } } $.post( "/async/blender", {job: "autosave", mode: params.mode, user: JSON.stringify(user_exclude_overlap), params: JSON.stringify(params), url: window.location.href}) .done(function( data ) { if(data == md5(JSON.stringify(user_exclude_overlap))) { //console.log("Save success"); } else { console.log("Save failed"); //user = JSON.parse(JSON.stringify(user)); } }); } if(planstosave[loop_index]) { if(params.mode != "commit") { $(".loader_info").html("Save Plan: "+obj["plans"][planstosave[loop_index].plan].name); console.log("Save Plan: "+obj["plans"][planstosave[loop_index].plan].name); } // load and export plans current_plan = planstosave[loop_index].plan; load_plan(current_plan); reload_clones(true); intersection_marker(); } else { if(params.mode == "commit") { var target_url = $("[data-action=save]").attr("data-done"); if($("#scale").length != 0){ target_url += (target_url.includes("?")?"&":"?")+"scale="+$("#scale").val(); } console.log("Done Confirm", target_url); window.location.replace(target_url); //$(".loader").hide(); } else { // load current plan without saving if(last_plan != current_plan) { current_plan = planstosave[loop_index-1].plan; console.log("Load Plan: "+current_plan); load_plan(last_plan); reload_clones(); sync(last_plan_position, last_plan_scale); intersection_marker(); } $("#exit_layout").removeClass("button_gray_disabled"); $("#save_layout").removeClass("action_required"); $("#save_layout").addClass("button_gray_disabled"); console.log("HIDE 4") $(".loader").hide(); print("Blending saved"); } } } function export_plan(download = false, return_val = false, backend = "/async/blender", marketing_export = false) { if(download) { if(preloading_complete) { print("Downloading Blending, please wait"); } else { print("Please wait for all Images to be preloaded"); return; } } var expo = plan.find(node => { return (node.attrs.id == "text" || node.attrs.id == "target" || node.attrs.id == "line");}); for(var key in expo) // set print color { if(expo[key] && expo[key].attrs && expo[key].attrs.id) { if(expo[key].attrs.id == "text") { expo[key].attrs.tmpfill = expo[key].attrs.fill; expo[key].attrs.fill = "#333"; // print color } if(expo[key].attrs.id == "target") { expo[key].attrs.tmpfill = expo[key].attrs.fill; expo[key].attrs.fill = "#aaa";// print color expo[key].attrs.tmpstroke = expo[key].attrs.stroke; expo[key].attrs.stroke = "#333"; // print color } if(expo[key].attrs.id == "line") { expo[key].attrs.tmpstroke = expo[key].attrs.stroke; expo[key].attrs.stroke = "#333"; // print color } } } if(!download && !return_val) { // enable hidden layers for(var layer in user.layer) { if(user.layer[layer] == false) { $(document.getElementById(layer)).children(".fas").show(); $(document.getElementById(layer)).children(".far").hide(); plan.find(node => { return node.attrs.layer && node.attrs.layer == layer;}).visible(true); } } } if(return_val && !marketing_export){ plan.find(node => { return node.attrs.layer && node.attrs.layer == "900_Overlay";}).visible(false); } if(marketing_export) { plan.find(node => { return node.attrs.layer && node.attrs.layer == "900_Overlay";}).visible(true); } plan.scale({x:1,y:1}); var offset = 0; var export_width = obj["plans"][current_plan]["box"][2] - obj["plans"][current_plan]["box"][0]; var export_height = obj["plans"][current_plan]["box"][3] - obj["plans"][current_plan]["box"][1]; var export_x = plan.x() + obj["plans"][current_plan]["box"][0]; var export_y = plan.y() - obj["plans"][current_plan]["box"][1] - Math.abs(obj["plans"][current_plan]["box"][1] - obj["plans"][current_plan]["box"][3]); scale_stroke(last_plan_scale.x); if(!download) { var plan_bg = new Konva.Rect({ x: -plan.x()+export_x, y: -plan.y()+export_y, width: export_width, height: export_height, fill: "#fff", id: "back", listening: false, perfectDrawEnabled: false }); plan.add(plan_bg); plan_bg.moveToBottom(); } intersection_marker(); batchDraw(plan); // safari 268436456 // chrome 75000000 // 64000000 8000x8000 // 50000000 // 36000000 6000x6000 // 16000000 4000x4000 var size = 4000; var quality = 0.8; var type = "jpeg" // "png" || "jpeg" if(!marketing_export && (download || return_val)){ size = 8000; quality = 1; //quality is ignored with png type = "png"; } // MESSE if(return_val && !marketing_export){ size = 4898; quality = 0.8; type = "png"; } var rw = size/export_width; var rh = size/export_height; var ratio = rh; if(rw 0) { undo_last--; user = JSON.parse(undo_user[undo_last]); reload_clones(false, true); print("Undo"); } } else if(e == "fas fa-redo-alt") { if(undo_last < undo_user.length-1) { undo_last++; user = JSON.parse(undo_user[undo_last]); reload_clones(false, true); print("Redo"); } } else { var user_js = JSON.stringify(user); if(user_js != undo_user[undo_user.length-1]) { // highlight save button if(undo_last < undo_user.length-1) { while(undo_user.length-1 > undo_last) undo_user.pop(); } undo_user.push(user_js); if(undo_user.length > undosteps) undo_user.shift(); if(undo_last < undosteps) undo_last++; } } if(undo_last > 0) $("#toggle_undo").css('color', "#fff"); else $("#toggle_undo").css('color', "#161616"); if(undo_last < undo_user.length-1) $("#toggle_redo").css('color', "#fff"); else $("#toggle_redo").css('color', "#161616"); } function calculate_used_slab_area(all = false) { var overall_used_percent = 0; var overall_used_slabs = 0; var overall_used_sets = 0; //slabs.find(node => { return node.attrs.name == "slabnumber";}).fill(color.white); for(var set_id in obj.slabs) { //if(!all && current_set_id != set_id) // continue; var ct = 0; // count slabs var ts = 0; // total slabs var us = 0; // used slabs var total_slabs_net_area = 0; var used_slabs_net_area = 0; var used_target_area = 0; var redflag = false; for(var slab_id in obj.slabs[set_id]) { if(isNaN(slab_id)) continue; var used_target_area_by_slab = 0; let used_recycle_area_by_slab = 0; var used_percent_by_slab = 0; //var slabname = obj.slabs[set_id][slab_id].block_name+"-"+obj.slabs[set_id][slab_id].name; var slabname = obj.slabs[set_id][slab_id].name; var tickets = 0; // if we have clones in user //data.reduce((r, o) => r + +!Object.values(o).includes(null) , 0); //data.filter(obj=>!Object.values(obj).includes(null)); if(user.clones[slab_id]) { if(user.clones[slab_id].filter(function (el) {return el != null;}).length > 0) { // loop all clones in user for(var instance in user.clones[slab_id]) // 2254 { if(usage.target && user && user.clones && user.clones[slab_id] && user.clones[slab_id][instance] && usage.target[user.clones[slab_id][instance].target]) { used_target_area += usage.target[user.clones[slab_id][instance].target]; used_target_area_by_slab += usage.target[user.clones[slab_id][instance].target]; tickets++; var intersections = user.clones[slab_id][instance].ic; if(intersections && (intersections.contour > 0 || intersections.ticket > 0)) { redflag = true; } } } if(user.recycle && user.recycle[slab_id]) { used_target_area += user.recycle[slab_id].area; used_target_area_by_slab += user.recycle[slab_id].area; used_recycle_area_by_slab += user.recycle[slab_id].area; } ct++; used_slabs_net_area += usage.slab[slab_id]; used_percent_by_slab = used_target_area_by_slab/usage.slab[slab_id]*100; let used_recycle_percent_by_slab = used_recycle_area_by_slab/usage.slab[slab_id]*100; if(used_percent_by_slab > 100) used_percent_by_slab = 100; //slabname = "Slab "+obj.slabs[set_id][slab_id].name+", "+tickets+" Tickets, Yield "+Math.round(used_percent_by_slab*10)/10+"%"; //slabname = obj.slabs[set_id][slab_id].block_name+"-"+obj.slabs[set_id][slab_id].name+", Yield "+Math.round(used_percent_by_slab*10)/10+"%"; slabname += "\nYield: "+Math.round(used_percent_by_slab)+"%"// //if(used_recycle_percent_by_slab > 0) //{ // slabname += " +"+Math.round(used_recycle_percent_by_slab)+"%"; //} } else { console.log("Delete "+slab_id+" fom user Object"); delete user.clones[slab_id]; } } ts++; total_slabs_net_area += usage.slab[slab_id]; var slabname_node = slabs.find(node => { return node.attrs.name != undefined && node.attrs.name == "slabnumber" && node.attrs.id == slab_id;}); if(slabname_node) { slabname_node.text( slabname ); // colorize name by yield if(used_percent_by_slab > 0) slabname_node.fill(color.orange); if(used_percent_by_slab > params.target_yield) slabname_node.fill(color.green); } } var used_percent_slabs = used_target_area/used_slabs_net_area*100; if(isNaN(used_percent_slabs))used_percent_slabs = 0; var used_percent_set = used_target_area/total_slabs_net_area*100; if(isNaN(used_percent_set))used_percent_set = 0; $("#used_slabs_"+set_id).text("Yield "+ct+"/"+ts+" "+Math.round(used_percent_slabs)+"%"); // hide unused sets from cart if(params.mode == "recycle" && used_percent_slabs == 0) { $("#used_slabs_"+set_id).closest( ".target" ).hide(); } if(redflag) { $("#alert_"+set_id).show(); } else { $("#alert_"+set_id).hide(); } overall_used_percent+=used_percent_slabs; if(used_percent_slabs > 0) overall_used_sets++; overall_used_slabs+=ct; //$(".set_title").text(set_id); //+", Used Slabs Yield: "+used_percent_slabs+"%, Set Yield: "+Math.round(used_percent_set*10)/10+"%" } if(!user.yield) { user.yield = {}; } user.yield = { "sets": overall_used_sets, "slabs": overall_used_slabs, "percent": overall_used_percent/overall_used_sets, } $(".info_slabs").text(overall_used_slabs); $(".info_yield").text(Math.round(overall_used_percent/overall_used_sets)+"%"); } function reload_clones(exp = false, undo = false) { setTimeout(function(){ slab_mode = false; slab_view_rotate_back(); // clear plan and used_area in slabs 1ms plan.find(node => { return node.attrs.name && node.attrs.name.includes("_group");}).destroy(); slabs.find(node => { return node.attrs.name && node.attrs.name == "used_area";}).destroy(); /* var used_images = []; for (var i = 0; i < plan.children.length; i++) { if(plan.children[i].attrs && plan.children[i].attrs.layer == "100-BLENDING") { used_images.push(plan.children[i]); } } */ var inspection_targets = []; if(window.inspection && window.inspection.samples) { inspection_targets = Object.keys(window.inspection.samples); } // loop all clones for (var parent in user.clones) { if(!user.clones[parent]) continue; // find parent salb var master = slabs.findOne("#"+parent); if(master) { // loop all clone instances for (var i = 0; i < user.clones[parent].length; i++) //i = instances { // skip empty array keys if(!user.clones[parent][i]) continue; // Delete ghosts when no target is defined in user obj if(user.clones[parent][i] && user.clones[parent][i].target == undefined) { console.log("Delete Ghost: "+user.clones[parent][i]); delete user.clones[parent][i]; if(Object.keys(user.clones[parent]).length == 0) { delete user.clones[parent]; break; } continue; } // skip all clones that have no target if( !available_targets.includes(user.clones[parent][i].target) ) continue; // find target on plan var target = plan.findOne("."+user.clones[parent][i].target); if(target) { // create new clone var clone = master.clone({ instance: i+1, scaleX: 1, scaleY: 1, }); clone.find(node => { if(node.attrs.instance != i+1) node.attrs.instance = i+1; return false; }); clone.attrs.layer = "100-BLENDING"; clone.visible(true); var target_rotation = -target.attrs.rota%360+360; var diff = userClone(clone).diff; var clone_x = userClone(clone).position.x; var clone_y = userClone(clone).position.y; var target_x = target.attrs.orig.x; var target_y = target.attrs.orig.y; var x = clone_x; var y = clone_y; var r = (target_rotation-(userClone(clone).targetrotation)) * (Math.PI / 180); // ??? read target_rotation before write? clone_x = x*Math.cos(r) - y*Math.sin(r); clone_y = x*Math.sin(r) + y*Math.cos(r); clone.position({x:(clone_x+target_x), y:(clone_y+target_y)}); clone.rotation(diff+target_rotation); //user.clones[parent][i].targetrotation = target_rotation; >> //userClone(clone).targetrotation = target_rotation; //userClone(clone).rotation = clone.rotation(); clone.findOne(node => {return node.attrs.id === clone.attrs.id+"_polyline"}).show(); //clip.add(clone); if(inspection_targets.includes(target.attrs.name)) { var tn = target.attrs.name; var is = window.inspection.samples; var ic = color.green; if(is[tn].details == 0 || is[tn].size == 0 || is[tn].veining == 0) { ic = color.red; } var inspected = new Konva.Line({ points: userClone(clone).mask, strokeWidth: stroke_width_tickets, stroke: ic, //dash: [3, 3], closed: true, name: "inspected", listening: false }); clone.add(inspected); } //console.log("reload_clones "+userClone(clone).target,userClone(clone).mask); clip_to_plan(clone, target); //clone.moveTo(plan); plan.add(clone); } else { //console.log("reload_clones other "+user.clones[parent][i].target, user.clones[parent][i].mask); // draw dashed area of tickets var parent_slab = slabs.findOne(".slab_"+parent+"_group"); var used = new Konva.Line({ points: user.clones[parent][i].mask, strokeWidth: stroke_width_tickets, //stroke: color.green, id: parent+"_"+i+"_used", dash: [3, 3], closed: true, name: "used_area", //target: userClone(source).target, slab: parent_slab.attrs.id, inst: i+1, rotation: -user.clones[parent][i].rotation, perfectDrawEnabled: false, shadowForStrokeEnabled: false, hitStrokeWidth: 0, //listening: false }); parent_slab.add(used); //parent_slab.draw(); } } } else { console.log("Missing Slab deleted");//: "+parent); // if we cant not find a parent slab, delete from user object delete user.clones[parent]; } } show_transformer(false); requestAnimationFrame(() => { setTimeout(() => { loading_complete(); if(exp) // wait for all images to be loaded??? { export_plan(); } else { intersection_check_all(); // hide loader if not saving plans if(planstosave.length == 0) { $(".loader").hide(); } } }, 10) }); //console.log("reload_clones => complete "+Math.round(performance.now()-timestart)+" ms"); },10); } function load_image(clone) { if(!clone.children || !clone.children[0]) return var required_imagesize = clone.children[0].attrs.width * plan.scaleX(); if(required_imagesize < small_image){ required_imagesize = small_image; } else{ required_imagesize = large_image; } //console.log(clone.children[0].attrs.width+" * "+plan.scaleX()+clone.children[0].attrs.imagesize+" == "+required_imagesize) if(clone.children[0].attrs.imagesize == required_imagesize) return; clone.children[0].attrs.imagesize = required_imagesize; images_to_load++; //console.log("load_image"); var img = new Image(); img.crossOrigin = 'Anonymous'; img.decoding = 'async'; img.onload = function(evt) { var scale = clone.children[0].width()/required_imagesize; clone.children[0].setAttr("fillPatternImage", img); clone.children[0].setAttr("fillPatternRepeat", "no-repeat"); clone.children[0].setAttr("fillPatternScaleX", scale); clone.children[0].setAttr("fillPatternScaleY", scale); clone.children[0].setAttr("fillPatternY", clone.children[0].height()); clone.children[0].setAttr("imagesize", required_imagesize); img.onload = null;// unbind onload img.onerror = null;// unbind onload images_to_load--; if(images_to_load == 0) { batchDraw(plan); } }; img.onerror = function(evt) { console.log("Img Onload Error: ",img,evt); }; if(sources[clone.children[0].attrs.id]) { if(required_imagesize == small_image) { img.src = sources[clone.children[0].attrs.id].thumb; } if(required_imagesize == large_image) { img.src = sources[clone.children[0].attrs.id].image; } } } function load_clones(callback) { $.post( "/async/blender", {job: "load", mode: params.mode, params: JSON.stringify(params), url: window.location.href}) .done(function( data ) { try{ var tmp = JSON.parse(data); if(Array.isArray(tmp.clones)) { var object = {}; for (var i = 0; i < tmp.clones.length; i++) { if(tmp.clones[i] != null) { object[i] = tmp.clones[i]; } } tmp.clones = object; } if(tmp.annotation) delete tmp.annotation; user = tmp; if(user.hash) { delete user.hash; } //console.log(user); //autosave_tmp = Object.assign({}, user); if(delete_unused_targets != false){ // clean unused targets from clones for(var slab_id in user.clones){ for(var instance in user.clones[slab_id]){ if(user.clones[slab_id][instance]){ if(!available_targets.includes(user.clones[slab_id][instance].target)){ if(delete_unused_targets == undefined){ delete_unused_targets = window.confirm("We have found targets for plans that are no longer included in the current plans. It is recommended to clean up these old targets, otherwise the collision detection can deliver incorrect results.\n\nShould these targets be cleaned up now?"); } if(delete_unused_targets){ console.log("Delete unused Target"+user.clones[slab_id][instance].target+" from Clone "+slab_id); delete user.clones[slab_id][instance]; }else{ break; } } } } } } find_changes(); callback(); } catch(error) { console.log("Failed to load Clones"); user = {clones:{}, layer:{}}; callback(); }; }); } function load_images() { //console.log("load_images => init "+Math.round(performance.now()-timestart)+" ms"); load_clones(function() { var images = {}; var loadedImages = 0; var numImages = Object.keys(sources).length; // hide loader if no slabs selected from gallery if(numImages == 0){ console.log("HIDE 3") $(".loader").hide(); } for (var sid in sources) { images[sid] = new Image(); images[sid].crossOrigin = 'Anonymous'; images[sid].decoding = 'async'; images[sid].alt = sid; images[sid].onload = function(evt) { images[this.alt].onload = null; images[this.alt].onerror = null; var imagebox = slabs.findOne(".slab_"+this.alt+"_imagebox"); if(imagebox) { var scale = Math.abs(imagebox.width())/small_image; if( Math.abs(imagebox.height()) > Math.abs(imagebox.width()) ) { scale = Math.abs(imagebox.height())/small_image; } imagebox.setAttr("fillPatternImage", images[this.alt]); imagebox.setAttr("fillPatternRepeat", "no-repeat"); imagebox.setAttr("fillPatternScaleX", scale); imagebox.setAttr("fillPatternScaleY", scale); imagebox.setAttr("fillPatternY", imagebox.height()); imagebox.setAttr("imagesize", small_image); imagebox.moveToBottom(); if(this.alt == 27105 || this.alt == 27104) { console.log("loading_images => onload"+this.alt, imagebox, images[this.alt].src, scale); } } else { console.log("Error finding "+".slab_"+this.alt+"_imagebox"); } if (++loadedImages == numImages) { finish_loading(); } else { $(".loader_info").html("Loading Slabs ("+loadedImages+" / "+numImages+")"); } }; images[sid].onerror = function(evt) { console.log("Load Image 404: "+images[this.alt].src); images[this.alt].onload = null; images[this.alt].onerror = null; if (++loadedImages == numImages) { finish_loading(); } else { $(".loader_info").html("Loading Slabs ("+loadedImages+" / "+numImages+")"); } }; images[sid].src = sources[sid].thumb; } }); } function finish_loading() { //console.log("load_images => complete "+Math.round(performance.now()-timestart)+" ms"); $(".loader_info").html("Loading Plan"); requestAnimationFrame(() => { reload_clones(); setTimeout(() => { preload_images(); }, 10); }); } function get_used_clones() { for (var set_name in obj.slabs) { for(var slab_id in obj.slabs[set_name]) { if(isNaN(slab_id)) continue; if(user.clones[slab_id]) { for (var instance in user.clones[slab_id]) { if(user.clones[slab_id][instance] != null) { if(isNaN(usage.tickets[slab_id])) { usage.tickets[slab_id] = 0; } usage.tickets[slab_id] += usage.target[user.clones[slab_id][instance].target]; } } } } } } function loading_complete() { //console.log("loading_complete => complete "+Math.round(performance.now()-timestart)+" ms"); // move layer 400 to top plan.find(node => { return node.attrs.layer && node.attrs.layer.startsWith("4");}).moveToTop(); calculate_used_slab_area(); //fit_to_screen(); toggleLayers(); intersection_check(); intersection_marker(); get_used_clones(); reload_recycle_polygons(); loading = false; batchDraw(stage); setTimeout(() => { //console.log("loading_complete => fit_to_screen") fit_to_screen(); }, 100) //console.log("preload_images => init "+Math.round(performance.now()-timestart)+" ms") } function preload_images() { print("Preloading Slabs", 300000) var images = {}; var loadedImages = 0; var numImages = Object.keys(sources).length; for (var sid in sources) { images[sid] = new Image(); images[sid].crossOrigin = 'Anonymous'; images[sid].decoding = 'async'; images[sid].alt = sid; images[sid].onload = function(evt) { print("Preloading Slabs "+(numImages-loadedImages-1) ) if(++loadedImages == numImages) { print(""); preloading_complete = true; //console.log("preload_images => complete "+Math.round(performance.now()-timestart)+" ms"); } } images[sid].onerror = function(evt){ console.log("Preload Image 404: "+images[this.alt].src); print(""); preloading_complete = true; if(++loadedImages == numImages) { //console.log("preload_images => complete "+Math.round(performance.now()-timestart)+" ms"); } } images[sid].src = sources[sid].image; } } function userClone(source) { if(user.clones[source.attrs.id] != undefined) return user.clones[source.attrs.id][source.attrs.instance-1]; else return undefined; } function relative_pointer_position(node) { var transform = node.getAbsoluteTransform().copy(); transform.invert(); var pos = node.getStage().getPointerPosition(); return transform.point(pos); } function print(x, duration = 5000) { $(".navi_input").val(x); if(console_timeout) clearTimeout(console_timeout); console_timeout = setTimeout(function(){ if(!$(".navi_input").is(':focus')) $(".navi_input").val(""); }, duration); }