Nano Hash - криптовалюты, майнинг, программирование

Многоуровневый холст в Java Swing

Я имитирую людей, идущих по графу связанных узлов. Чтобы отобразить его визуально, я использую объект холста. Я делаю это, сначала визуализируя граф с его узлами и связями между ними, после этого я начинаю рисовать человека, который представлен небольшим квадратом, движущимся по графу.

Моя проблема в том, что после того, как я рисую карту (или график), анимация человека удаляет метки, а иногда и линии графика. Я знаю, что это потому, что я визуализирую движение человека на том же холсте, который содержит карту (и из-за вызова метода clearRect()).

Как я могу избежать очистки графика? Сначала я посмотрел на JLayeredPane, но холсты перекрываются, и на самом верхнем холсте нет прозрачности (без прозрачности человека). Второй вариант, который я придумал, — это скопировать область перед рисованием человека, а затем восстановить область, когда человек движется, но я не уверен, как этого добиться, поэтому руководство приветствуется, поскольку я не использовал swing или awt, которые много, и я думаю, что это может быть общей проблемой.

Я прикрепил изображение, чтобы показать мою проблему и мой код рендеринга для каждого человека

public class Person extends Thread {

    public Person(String name, Spot location, World world, Graphics g) {
    this.name= name;
    this.location= location;
    this.world= world;
    this.g= g;
}

private void move() {
    Set<Link> links= world.getLinksFrom(location.getId());  
Link route= CollectionUtil.getRandomElement(links);  
Spot destination= route.getOriginX() == location.getX() &&   
    route.getOriginY() == location.getY() ?  
    route.getTheTarget(): route.getTheOrigin();  

try {
    double deltaX= (destination.getX() - location.getX()) / route.distance();
    double deltaY= (destination.getY() - location.getY()) / route.distance();
    double w2= (PERSON_WIDTH / 2);

    for(double i=location.getX(), j=location.getY(), d= route.distance(); 
    d > 5;
    i+=deltaX, j+= deltaY, 
    d=Point2D.distance(i, j, destination.getX(), destination.getY())) {
            g.clearRect((int)(i - w2 - deltaX), (int)(j - w2 - deltaY), 
        PERSON_WIDTH, PERSON_WIDTH);
        g.drawRect((int)(i-w2), (int)(j-w2), 
        PERSON_WIDTH-1, PERSON_WIDTH-1);
        Thread.sleep(50);
    }

    this.location= destination;

    // Stay ath the new location for a while
    Thread.sleep(new Random(System.currentTimeMillis()).nextInt(Person.MAX_SPOT_MILLIS));
    } catch(InterruptedException e) {
        throw new RuntimeException(e);
    }

@Override
public void run() {
    while(!isInterrupted()) {
        this.move();            
    }
}
}

Моя проблема с рендерингом


  • Чтобы быстрее получить помощь, опубликуйте SSCCE. 27.05.2013
  • Недостаточно информации для продолжения, но похоже, что вы сохраняете ссылку на графический объект, возможно, используя getGraphics. Это не очень хорошая идея. Графический контекст, возвращаемый getGraohics, может иметь значение null и изменяться между перерисовками. Вы также, похоже, обновляете пользовательский интерфейс вне потока Dispathing события. Это также плохая идея, поскольку вы фактически не контролируете, когда происходит перерисовка, и это может привести к артефактам рисования. Дополнительные сведения см. в разделе Пользовательское рисование. 27.05.2013
  • Вы правы во всем, что вы предположили (ссылка getGraphics, каждый человек является Thread), спасибо за ссылку, я посмотрю, смогу ли я настроить SSCCE, как указывает @AndreThompson. 27.05.2013
  • используйте JLayer/GlassPane для рисования поверх 27.05.2013
  • не блокируйте EDT ни с помощью Thread.sleep(int), ни для OpenGL/CL 27.05.2013
  • @mKorbel Что вы подразумеваете под EDT? Вы бы использовали для этого JInternalFrame? 27.05.2013
  • поток отправки событий, я бы не стал использовать JInternalFrame, нет причин создавать MDI, как (из) времен Atari 27.05.2013

Ответы:


1

Два наиболее распространенных решения:

  1. Рисуйте график каждый раз. Используйте двойную буферизацию, чтобы избежать мерцания. Если в вашем графе много узлов и у вас очень медленный компьютер, это должно работать хорошо.

  2. Превратите график в изображение. Прежде чем приступить к рисованию лиц, нарисуйте изображение один раз. Это очищает весь холст.

27.05.2013
  • Я не уверен, использует ли OP java.awt.Canvas или нет. Если они есть, то возникает вопрос, почему? Компоненты Swing по умолчанию имеют двойную буферизацию. Второй пункт — отличное предложение, несмотря на 27.05.2013

  • 2
    1. Если вы не создадите его самостоятельно, вам не следует поддерживать ссылку на контекст Graphics. Графический контекст может быть изменен между перерисовками, и перерисовки переопределяют все, что было нарисовано в нем между ними. Вы должны переопределить метод paintComponet пользовательского компонента (например, JPanel) и рисовать текущее состояние компонента при каждом вызове.
    2. Похоже, вы используете java.awt.Canvas, вам следует избегать AWT и вместо этого использовать Swing. Это более современная структура и по умолчанию используется двойная буферизация. Вам следует избегать смешивания компонентов AWT и Swing, поскольку они, как правило, не очень хорошо сочетаются друг с другом.
    3. Swing — это однопоточная среда, а это означает, что вы должны взаимодействовать с пользовательским интерфейсом только в контексте потока диспетчеризации событий? Дополнительные сведения см. в разделе Параллелизм в Swing.
    4. Есть 3 способа сделать анимацию Swing. Вы можете использовать Thread или javax.swing.Timer или одну из анимационных фреймворков.

    Использование потоков является более сложным и возлагает на вас ответственность за правильную повторную синхронизацию обновлений с клиентом. Это полезно для сложных анимационных заданий, где время между вычислением состояния в каждом цикле может варьироваться.

    javax.swing.Timer проще, так как его обратный вызов выполняется в контексте EDT через равные промежутки времени. Это отлично подходит для простых анимаций, где время, необходимое для обновления состояния, короткое и / или разумно постоянное, поэтому они не могут вызвать ошеломляющие кадры.

    27.05.2013

    3

    Если вы хотите попробовать два холста, думаю, у меня есть решение.

    Создайте свой первый холст, который будет рисовать ваш график, и создайте холст поверх него, который будет рисовать ваших людей.

    Вы можете установить чистый цвет фона верхнего холста, а затем также очистить холст тем же чистым цветом.

    Например:

    g.setBackground(new Color(0,0,0,0));
    g.clearRect(0, 0, width, height);
    

    очистит верхний холст, оставив полностью прозрачный цвет

    canvas.setBackground(new Color(0,0,0,0);
    

    сделает компонент холста прозрачным. Его нужно вызвать только один раз при создании холста.

    Теперь верхний холст будет полностью прозрачным после того, как вы очистите его с альфа-цветом 0, и только то, что вы рисуете на верхнем холсте, будет блокировать нижний холст.

    Надеюсь это поможет

    27.05.2013
  • Если OP использует компоненты AWT, это может вызвать артефакты рисования, поскольку, если я не ошибаюсь, компоненты AWT не поддерживают прозрачность, и менеджер перерисовки не будет знать, нужно ли обновлять компоненты под ним. Если OP выпускает компоненты Swing, то они должны устанавливать прозрачность компонента с помощью метода setOpaque, который сообщает диспетчеру перерисовки, что компоненты под ним также необходимо обновить. 27.05.2013
  • Думаю, то, о чем я думал, было более сложным ответом, чем я понял. Я думал о том, чтобы иметь 2 класса, расширяющих JPanels свинга и запускающих свои собственные таймеры. Таким образом, ими можно было бы управлять по отдельности с помощью настроек прозрачности. Я попробовал это, используя несколько панелей, созданных из отдельных потоков. Это сработало, как я и думал, хотя производительность слишком сильно упала. Несколько графических контекстов из разных потоков означают, что мне нужно много синхронизации, и это действительно слишком сложное решение более простой проблемы. 27.05.2013
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..