<commit_before># ===============================================================================
# Copyright 2013 <NAME>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================

# ============= enthought library imports =======================
from chaco.abstract_overlay import AbstractOverlay
from chaco.array_data_source import ArrayDataSource
from chaco.label import Label
from chaco.plot_label import PlotLabel
from enable.enable_traits import LineStyle
from kiva import FILL
from kiva.trait_defs.kiva_font_trait import KivaFont
from numpy import linspace, pi
from six.moves import zip
from traits.api import Float, Str, Instance
from uncertainties import std_dev, nominal_value

from pychron.core.helpers.formatting import floatfmt, calc_percent_error, format_percent_error
from pychron.core.stats import validate_mswd
from pychron.graph.error_ellipse_overlay import ErrorEllipseOverlay
from pychron.graph.error_envelope_overlay import ErrorEnvelopeOverlay
from pychron.graph.ml_label import tokenize
from pychron.pipeline.plot.overlays.isochron_inset import InverseIsochronPointsInset, InverseIsochronLineInset
from pychron.pipeline.plot.plotter.arar_figure import BaseArArFigure
from pychron.processing.analyses.analysis_group import StepHeatAnalysisGroup
from pychron.pychron_constants import PLUSMINUS, SIGMA, MSEM, SEM, SE, MSE


class MLTextLabel(Label):
    def draw(self, gc):
        """ Draws the label.

        This method assumes the graphics context has been translated to the
        correct position such that the origin is at the lower left-hand corner
        of this text label's box.
        """
        # Make sure `max_width` is respected
        self._fit_text_to_max_width(gc)

        # For this version we're not supporting rotated text.
        self._calc_line_positions(gc)

        with gc:
            bb_width, bb_height = self.get_bounding_box(gc)

            # Rotate label about center of bounding box
            width, height = self._bounding_box
            gc.translate_ctm(bb_width / 2.0, bb_height / 2.0)
            gc.rotate_ctm(pi / 180.0 * self.rotate_angle)
            gc.translate_ctm(-width / 2.0, -height / 2.0)

            # Draw border and fill background
            if self.bgcolor != "transparent":
                gc.set_fill_color(self.bgcolor_)
                gc.draw_rect((0, 0, width, height), FILL)
            if self.border_visible and self.border_width > 0:
                gc.set_stroke_color(self.border_color_)
                gc.set_line_width(self.border_width)
                border_offset = (self.border_width - 1) / 2.0
                gc.rect(border_offset, border_offset,
                        width - 2 * border_offset, height - 2 * border_offset)
                gc.stroke_path()

            gc.set_fill_color(self.color_)
            gc.set_stroke_color(self.color_)
            gc.set_font(self.font)
            if self.font.size <= 8.0:
                gc.set_antialias(0)
            else:
                gc.set_antialias(1)

            lines = self.text.split("\n")
            if self.border_visible:
                gc.translate_ctm(self.border_width, self.border_width)

            # width, height = self.get_width_height(gc)
            for i, line in enumerate(lines):
                if line == "":
                    continue
                x_offset = round(self._line_xpos[i])
                y_offset = round(self._line_ypos[i])
                with gc:
                    gc.translate_ctm(x_offset, y_offset)
                    self._draw_line(gc, line)

    def _draw_line(self, gc, txt):
        def gen():
            offset = 0
            for ti in tokenize(txt):
                if ti == 'sup':
                    offset = 1
                elif ti == 'sub':
                    offset = -1
                elif ti in ('/sup', '/sub'):
                    offset = 0
                else:
                    yield offset, ti

        ofont = self.font
        sfont = self.font.copy()
        sfont.size = int(sfont.size * 0.80)
        suph = int(ofont.size * 0.4)
        subh = -int(ofont.size * 0.3)

        x = 0
        for offset, text in gen():
            with gc:
                if offset == 1:
                    gc.translate_ctm(0, suph)
                    gc.set_font(sfont)
                elif offset == -1:
                    gc.set_font(sfont)
                    gc.translate_ctm(0, subh)
                else:
                    gc.set_font(ofont)

                w, h, _, _ = gc.get_full_text_extent(text)
                gc.set_text_position(x, 0)
                gc.show_text(text)
                x += w


class OffsetPlotLabel(PlotLabel):
    offset = None
    _label = Instance(MLTextLabel, args=())

    def overlay(self, component, gc, view_bounds=None, mode="normal"):
        with gc:
            if self.offset:
                gc.translate_ctm(*self.offset)
            super(OffsetPlotLabel, self).overlay(component, gc, view_bounds, mode)


