<commit_before>/*******************************************************************************
 * Copyright (c) 2015-2016 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.editor.util;

import static org.csstudio.display.builder.editor.Plugin.logger;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.RecursiveTask;
import java.util.logging.Level;

import org.csstudio.display.builder.model.ChildrenProperty;
import org.csstudio.display.builder.model.DisplayModel;
import org.csstudio.display.builder.model.Widget;
import org.csstudio.display.builder.model.properties.InsetsWidgetProperty;

import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;

/** Helpers for handling geometry
 *  @author <NAME>
 */
@SuppressWarnings("nls")
public class GeometryTools
{
    /** Get offset of widget inside the display.
     *
     *  <p>Widgets that are inside a container
     *  are positioned relative to the container.
     *
     *  @param widget Model widget, must already be in the model, i.e. have a parent
     *  @return {@link Point2D} Offset of the widget relative to the display model
     */
    public static Point2D getDisplayOffset(final Widget widget)
    {
        return getContainerOffset(widget.getParent().orElse(null));
    }

    /** Get offset of widgets inside a container from display root
     *
     *  <p>Widgets that are inside a container
     *  are positioned relative to the container.
     *
     *  @param container Container, i.e. GroupWidget, TabWidget or DisplayModel root
     *  @return {@link Point2D} Offset of the widgets inside that container
     */
    public static Point2D getContainerOffset(Widget container)
    {
        int dx = 0, dy = 0;

        while (container != null)
        {   // Ignore the x/y location of the display model,
            // it's used as origin coordinate for standalone window,
            // not as offset within a display
            if (! (container instanceof DisplayModel))
            {
                dx += container.propX().getValue();
                dy += container.propY().getValue();
            }
            final int[] insets = InsetsWidgetProperty.getInsets(container);
            if (insets != null)
            {
                dx += insets[0];
                dy += insets[1];
            }
            container = container.getParent().orElse(null);
        }

        return new Point2D(dx, dy);
    }

    /** Get bounds of widget, relative to container
     *  @param widget Model widget
     *  @return {@link Rectangle2D}
     */
    public static Rectangle2D getBounds(final Widget widget)
    {
        return new Rectangle2D(widget.propX().getValue(),
                               widget.propY().getValue(),
                               widget.propWidth().getValue(),
                               widget.propHeight().getValue());
    }

    /** Get bounds of widgets relative to container
     *  @param widgets Model widgets
     *  @return {@link Rectangle2D}
     */
    public static Rectangle2D getBounds(final Collection<Widget> widgets)
    {
        return widgets.stream()
                      .map(GeometryTools::getBounds)
                      .reduce(null, GeometryTools::join);
    }

    /** Get bounds of widget relative to display model
     *  @param widget Model widget
     *  @return {@link Rectangle2D}
     */
    public static Rectangle2D getDisplayBounds(final Widget widget)
    {
        final Point2D offset = getDisplayOffset(widget);
        try
        {
            return new Rectangle2D(offset.getX() + widget.propX().getValue(),
                                   offset.getY() + widget.propY().getValue(),
                                   widget.propWidth().getValue(),
                                   widget.propHeight().getValue());
        }
        catch (IllegalArgumentException ex)
        {
            logger.log(Level.WARNING, "Widget has invalid size " + widget, ex);
            return new Rectangle2D(offset.getX() + widget.propX().getValue(),
                                   offset.getY() + widget.propY().getValue(),
                                   1, 1);
        }
    }

    /** Get bounds of widgets relative to display model
     *  @param widgets Model widgets
     *  @return {@link Rectangle2D}
     */
    public static Rectangle2D getDisplayBounds(final Collection<Widget> widgets)
    {
        return widgets.stream()
                      .map(GeometryTools::getDisplayBounds)
                      .reduce(null, GeometryTools::join);
    }

    /** Compute bounding rectangle
     *  @param one One rect, may be <code>null</code>
     *  @param other Other rect, may be <code>null</code>
     *  @return Bounding rectangle of one and other.
     *          <code>null</code> if both inputs are <code>null</code>.
     */
    public static Rectangle2D join(final Rectangle2D one, final Rectangle2D other)
    {
        if (one == null)
            return other;
        if (other == null)
            return one;
        final double x = Math.min(one.getMinX(), other.getMinX());
        final double y = Math.min(one.getMinY(), other.getMinY());
        final double x2 = Math.max(one.getMaxX(), other.getMaxX());
        final double y2 = Math.max(one.getMaxY(), other.getMaxY());
        return new Rectangle2D(x, y, x2-x, y2-y);
    }

    /** Parallel search for widgets within a region */
    private static class WidgetSearch extends RecursiveTask<List<Widget>>
    {
        private static final long serialVersionUID = 1L;
        private static final int THRESHOLD = 50; // XXX Find suitable threshold
        private final List<Widget> widgets;
        private final Rectangle2D region;

        public WidgetSearch(final List<Widget> widgets, final Rectangle2D region)
        {
            this.widgets = widgets;
            this.region = region;
        }

        @Override
        protected List<Widget> compute()
        {
            if (widgets.size() > THRESHOLD)
            {   // Spawn sub-task for first half
                final int half = widgets.size() / 2;
                final WidgetSearch sub = new WidgetSearch(widgets.subList(0, half), region);
                sub.fork();
                // Search second half of list in this thread
                final List<Widget> found = searchWidgetList(widgets.subList(half, widgets.size()));
                // Return combined result
                found.addAll(sub.join());
                return found;
            }
            else // Search complete list in this thread
                return searchWidgetList(widgets);
        }

        private List<Widget> searchWidgetList(final List<Widget> widgets)
        {
            // System.out.println("Searching in " + Thread.currentThread().getName() + ": " + widgets);
            final List<Widget> found = new ArrayList<>();
            for (final Widget widget : widgets)
            {
                if (region.contains(getDisplayBounds(widget)))
                    found.add(widget);
                final ChildrenProperty children = ChildrenProperty.getChildren(widget);
                if (children != null)
                {
                    final WidgetSearch sub = new WidgetSearch(children.getValue(), region);
                    found.addAll(sub.compute());
                }
            }
            return found;
        }
    }

    /** Find widgets inside a region
     *  @param model Model to search
     *  @param region Region in which to locate widgets
     *  @return Widgets within the region
     */
    public static List<Widget> findWidgets(final DisplayModel model,
                                           final Rectangle2D region)
    {
        final WidgetSearch search = new WidgetSearch(model.getChildren(), region);
        return search.compute();
    }

    /** Move widgets to a new location<commit_msg>Children selected only if their container is not selected.<commit_after>/*******************************************************************************
 * Copyright (c) 2015-2016 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.editor.util;

import static org.csstudio.display.builder.editor.Plugin.logger;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.RecursiveTask;
import java.util.logging.Level;

import org.csstudio.display.builder.model.ChildrenProperty;
import org.csstudio.display.builder.model.DisplayModel;
import org.csstudio.display.builder.model.Widget;
import org.csstudio.display.builder.model.properties.InsetsWidgetProperty;

import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;

/** Helpers for handling geometry
 *  @author <NAME>
 */
@SuppressWarnings("nls")
public class GeometryTools
{
    /** Get offset of widget inside the display.
     *
     *  <p>Widgets that are inside a container
     *  are positioned relative to the container.
     *
     *  @param widget Model widget, must already be in the model, i.e. have a parent
     *  @return {@link Point2D} Offset of the widget relative to the display model
     */
    public static Point2D getDisplayOffset(final Widget widget)
    {
        return getContainerOffset(widget.getParent().orElse(null));
    }

    /** Get offset of widgets inside a container from display root
     *
     *  <p>Widgets that are inside a container
     *  are positioned relative to the container.
     *
     *  @param container Container, i.e. GroupWidget, TabWidget or DisplayModel root
     *  @return {@link Point2D} Offset of the widgets inside that container
     */
    public static Point2D getContainerOffset(Widget container)
    {
        int dx = 0, dy = 0;

        while (container != null)
        {   // Ignore the x/y location of the display model,
            // it's used as origin coordinate for standalone window,
            // not as offset within a display
            if (! (container instanceof DisplayModel))
            {
                dx += container.propX().getValue();
                dy += container.propY().getValue();
            }
            final int[] insets = InsetsWidgetProperty.getInsets(container);
            if (insets != null)
            {
                dx += insets[0];
                dy += insets[1];
            }
            container = container.getParent().orElse(null);
        }

        return new Point2D(dx, dy);
    }

    /** Get bounds of widget, relative to container
     *  @param widget Model widget
     *  @return {@link Rectangle2D}
     */
    public static Rectangle2D getBounds(final Widget widget)
    {
        return new Rectangle2D(widget.propX().getValue(),
                               widget.propY().getValue(),
                               widget.propWidth().getValue(),
                               widget.propHeight().getValue());
    }

