Factor curve-dispersion sorting into primary loop
We can determine the major curve (in view) in the first pass of all `Viz`s so drop the 2nd loop and thus the `mxmn_groups: dict`. Also simplifies logic for the case of only one (the major) curve in view.multichartz
							parent
							
								
									404a5e1263
								
							
						
					
					
						commit
						2aa5137283
					
				| 
						 | 
					@ -908,7 +908,6 @@ class ChartView(ViewBox):
 | 
				
			||||||
        profiler = Profiler(
 | 
					        profiler = Profiler(
 | 
				
			||||||
            msg=f'ChartView.interact_graphics_cycle() for {self.name}',
 | 
					            msg=f'ChartView.interact_graphics_cycle() for {self.name}',
 | 
				
			||||||
            # disabled=not pg_profile_enabled(),
 | 
					            # disabled=not pg_profile_enabled(),
 | 
				
			||||||
 | 
					 | 
				
			||||||
            # ms_threshold=ms_slower_then,
 | 
					            # ms_threshold=ms_slower_then,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            disabled=True,
 | 
					            disabled=True,
 | 
				
			||||||
| 
						 | 
					@ -941,94 +940,24 @@ class ChartView(ViewBox):
 | 
				
			||||||
                tuple[float, float],
 | 
					                tuple[float, float],
 | 
				
			||||||
            ] = {}
 | 
					            ] = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # collect certain flows have grapics objects **in seperate
 | 
					 | 
				
			||||||
            # plots/viewboxes** into groups and do a common calc to
 | 
					 | 
				
			||||||
            # determine auto-ranging input for `._set_yrange()`.
 | 
					 | 
				
			||||||
            # this is primarly used for our so called "log-linearized
 | 
					 | 
				
			||||||
            mxmn_groups: dict[
 | 
					 | 
				
			||||||
                set[Viz],
 | 
					 | 
				
			||||||
                set[Viz, tuple[float, float]],
 | 
					 | 
				
			||||||
            ] = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for name, viz in chart._vizs.items():
 | 
					 | 
				
			||||||
                if not viz.render:
 | 
					 | 
				
			||||||
                    # print(f'skipping {flow.name}')
 | 
					 | 
				
			||||||
                    continue
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # pass in no array which will read and render from the last
 | 
					 | 
				
			||||||
                # passed array (normally provided by the display loop.)
 | 
					 | 
				
			||||||
                in_view, i_read_range, _ = viz.update_graphics()
 | 
					 | 
				
			||||||
                if not in_view:
 | 
					 | 
				
			||||||
                    continue
 | 
					 | 
				
			||||||
                profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                out = viz.maxmin(i_read_range=i_read_range)
 | 
					 | 
				
			||||||
                if out is None:
 | 
					 | 
				
			||||||
                    log.warning(f'No yrange provided for {name}!?')
 | 
					 | 
				
			||||||
                    return
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ixrng,
 | 
					 | 
				
			||||||
                    _,
 | 
					 | 
				
			||||||
                    yrange
 | 
					 | 
				
			||||||
                ) = out
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                pi = viz.plot
 | 
					 | 
				
			||||||
                mxmn = mxmns_by_common_pi.get(pi)
 | 
					 | 
				
			||||||
                if mxmn:
 | 
					 | 
				
			||||||
                    yrange = mxmns_by_common_pi[pi] = (
 | 
					 | 
				
			||||||
                        min(yrange[0], mxmn[0]),
 | 
					 | 
				
			||||||
                        max(yrange[1], mxmn[1]),
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    mxmns_by_common_pi[pi] = yrange
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # TODO: a better predicate here, likely something
 | 
					 | 
				
			||||||
                # to do with overlays and their settings..
 | 
					 | 
				
			||||||
                if (
 | 
					 | 
				
			||||||
                    viz.is_ohlc
 | 
					 | 
				
			||||||
                ):
 | 
					 | 
				
			||||||
                    # print(f'adding {viz.name} to overlay')
 | 
					 | 
				
			||||||
                    mxmn_groups[viz.name] = out
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    pi.vb._set_yrange(yrange=yrange)
 | 
					 | 
				
			||||||
                    profiler(
 | 
					 | 
				
			||||||
                        f'{viz.name}@{chart_name} `Viz.plot.vb._set_yrange()`'
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            profiler(f'<{chart_name}>.interact_graphics_cycle({name})')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # if no overlays, set lone chart's yrange and short circuit
 | 
					 | 
				
			||||||
            if (
 | 
					 | 
				
			||||||
                len(mxmn_groups) < 2
 | 
					 | 
				
			||||||
            ):
 | 
					 | 
				
			||||||
                print(f'ONLY ranging major: {viz.name}')
 | 
					 | 
				
			||||||
                for viz_name, out in mxmn_groups.items():
 | 
					 | 
				
			||||||
                    (
 | 
					 | 
				
			||||||
                        ixrng,
 | 
					 | 
				
			||||||
                        read_slc,
 | 
					 | 
				
			||||||
                        yrange,
 | 
					 | 
				
			||||||
                    ) = out
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    # determine start datum in view
 | 
					 | 
				
			||||||
                    viz = chart._vizs[viz_name]
 | 
					 | 
				
			||||||
                    viz.plot.vb._set_yrange(
 | 
					 | 
				
			||||||
                        yrange=yrange,
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # proportional group auto-scaling per overlay set.
 | 
					            # proportional group auto-scaling per overlay set.
 | 
				
			||||||
            # -> loop through overlays on each multi-chart widget
 | 
					            # -> loop through overlays on each multi-chart widget
 | 
				
			||||||
            #    and scale all y-ranges based on autoscale config.
 | 
					            #    and scale all y-ranges based on autoscale config.
 | 
				
			||||||
            # -> for any "group" overlay we want to dispersion normalize
 | 
					            # -> for any "group" overlay we want to dispersion normalize
 | 
				
			||||||
            #    and scale minor charts onto the major chart: the chart
 | 
					            #    and scale minor charts onto the major chart: the chart
 | 
				
			||||||
            #    with the most dispersion in the set.
 | 
					            #    with the most dispersion in the set.
 | 
				
			||||||
 | 
					            major_viz: Viz = None
 | 
				
			||||||
            major_mx: float = 0
 | 
					            major_mx: float = 0
 | 
				
			||||||
            major_mn: float = float('inf')
 | 
					            major_mn: float = float('inf')
 | 
				
			||||||
            mx_up_rng: float = 0
 | 
					            mx_up_rng: float = 0
 | 
				
			||||||
            mn_down_rng: float = 0
 | 
					            mn_down_rng: float = 0
 | 
				
			||||||
            mx_disp: float = 0
 | 
					            mx_disp: float = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # collect certain flows have grapics objects **in seperate
 | 
				
			||||||
 | 
					            # plots/viewboxes** into groups and do a common calc to
 | 
				
			||||||
 | 
					            # determine auto-ranging input for `._set_yrange()`.
 | 
				
			||||||
 | 
					            # this is primarly used for our so called "log-linearized
 | 
				
			||||||
 | 
					            # multi-plot" overlay technique.
 | 
				
			||||||
            start_datums: dict[
 | 
					            start_datums: dict[
 | 
				
			||||||
                ViewBox,
 | 
					                ViewBox,
 | 
				
			||||||
                tuple[
 | 
					                tuple[
 | 
				
			||||||
| 
						 | 
					@ -1042,79 +971,141 @@ class ChartView(ViewBox):
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
            ] = {}
 | 
					            ] = {}
 | 
				
			||||||
            max_istart: float = 0
 | 
					            max_istart: float = 0
 | 
				
			||||||
            major_viz: Viz = None
 | 
					 | 
				
			||||||
            # major_in_view: np.ndarray = None
 | 
					            # major_in_view: np.ndarray = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for viz_name, out in mxmn_groups.items():
 | 
					            for name, viz in chart._vizs.items():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if not viz.render:
 | 
				
			||||||
 | 
					                    # print(f'skipping {flow.name}')
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # pass in no array which will read and render from the last
 | 
				
			||||||
 | 
					                # passed array (normally provided by the display loop.)
 | 
				
			||||||
 | 
					                in_view, i_read_range, _ = viz.update_graphics()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if not in_view:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                out = viz.maxmin(i_read_range=i_read_range)
 | 
				
			||||||
 | 
					                if out is None:
 | 
				
			||||||
 | 
					                    log.warning(f'No yrange provided for {name}!?')
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
                (
 | 
					                (
 | 
				
			||||||
                    ixrng,
 | 
					                    ixrng,
 | 
				
			||||||
                    read_slc,
 | 
					                    read_slc,
 | 
				
			||||||
                    (ymn, ymx),
 | 
					                    yrange
 | 
				
			||||||
                ) = out
 | 
					                ) = out
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # determine start datum in view
 | 
					                pi = viz.plot
 | 
				
			||||||
                viz = chart._vizs[viz_name]
 | 
					 | 
				
			||||||
                arr = viz.shm.array
 | 
					 | 
				
			||||||
                in_view = arr[read_slc]
 | 
					 | 
				
			||||||
                row_start = arr[read_slc.start - 1]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                max_istart = max(in_view[0]['index'], max_istart)
 | 
					                # handle multiple graphics-objs per viewbox cases
 | 
				
			||||||
 | 
					                mxmn = mxmns_by_common_pi.get(pi)
 | 
				
			||||||
 | 
					                if mxmn:
 | 
				
			||||||
 | 
					                    yrange = mxmns_by_common_pi[pi] = (
 | 
				
			||||||
 | 
					                        min(yrange[0], mxmn[0]),
 | 
				
			||||||
 | 
					                        max(yrange[1], mxmn[1]),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if viz.is_ohlc:
 | 
					 | 
				
			||||||
                    y_med = np.median(in_view['close'])
 | 
					 | 
				
			||||||
                    y_start = row_start['open']
 | 
					 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    y_med = np.median(in_view[viz.name])
 | 
					                    mxmns_by_common_pi[pi] = yrange
 | 
				
			||||||
                    y_start = row_start[viz.name]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # x_start = ixrng[0]
 | 
					                # handle overlay log-linearized group scaling cases
 | 
				
			||||||
                # print(
 | 
					                # TODO: a better predicate here, likely something
 | 
				
			||||||
                #     f'{viz.name} ->\n'
 | 
					                # to do with overlays and their settings..
 | 
				
			||||||
                #     f'(x_start: {x_start}, y_start: {y_start}\n'
 | 
					                if (
 | 
				
			||||||
                # )
 | 
					                    viz.is_ohlc
 | 
				
			||||||
                start_datums[viz.plot.vb] = (
 | 
					                ):
 | 
				
			||||||
                    viz,
 | 
					                    ymn, ymx = yrange
 | 
				
			||||||
                    y_start,
 | 
					                    # print(f'adding {viz.name} to overlay')
 | 
				
			||||||
                    ymn,
 | 
					                    # mxmn_groups[viz.name] = out
 | 
				
			||||||
                    ymx,
 | 
					                    # viz = chart._vizs[viz_name]
 | 
				
			||||||
                    y_med,
 | 
					
 | 
				
			||||||
                    read_slc,
 | 
					                    # determine start datum in view
 | 
				
			||||||
                    in_view,
 | 
					                    arr = viz.shm.array
 | 
				
			||||||
 | 
					                    in_view = arr[read_slc]
 | 
				
			||||||
 | 
					                    row_start = arr[read_slc.start - 1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    max_istart = max(in_view[0]['index'], max_istart)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if viz.is_ohlc:
 | 
				
			||||||
 | 
					                        y_med = np.median(in_view['close'])
 | 
				
			||||||
 | 
					                        y_start = row_start['open']
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        y_med = np.median(in_view[viz.name])
 | 
				
			||||||
 | 
					                        y_start = row_start[viz.name]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # x_start = ixrng[0]
 | 
				
			||||||
 | 
					                    # print(
 | 
				
			||||||
 | 
					                    #     f'{viz.name} ->\n'
 | 
				
			||||||
 | 
					                    #     f'(x_start: {x_start}, y_start: {y_start}\n'
 | 
				
			||||||
 | 
					                    # )
 | 
				
			||||||
 | 
					                    start_datums[viz.plot.vb] = (
 | 
				
			||||||
 | 
					                        viz,
 | 
				
			||||||
 | 
					                        y_start,
 | 
				
			||||||
 | 
					                        ymn,
 | 
				
			||||||
 | 
					                        ymx,
 | 
				
			||||||
 | 
					                        y_med,
 | 
				
			||||||
 | 
					                        read_slc,
 | 
				
			||||||
 | 
					                        in_view,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # find curve with max dispersion
 | 
				
			||||||
 | 
					                    disp = abs(ymx - ymn) / y_med
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # track the "major" curve as the curve with most
 | 
				
			||||||
 | 
					                    # dispersion.
 | 
				
			||||||
 | 
					                    if disp > mx_disp:
 | 
				
			||||||
 | 
					                        major_viz = viz
 | 
				
			||||||
 | 
					                        mx_disp = disp
 | 
				
			||||||
 | 
					                        major_mn = ymn
 | 
				
			||||||
 | 
					                        major_mx = ymx
 | 
				
			||||||
 | 
					                        # major_in_view = in_view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # compute directional (up/down) y-range % swing/dispersion
 | 
				
			||||||
 | 
					                    y_ref = y_med
 | 
				
			||||||
 | 
					                    up_rng = (ymx - y_ref) / y_ref
 | 
				
			||||||
 | 
					                    down_rng = (ymn - y_ref) / y_ref
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    mx_up_rng = max(mx_up_rng, up_rng)
 | 
				
			||||||
 | 
					                    mn_down_rng = min(mn_down_rng, down_rng)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # print(
 | 
				
			||||||
 | 
					                    #     f'{viz.name}@{chart_name} group mxmn calc\n'
 | 
				
			||||||
 | 
					                    #     '--------------------\n'
 | 
				
			||||||
 | 
					                    #     f'y_start: {y_start}\n'
 | 
				
			||||||
 | 
					                    #     f'ymn: {ymn}\n'
 | 
				
			||||||
 | 
					                    #     f'ymx: {ymx}\n'
 | 
				
			||||||
 | 
					                    #     f'mx_disp: {mx_disp}\n'
 | 
				
			||||||
 | 
					                    #     f'up %: {up_rng * 100}\n'
 | 
				
			||||||
 | 
					                    #     f'down %: {down_rng * 100}\n'
 | 
				
			||||||
 | 
					                    #     f'mx up %: {mx_up_rng * 100}\n'
 | 
				
			||||||
 | 
					                    #     f'mn down %: {mn_down_rng * 100}\n'
 | 
				
			||||||
 | 
					                    # )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # non-overlay group case
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    pi.vb._set_yrange(yrange=yrange)
 | 
				
			||||||
 | 
					                    profiler(
 | 
				
			||||||
 | 
					                        f'{viz.name}@{chart_name} `Viz.plot.vb._set_yrange()`'
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            profiler(f'<{chart_name}>.interact_graphics_cycle({name})')
 | 
				
			||||||
 | 
					            if not start_datums:
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # if no overlays, set lone chart's yrange and short circuit
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                len(start_datums) < 2
 | 
				
			||||||
 | 
					            ):
 | 
				
			||||||
 | 
					                # print(f'ONLY ranging major: {viz.name}')
 | 
				
			||||||
 | 
					                major_viz.plot.vb._set_yrange(
 | 
				
			||||||
 | 
					                    yrange=yrange,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # find curve with max dispersion
 | 
					            # conduct "log-linearized multi-plot" scalings for all groups
 | 
				
			||||||
                disp = abs(ymx - ymn) / y_med
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # track the "major" curve as the curve with most
 | 
					 | 
				
			||||||
                # dispersion.
 | 
					 | 
				
			||||||
                if disp > mx_disp:
 | 
					 | 
				
			||||||
                    major_viz = viz
 | 
					 | 
				
			||||||
                    mx_disp = disp
 | 
					 | 
				
			||||||
                    major_mn = ymn
 | 
					 | 
				
			||||||
                    major_mx = ymx
 | 
					 | 
				
			||||||
                    # major_in_view = in_view
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # compute directional (up/down) y-range % swing/dispersion
 | 
					 | 
				
			||||||
                y_ref = y_med
 | 
					 | 
				
			||||||
                up_rng = (ymx - y_ref) / y_ref
 | 
					 | 
				
			||||||
                down_rng = (ymn - y_ref) / y_ref
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                mx_up_rng = max(mx_up_rng, up_rng)
 | 
					 | 
				
			||||||
                mn_down_rng = min(mn_down_rng, down_rng)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # print(
 | 
					 | 
				
			||||||
                #     f'{viz.name}@{chart_name} group mxmn calc\n'
 | 
					 | 
				
			||||||
                #     '--------------------\n'
 | 
					 | 
				
			||||||
                #     f'y_start: {y_start}\n'
 | 
					 | 
				
			||||||
                #     f'ymn: {ymn}\n'
 | 
					 | 
				
			||||||
                #     f'ymx: {ymx}\n'
 | 
					 | 
				
			||||||
                #     f'mx_disp: {mx_disp}\n'
 | 
					 | 
				
			||||||
                #     f'up %: {up_rng * 100}\n'
 | 
					 | 
				
			||||||
                #     f'down %: {down_rng * 100}\n'
 | 
					 | 
				
			||||||
                #     f'mx up %: {mx_up_rng * 100}\n'
 | 
					 | 
				
			||||||
                #     f'mn down %: {mn_down_rng * 100}\n'
 | 
					 | 
				
			||||||
                # )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (
 | 
					            for (
 | 
				
			||||||
                view,
 | 
					                view,
 | 
				
			||||||
                (
 | 
					                (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue