From 2fef03a69c75d8d97a7f475076e9bfbdac5f0606 Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Sat, 30 Mar 2024 02:18:58 +1100 Subject: [PATCH 1/7] Matrix intersection quantity --- plot/matrix/matrix.view.ts | 81 +++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/plot/matrix/matrix.view.ts b/plot/matrix/matrix.view.ts index e32a9a0..13b7b49 100644 --- a/plot/matrix/matrix.view.ts +++ b/plot/matrix/matrix.view.ts @@ -109,6 +109,24 @@ namespace $.$$ { return this.json_master().payload.links.slice().sort( (a, b) => a.value - b.value ) } + @ $mol_mem + links_map() { + const map = new Map< string, typeof $mpds_visavis_plot_matrix_json_link['Value'][] >() + + this.links().forEach( l => { + const prev = map.get( l.cmt ) ?? [] + map.set( l.cmt, [ ...prev, l ] ) + } ) + + return map + } + + @ $mol_mem_key + intersection_label( cmt: string ): string { + const quantity = this.links_map().get( cmt )?.length! + return quantity > 1 ? String( quantity ) : '' + } + links_value_min() { return this.links()[0].value } @@ -231,22 +249,29 @@ namespace $.$$ { } @ $mol_mem_key - draw_cells(node: SVGElement, row: Matrix_cell[]) { + draw_cells(row_node: SVGElement, cells: Matrix_cell[]) { const that = this - d3.select(node) + + const range = this.range() + const rangeBand = range.rangeBand() + + const enters = d3.select(row_node) .selectAll('.cell') - .data(row.filter((d: any) => d.z)) + .data(cells.filter((d: any) => d.z)) // .join('rect') // for new d3 version - .enter().append('rect') - .attr('class', (d: any) => d.nonformer ? 'nonformer cell' : 'cell') + .enter() + + const rects = enters.append('rect') + + rects.attr('class', (d: any) => d.nonformer ? 'nonformer cell' : 'cell') .attr('id', (d: any) => 'c_' + this.nodes()[d.x].num.toString() + '_' + this.nodes()[d.y].num.toString()) - .attr('x', (d: any) => this.range()(d.x) as any) - // .attr('width', this.range().bandwidth()) // for new d3 version - // .attr('height', this.range().bandwidth()) // for new d3 version - .attr('width', this.range().rangeBand()) - .attr('height', this.range().rangeBand()) + .attr('x', (d: any) => range(d.x) ) + // .attr('width', range.bandwidth()) // for new d3 version + // .attr('height', range.bandwidth()) // for new d3 version + .attr('width', rangeBand) + .attr('height', rangeBand) .style('fill-opacity', (d: any) => this.opacity(d.z)) - .style('fill', (d: any) => this.color(d.z, d.cmp) ) + .style('fill', (d: any) => that.intersection_label( d.cmt ) ? 'gray' : this.color(d.z, d.cmp)) .on('mouseover', function (this: any, event: PointerEvent) { const cell_data = d3.select(this).data()[0] as Matrix_cell @@ -268,7 +293,16 @@ namespace $.$$ { that.matrix_click( { cmt: cell_data.cmt } ) } ) - .append('svg:title').text((cell: any) => this.svg_title_text(cell)) + rects.append('svg:title').text((cell: any) => this.svg_title_text(cell)) + + enters.append('text') + .text((cell: any) => that.intersection_label(cell.cmt)) + .attr('x', (d: any) => range(d.x) + rangeBand / 2 ) + .attr('dy', '.85em') + .attr('text-anchor', 'middle') + .attr('font-weight', 'bold') + .attr('pointer-events', 'none') + } @ $mol_mem @@ -278,10 +312,13 @@ namespace $.$$ { const svg_element = $mol_wire_sync( document ).createElementNS( 'http://www.w3.org/2000/svg', 'svg' ) const svg = d3.select(svg_element) - svg.attr('width', this.size() + this.axis_width()) - .attr('height', this.size() + this.axis_width()) + const size = this.size() + const rangeBand = this.range().rangeBand() + + svg.attr('width', size + this.axis_width()) + .attr('height', size + this.axis_width()) // .style('font-size', this.range().bandwidth()) // for new d3 version - .style('font-size', this.range().rangeBand() + 'px') + .style('font-size', rangeBand + 'px') .style('letter-spacing', '1px') const group = svg[ svg.select('g').empty() ? 'append' : 'select' ]('g') @@ -291,8 +328,8 @@ namespace $.$$ { group.append('rect') .attr('class', 'bgmatrix') - .attr('width', this.size()) - .attr('height', this.size()); + .attr('width', size) + .attr('height', size); const draw_cells = (node: any, row: Matrix_cell[]) => this.draw_cells(node, row) @@ -302,15 +339,15 @@ namespace $.$$ { .enter().append('g') .attr('class', 'row') .attr('transform', (d: any, i: number) => 'translate(0,' + this.range()(i as any) + ')' ) - .each(function (this: any, row: any) { draw_cells(this, row) }) + .each(function (this: any, cells: any) { draw_cells(this, cells) }) row.append('line') - .attr('x2', this.size()); + .attr('x2', size); row.append('text') .attr('x', -6) // .attr('y', this.range().bandwidth() / 2) // for new d3 version - .attr('y', this.range().rangeBand() / 2) + .attr('y', rangeBand / 2) .attr('dy', '.32em') .attr('text-anchor', 'end') .text((d: any, i: any)=> this.nodes()[i].name) @@ -323,12 +360,12 @@ namespace $.$$ { .attr('transform', (d: any, i: any)=> 'translate(' + this.range()(i) + ')rotate(-90)'); column.append('line') - .attr('x1', -this.size()); + .attr('x1', -size); column.append('text') .attr('x', 6) // .attr('y', this.range().bandwidth() / 2) // for new d3 version - .attr('y', this.range().rangeBand() / 2) + .attr('y', rangeBand / 2) .attr('dy', '.32em') .attr('text-anchor', 'start') .text((d: any, i: any) => this.nodes()[i].name); From 579c31b97347b79749fb21d98385b484891165c6 Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Sat, 30 Mar 2024 02:30:55 +1100 Subject: [PATCH 2/7] Matrix intersection_only --- plot/matrix/matrix.view.tree | 6 +++--- plot/matrix/matrix.view.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plot/matrix/matrix.view.tree b/plot/matrix/matrix.view.tree index 74cc7da..32becc3 100644 --- a/plot/matrix/matrix.view.tree +++ b/plot/matrix/matrix.view.tree @@ -64,9 +64,9 @@ $mpds_visavis_plot_matrix $mol_view hint \Continuous solid solutions and complete insolubility systems title \Show non-formers checked? <=> nonformers_checked? false - <= Difference_on $mol_check_box - title \Show difference - checked? <=> difference_checked? false + <= Intersection_on $mol_check_box + title \Show intersection + checked? <=> intersection_only? false <= Order $mol_labeler title \Sort by Content <= Order_switch $mol_switch diff --git a/plot/matrix/matrix.view.ts b/plot/matrix/matrix.view.ts index 13b7b49..e076452 100644 --- a/plot/matrix/matrix.view.ts +++ b/plot/matrix/matrix.view.ts @@ -52,7 +52,7 @@ namespace $.$$ { setup() { return [ ... this.json().payload.fixel ? [ this.Fixel() ] : [], - this.multi_jsons() ? this.Difference_on() : this.Nonformers(), + this.multi_jsons() ? this.Intersection_on() : this.Nonformers(), ... this.show_setup() ? [ this.Order() ] : [], ] } @@ -248,8 +248,8 @@ namespace $.$$ { return title } - @ $mol_mem_key - draw_cells(row_node: SVGElement, cells: Matrix_cell[]) { + @ $mol_action + draw_cells(row_node: SVGElement, cells: Matrix_cell[], intersection_only: boolean) { const that = this const range = this.range() @@ -257,7 +257,7 @@ namespace $.$$ { const enters = d3.select(row_node) .selectAll('.cell') - .data(cells.filter((d: any) => d.z)) + .data(cells.filter(d => d.z && ( !intersection_only || that.intersection_label( d.cmt ) ))) // .join('rect') // for new d3 version .enter() @@ -331,7 +331,7 @@ namespace $.$$ { .attr('width', size) .attr('height', size); - const draw_cells = (node: any, row: Matrix_cell[]) => this.draw_cells(node, row) + const draw_cells = (node: any, row: Matrix_cell[]) => this.draw_cells(node, row, this.intersection_only()) const row = group.selectAll('.row') .data(this.matrix()) From 3ac6e0335a5be4a1566ff33b622777ed3ea782a1 Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Sat, 30 Mar 2024 02:44:25 +1100 Subject: [PATCH 3/7] Matrix cells title with cmp labels --- plot/matrix/matrix.view.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plot/matrix/matrix.view.ts b/plot/matrix/matrix.view.ts index e076452..c6624bd 100644 --- a/plot/matrix/matrix.view.ts +++ b/plot/matrix/matrix.view.ts @@ -241,11 +241,15 @@ namespace $.$$ { if (!cell.cmt) return '' const text = `${cell.cmt}: ${cell.z}` - const title = !this.heatmap() - ? `${text} ${cell.z === 1 ? 'entry' : 'entries'}` - : text - return title + if( this.heatmap() ) return text + + const links = this.links_map().get( cell.cmt ) + const title = `${text} ${cell.z === 1 ? 'entry' : 'entries'}` + + if( links?.length == 1 ) return title + + return `${title} (${ links?.map( l => this.cmp_labels()[ l.cmp ?? 0 ] ).join('; ') })` } @ $mol_action From 5378dcad800e3522d796aab4b9ece64cfbc0ad36 Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Sat, 30 Mar 2024 04:27:12 +1100 Subject: [PATCH 4/7] Cube intersections --- plot/cube/cube.view.tree | 6 +- plot/cube/cube.view.ts | 113 ++++++++++++++++++++++++++++------ plot/legend/cmp/cmp.view.tree | 5 +- plot/legend/cmp/cmp.view.ts | 8 ++- 4 files changed, 106 insertions(+), 26 deletions(-) diff --git a/plot/cube/cube.view.tree b/plot/cube/cube.view.tree index 7ebd40f..0c3c0a5 100644 --- a/plot/cube/cube.view.tree +++ b/plot/cube/cube.view.tree @@ -67,9 +67,9 @@ $mpds_visavis_plot_cube $mol_view hint \Continuous solid solutions and complete insolubility systems title \Show non-formers checked? <=> nonformers_checked? false - <= Difference_on $mol_check_box - title \Show difference - checked? <=> difference_checked? false + <= Intersection_on $mol_check_box + title \Show intersection + checked? <=> intersection_only? false <= X_order $mol_labeler title \X sort by Content <= X_order_select $mol_select diff --git a/plot/cube/cube.view.ts b/plot/cube/cube.view.ts index 2aecb05..44b4c97 100644 --- a/plot/cube/cube.view.ts +++ b/plot/cube/cube.view.ts @@ -29,7 +29,7 @@ namespace $.$$ { setup() { return [ ... this.show_fixel() ? [ this.Fixel() ] : [], - this.multi_jsons() ? this.Difference_on() : this.Nonformers(), + this.multi_jsons() ? this.Intersection_on() : this.Nonformers(), ... this.show_setup() ? [ this.X_order(), this.Y_order(), this.Z_order() ] : [], ] } @@ -138,27 +138,103 @@ namespace $.$$ { } @ $mol_mem - multi_dataset(): any[] | null { - if( ! this.multi_jsons() ) return null + scatters() { - this.nonformers_checked( false ) + const values: Map< [string/*label*/, number/*json index*/], number/*v*/ > = new Map() + + const entries: Map< string/*point label*/, number[]/*json indexes*/> = new Map() + + const labels: Set< string > = new Set() + let points_x: number[] = [] + let points_y: number[] = [] + let points_z: number[] = [] + + this.multi_jsons().map( (json: any, index: number) => { + + const points = $mpds_visavis_plot_cube_json( json ).payload.points + points.labels.forEach( (label, i)=> { + + const prev = entries.get( label ) ?? [] + entries.set( label, [ ...prev, index ] ) + values.set( [ label, index ], points.v[i] ) + + if( !labels.has( label ) ) { + labels.add( label ) + points_x.push( points.x[i] ) + points_y.push( points.y[i] ) + points_z.push( points.z[i] ) + } - return this.multi_jsons().map( (json: any, index: number) => { - const json_valid = $mpds_visavis_plot_cube_json( json ) + } ) + + } ) + + const converted = this.convert_to_axes( + points_x, points_y, points_z, + this.x_sort() as Prop_name, + this.y_sort() as Prop_name, + this.z_sort() as Prop_name, + ) + + const points: Map< string, {x:number, y:number, z:number} > = new Map + ;[...labels].forEach( ( label, i ) => points.set( label, { + x: converted.x[ i], + y: converted.y[ i ] , + z: converted.z[ i ], + }) ) + + const new_scatter = ( index: number | 'intersection' )=> { return { ...this.scatter3d_common(), - text: json_valid.payload.points.labels, - marker: this.marker( index ), - ...this.convert_to_axes( - json_valid.payload.points.x, - json_valid.payload.points.y, - json_valid.payload.points.z, - this.x_sort() as Prop_name, - this.y_sort() as Prop_name, - this.z_sort() as Prop_name, - ) + marker: index == 'intersection' ? {color: "#303030", size: 5, opacity: 0.9} : this.marker( index ), + x: [] as number[], + y: [] as number[], + z: [] as number[], + v: [] as number[], + text: [] as string[], + } + } + + const scatters_once: Map> = new Map() + const intersects = new_scatter( 'intersection' ) + + entries.forEach( ( entry, label )=> { + + const point = points.get( label )! + + let scatter = intersects + if( entry.length == 1 ) { + const index = entry[ 0 ] + scatter = scatters_once.get( index ) ?? new_scatter( index ) + scatters_once.set( index, scatter ) + + scatter.v.push( values.get( [ label, index ] )! ) } + + scatter.text.push( label ) + scatter.x.push( point.x ) + scatter.y.push( point.y ) + scatter.z.push( point.z ) + } ) + + return { intersects, scatters_once } + + } + + @ $mol_mem + multi_dataset(): any[] | null { + + if( ! this.multi_jsons() ) return null + + this.nonformers_checked( false ) + const { intersects, scatters_once } = this.scatters() + + return [ + intersects, + ... this.intersection_only() ? [] : scatters_once.values() + ] + } @ $mol_mem @@ -168,9 +244,10 @@ namespace $.$$ { @ $mol_mem data_shown() { + const dataset = this.multi_dataset() return [ ... this.nonformers_checked() ? [ this.data_nonformers() ] : [], - ... this.multi_dataset() ? this.multi_dataset()! : [ this.data() ], + ... dataset ? dataset! : [ this.data() ], ] } @@ -276,7 +353,7 @@ namespace $.$$ { z_op?: any ){ //console.log(x_src, y_src, z_src, x_sort, y_sort, z_sort, x_op, y_op, z_op); - var converted = {'x': [], 'y': [], 'z': []}; + var converted: {x: number[], y: number[], z: number[]} = {'x': [], 'y': [], 'z': []} if (x_op){ var x_temp = []; diff --git a/plot/legend/cmp/cmp.view.tree b/plot/legend/cmp/cmp.view.tree index aef3282..fa0a8fb 100644 --- a/plot/legend/cmp/cmp.view.tree +++ b/plot/legend/cmp/cmp.view.tree @@ -3,8 +3,9 @@ $mpds_visavis_plot_legend_cmp $mol_view <= Label*0 $mpds_visavis_plot_legend_cmp_label label <= label* \ background <= background* \ - \vs. - <= Label*1 + <= Intersection $mpds_visavis_plot_legend_cmp_label + label \Intersection + background \gray labels / colorset / diff --git a/plot/legend/cmp/cmp.view.ts b/plot/legend/cmp/cmp.view.ts index d16841a..64ceb3f 100644 --- a/plot/legend/cmp/cmp.view.ts +++ b/plot/legend/cmp/cmp.view.ts @@ -2,9 +2,11 @@ namespace $.$$ { export class $mpds_visavis_plot_legend_cmp extends $.$mpds_visavis_plot_legend_cmp { sub(): readonly any[] { - return this.labels().length == 2 - ? super.sub() - : this.labels().map( ( label, ind ) => this.Label( ind ) ) + const labels = this.labels() + return [ + ...labels.map( ( label, ind ) => this.Label( ind ) ), + ...labels.length > 1 ? [ this.Intersection() ] : [] + ] } label( index: number ): string { From b8eb9df97e242dcb1f7ec074ba6740c4c01e8dfc Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Sat, 30 Mar 2024 04:36:27 +1100 Subject: [PATCH 5/7] chore --- plot/cube/cube.view.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plot/cube/cube.view.ts b/plot/cube/cube.view.ts index 44b4c97..95a389b 100644 --- a/plot/cube/cube.view.ts +++ b/plot/cube/cube.view.ts @@ -167,19 +167,19 @@ namespace $.$$ { } ) - } ) + } ) const converted = this.convert_to_axes( - points_x, points_y, points_z, - this.x_sort() as Prop_name, - this.y_sort() as Prop_name, + points_x, points_y, points_z, + this.x_sort() as Prop_name, + this.y_sort() as Prop_name, this.z_sort() as Prop_name, ) - const points: Map< string, {x:number, y:number, z:number} > = new Map + const points: Map< string, { x: number, y: number, z: number } > = new Map ;[...labels].forEach( ( label, i ) => points.set( label, { - x: converted.x[ i], - y: converted.y[ i ] , + x: converted.x[ i ], + y: converted.y[ i ], z: converted.z[ i ], }) ) From 13a7c98672292f2c3614a056529378f53c8dbf97 Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Sat, 30 Mar 2024 10:40:49 +1100 Subject: [PATCH 6/7] array push instead recreating --- plot/cube/cube.view.ts | 6 +++--- plot/matrix/matrix.view.ts | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/plot/cube/cube.view.ts b/plot/cube/cube.view.ts index 95a389b..6c5323f 100644 --- a/plot/cube/cube.view.ts +++ b/plot/cube/cube.view.ts @@ -140,7 +140,7 @@ namespace $.$$ { @ $mol_mem scatters() { - const values: Map< [string/*label*/, number/*json index*/], number/*v*/ > = new Map() + const values: Map< [ label: string, json_index: number ], number/*v*/ > = new Map() const entries: Map< string/*point label*/, number[]/*json indexes*/> = new Map() @@ -154,8 +154,8 @@ namespace $.$$ { const points = $mpds_visavis_plot_cube_json( json ).payload.points points.labels.forEach( (label, i)=> { - const prev = entries.get( label ) ?? [] - entries.set( label, [ ...prev, index ] ) + entries.get( label )?.push( index ) ?? entries.set( label, [ index ] ) + values.set( [ label, index ], points.v[i] ) if( !labels.has( label ) ) { diff --git a/plot/matrix/matrix.view.ts b/plot/matrix/matrix.view.ts index c6624bd..9586f37 100644 --- a/plot/matrix/matrix.view.ts +++ b/plot/matrix/matrix.view.ts @@ -114,8 +114,7 @@ namespace $.$$ { const map = new Map< string, typeof $mpds_visavis_plot_matrix_json_link['Value'][] >() this.links().forEach( l => { - const prev = map.get( l.cmt ) ?? [] - map.set( l.cmt, [ ...prev, l ] ) + map.get( l.cmt )?.push( l ) ?? map.set( l.cmt, [ l ] ) } ) return map From f8c1852b4c3b47986d07cfc71f43789c59d4525b Mon Sep 17 00:00:00 2001 From: Evgeny Blokhin Date: Sat, 30 Mar 2024 13:40:16 +0100 Subject: [PATCH 7/7] Polish labels --- plot/legend/cmp/cmp.view.css.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plot/legend/cmp/cmp.view.css.ts b/plot/legend/cmp/cmp.view.css.ts index f3dbec7..2f75184 100644 --- a/plot/legend/cmp/cmp.view.css.ts +++ b/plot/legend/cmp/cmp.view.css.ts @@ -2,7 +2,7 @@ namespace $.$$ { $mol_style_define( $mpds_visavis_plot_legend_cmp, { - lineHeight: '1', + lineHeight: '1.3', position: 'absolute', left: 0, @@ -11,7 +11,7 @@ namespace $.$$ { padding: { bottom: $mol_gap.space, }, - + gap: $mol_gap.block, align: { items: 'center', @@ -25,6 +25,8 @@ namespace $.$$ { $mol_style_define( $mpds_visavis_plot_legend_cmp_label, { color: 'white', + fontSize: '0.8em', + fontStyle: 'italic', padding: { left: $mol_gap.space, right: $mol_gap.space,