    /** Get bounds of widgets relative to container
     *  @param widgets Model widgets
     *  @return {@link Rectangle2D}
     */
    public static Rectangle2D getBounds(final Collection<Widget> widgets)
    {
        return widgets.stream()
                      .map(GeometryTools::getBounds)
                      .reduce(null, GeometryTools::join);
    }

    /** Get bounds of widget relative to display model
     *  @param widget Model widget
     *  @return {@link Rectangle2D}
     */
    public static Rectangle2D getDisplayBounds(final Widget widget)
    {
        final Point2D offset = getDisplayOffset(widget);
        try
        {
            return new Rectangle2D(offset.getX() + widget.propX().getValue(),
                                   offset.getY() + widget.propY().getValue(),
                                   widget.propWidth().getValue(),
                                   widget.propHeight().getValue());
        }
        catch (IllegalArgumentException ex)
        {
            logger.log(Level.WARNING, "Widget has invalid size " + widget, ex);
            return new Rectangle2D(offset.getX() + widget.propX().getValue(),
                                   offset.getY() + widget.propY().getValue(),
                                   1, 1);
        }
    }

    /** Get bounds of widgets relative to display model
     *  @param widgets Model widgets
     *  @return {@link Rectangle2D}
     */
    public static Rectangle2D getDisplayBounds(final Collection<Widget> widgets)
    {
        return widgets.stream()
                      .map(GeometryTools::getDisplayBounds)
                      .reduce(null, GeometryTools::join);
    }

    /** Compute bounding rectangle
     *  @param one One rect, may be <code>null</code>
     *  @param other Other rect, may be <code>null</code>
     *  @return Bounding rectangle of one and other.
     *          <code>null</code> if both inputs are <code>null</code>.
     */
    public static Rectangle2D join(final Rectangle2D one, final Rectangle2D other)
    {
        if (one == null)
            return other;
        if (other == null)
            return one;
        final double x = Math.min(one.getMinX(), other.getMinX());
        final double y = Math.min(one.getMinY(), other.getMinY());
        final double x2 = Math.max(one.getMaxX(), other.getMaxX());
        final double y2 = Math.max(one.getMaxY(), other.getMaxY());
        return new Rectangle2D(x, y, x2-x, y2-y);
    }

    /** Parallel search for widgets within a region */
    private static class WidgetSearch extends RecursiveTask<List<Widget>>
    {
        private static final long serialVersionUID = 1L;
        private static final int THRESHOLD = 50; // XXX Find suitable threshold
        private final List<Widget> widgets;
        private final Rectangle2D region;

        public WidgetSearch(final List<Widget> widgets, final Rectangle2D region)
        {
            this.widgets = widgets;
            this.region = region;
        }

        @Override
        protected List<Widget> compute()
        {
            if (widgets.size() > THRESHOLD)
            {   // Spawn sub-task for first half
                final int half = widgets.size() / 2;
                final WidgetSearch sub = new WidgetSearch(widgets.subList(0, half), region);
                sub.fork();
                // Search second half of list in this thread
                final List<Widget> found = searchWidgetList(widgets.subList(half, widgets.size()));
                // Return combined result
                found.addAll(sub.join());
                return found;
            }
            else // Search complete list in this thread
                return searchWidgetList(widgets);
        }

        private List<Widget> searchWidgetList(final List<Widget> widgets)
        {
            // System.out.println("Searching in " + Thread.currentThread().getName() + ": " + widgets);
            final List<Widget> found = new ArrayList<>();
            for (final Widget widget : widgets)
            {
                if (region.contains(getDisplayBounds(widget))) {
                    found.add(widget);
                } else {
                    final ChildrenProperty children = ChildrenProperty.getChildren(widget);
                    if (children != null)
                    {
                        final WidgetSearch sub = new WidgetSearch(children.getValue(), region);
                        found.addAll(sub.compute());
                    }
                }
            }
            return found;
        }
    }

    /** Find widgets inside a region
     *  @param model Model to search
     *  @param region Region in which to locate widgets
     *  @return Widgets within the region
     */
    public static List<Widget> findWidgets(final DisplayModel model,
                                           final Rectangle2D region)
    {
        final WidgetSearch search = new WidgetSearch(model.getChildren(), region);
        return search.compute();
    }

    /** Move widgets to a new location
{'fraction_non_alphanumeric': 0.11278330735100132, 'fraction_numerical': 0.006355678138865572, 'mean_word_length': 2.015548725366118, 'pattern_counts': {'":': 2, '<': 51, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1379184', 'n_tokens_mistral': 4318, 'n_tokens_neox': 4169, 'n_words': 1244}