class AtmInterceptOverlay(AbstractOverlay):
    line_width = Float(1.5)
    font = KivaFont("modern 10")
    line_style = LineStyle('dash')
    label = Str
    value = Float

    def overlay(self, component, gc, view_bounds=None, mode="normal"):
        x, y = component.map_screen((0, self.value))
        xo = component.x
        if x < xo:
            x = xo + 5

        with gc:
            txt = self.label
            gc.set_font(self.font)
            w, h = gc.get_full_text_extent(txt)[:2]

            gc.clip_to_rect(component.x - w - 5, component.y, component.width, component.height)

            gc.set_line_width(self.line_width)
            gc.set_line_dash(self.line_style_)
            gc.move_to(xo, y)
            gc.line_to(x, y)
            gc.draw_path()

            gc.set_text_position(xo - w - 2, y)
            gc.show_text(txt)


class Isochron(BaseArArFigure):
    pass


class InverseIsochron(Isochron):
    _plot_label = None
    xpad = None
    _analysis_group_klass = StepHeatAnalysisGroup

    def post_make(self):
        g = self.graph
        for i, p in enumerate(g.plots):
            l, h = self.ymis[i], self.ymas[i]
            g.set_y_limits(max(0, l), h, pad='0.1', pad_style='upper', plotid=i)

        g.set_x_limits(0, self.xma, pad='0.1')
        self._fix_log_axes()

    def plot(self, plots, legend=None):
        """
            plot data on plots
        """
        graph = self.graph

        if self.options.omit_non_plateau:
            self.analysis_group.do_omit_non_plateau()

        for pid, (plotobj, po) in enumerate(zip(graph.plots, plots)):
            getattr(self, '_plot_{}'.format(po.plot_name))(po, plotobj, pid)

    # ===============================================================================
    # plotters
    # ===============================================================================
    def _plot_aux(self, title, vk, po, pid):
        ys, es = self._get_aux_plot_data(vk, po.scalar)
        self._add_aux_plot(ys, title, vk, pid)

    def _add_plot(self, xs, ys, es, plotid, value_scale='linear'):
        pass

    def _plot_inverse_isochron(self, po, plot, pid):
        opt = self.options
        self.analysis_group.isochron_age_error_kind = opt.error_calc_method
        self.analysis_group.isochron_method = opt.regressor_kind
        _, _, reg = self.analysis_group.get_isochron_data(exclude_non_plateau=opt.exclude_non_plateau)<commit_msg>only pad isochron xmax<commit_after># ===============================================================================
# Copyright 2013 <NAME>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================

# ============= enthought library imports =======================
from chaco.abstract_overlay import AbstractOverlay
from chaco.array_data_source import ArrayDataSource
from chaco.label import Label
from chaco.plot_label import PlotLabel
from enable.enable_traits import LineStyle
from kiva import FILL
from kiva.trait_defs.kiva_font_trait import KivaFont
from numpy import linspace, pi
from six.moves import zip
from traits.api import Float, Str, Instance
from uncertainties import std_dev, nominal_value

from pychron.core.helpers.formatting import floatfmt, calc_percent_error, format_percent_error
from pychron.core.stats import validate_mswd
from pychron.graph.error_ellipse_overlay import ErrorEllipseOverlay
from pychron.graph.error_envelope_overlay import ErrorEnvelopeOverlay
from pychron.graph.ml_label import tokenize
from pychron.pipeline.plot.overlays.isochron_inset import InverseIsochronPointsInset, InverseIsochronLineInset
from pychron.pipeline.plot.plotter.arar_figure import BaseArArFigure
from pychron.processing.analyses.analysis_group import StepHeatAnalysisGroup
from pychron.pychron_constants import PLUSMINUS, SIGMA, MSEM, SEM, SE, MSE


class MLTextLabel(Label):
    def draw(self, gc):
        """ Draws the label.

        This method assumes the graphics context has been translated to the
        correct position such that the origin is at the lower left-hand corner
        of this text label's box.
        """
        # Make sure `max_width` is respected
        self._fit_text_to_max_width(gc)

        # For this version we're not supporting rotated text.
        self._calc_line_positions(gc)

        with gc:
            bb_width, bb_height = self.get_bounding_box(gc)

            # Rotate label about center of bounding box
            width, height = self._bounding_box
            gc.translate_ctm(bb_width / 2.0, bb_height / 2.0)
            gc.rotate_ctm(pi / 180.0 * self.rotate_angle)
            gc.translate_ctm(-width / 2.0, -height / 2.0)

            # Draw border and fill background
            if self.bgcolor != "transparent":
                gc.set_fill_color(self.bgcolor_)
                gc.draw_rect((0, 0, width, height), FILL)
            if self.border_visible and self.border_width > 0:
                gc.set_stroke_color(self.border_color_)
                gc.set_line_width(self.border_width)
                border_offset = (self.border_width - 1) / 2.0
                gc.rect(border_offset, border_offset,
                        width - 2 * border_offset, height - 2 * border_offset)
                gc.stroke_path()

            gc.set_fill_color(self.color_)
            gc.set_stroke_color(self.color_)
            gc.set_font(self.font)
            if self.font.size <= 8.0:
                gc.set_antialias(0)
            else:
                gc.set_antialias(1)

            lines = self.text.split("\n")
            if self.border_visible:
                gc.translate_ctm(self.border_width, self.border_width)

            # width, height = self.get_width_height(gc)
            for i, line in enumerate(lines):
                if line == "":
                    continue
                x_offset = round(self._line_xpos[i])
                y_offset = round(self._line_ypos[i])
                with gc:
                    gc.translate_ctm(x_offset, y_offset)
                    self._draw_line(gc, line)

    def _draw_line(self, gc, txt):
        def gen():
            offset = 0
            for ti in tokenize(txt):
                if ti == 'sup':
                    offset = 1
                elif ti == 'sub':
                    offset = -1
                elif ti in ('/sup', '/sub'):
                    offset = 0
                else:
                    yield offset, ti

        ofont = self.font
        sfont = self.font.copy()
        sfont.size = int(sfont.size * 0.80)
        suph = int(ofont.size * 0.4)
        subh = -int(ofont.size * 0.3)

        x = 0
        for offset, text in gen():
            with gc:
                if offset == 1:
                    gc.translate_ctm(0, suph)
                    gc.set_font(sfont)
                elif offset == -1:
                    gc.set_font(sfont)
                    gc.translate_ctm(0, subh)
                else:
                    gc.set_font(ofont)

                w, h, _, _ = gc.get_full_text_extent(text)
                gc.set_text_position(x, 0)
                gc.show_text(text)
                x += w


class OffsetPlotLabel(PlotLabel):
    offset = None
    _label = Instance(MLTextLabel, args=())

    def overlay(self, component, gc, view_bounds=None, mode="normal"):
        with gc:
            if self.offset:
                gc.translate_ctm(*self.offset)
            super(OffsetPlotLabel, self).overlay(component, gc, view_bounds, mode)


class AtmInterceptOverlay(AbstractOverlay):
    line_width = Float(1.5)
    font = KivaFont("modern 10")
    line_style = LineStyle('dash')
    label = Str
    value = Float

    def overlay(self, component, gc, view_bounds=None, mode="normal"):
        x, y = component.map_screen((0, self.value))
        xo = component.x
        if x < xo:
            x = xo + 5

        with gc:
            txt = self.label
            gc.set_font(self.font)
            w, h = gc.get_full_text_extent(txt)[:2]

            gc.clip_to_rect(component.x - w - 5, component.y, component.width, component.height)

            gc.set_line_width(self.line_width)
            gc.set_line_dash(self.line_style_)
            gc.move_to(xo, y)
            gc.line_to(x, y)
            gc.draw_path()

            gc.set_text_position(xo - w - 2, y)
            gc.show_text(txt)


class Isochron(BaseArArFigure):
    pass


class InverseIsochron(Isochron):
    _plot_label = None
    xpad = None
    _analysis_group_klass = StepHeatAnalysisGroup

    def post_make(self):
        g = self.graph
        for i, p in enumerate(g.plots):
            l, h = self.ymis[i], self.ymas[i]
            g.set_y_limits(max(0, l), h, pad='0.1', pad_style='upper', plotid=i)

        g.set_x_limits(0, self.xma*1.1)
        self._fix_log_axes()

    def plot(self, plots, legend=None):
        """
            plot data on plots
        """
        graph = self.graph

        if self.options.omit_non_plateau:
            self.analysis_group.do_omit_non_plateau()

        for pid, (plotobj, po) in enumerate(zip(graph.plots, plots)):
            getattr(self, '_plot_{}'.format(po.plot_name))(po, plotobj, pid)

    # ===============================================================================
    # plotters
    # ===============================================================================
    def _plot_aux(self, title, vk, po, pid):
        ys, es = self._get_aux_plot_data(vk, po.scalar)
        self._add_aux_plot(ys, title, vk, pid)

    def _add_plot(self, xs, ys, es, plotid, value_scale='linear'):
        pass

    def _plot_inverse_isochron(self, po, plot, pid):
        opt = self.options
        self.analysis_group.isochron_age_error_kind = opt.error_calc_method
        self.analysis_group.isochron_method = opt.regressor_kind
        _, _, reg = self.analysis_group.get_isochron_data(exclude_non_plateau=opt.exclude_non_plateau)
{'fraction_non_alphanumeric': 0.13093156843156842, 'fraction_numerical': 0.007992007992007992, 'mean_word_length': 2.2298850574712645, 'pattern_counts': {'":': 4, '<': 9, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3300854', 'n_tokens_mistral': 5051, 'n_tokens_neox': 4667, 'n_words': 1151}