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

Заставьте 2 противоречивых метода работать в drawRect

Я пишу элементы построения приложения, состоящие из CGPoints. У меня есть 2 кнопки: makeRectangle и makeTriangle. Для этапа построения/рисования я использую три метода для прямоугольника и три метода для треугольника внутри drawRect.

Я застрял с моим кодом в drawRect. В if-else-statement каждый метод меняет схему построения/чертежа на предыдущий элемент при каждом нажатии кнопки.

Если я уже построил прямоугольник, а затем нажимаю кнопку makeTriangle, я получаю новый треугольник, но мой прямоугольник превращается в треугольник с одной несоединенной точкой.

Есть ли обходной путь или мне не следует использовать метод drawRect?

Вот сообщение SO по теме drawRect: graphics-vs-su">РисоватьRect или не рисоватьRect

Анимация неправильно нарисованной трапеции и треугольника

Изображение правильно нарисованной трапеции и треугольника

Объявление элемента:

enum Element {
    case point1(point: CGPoint)
    case point2(point: CGPoint)
    case point3(point: CGPoint)
    case point4(point: CGPoint)

    func coord() -> [CGPoint] {    
        switch self {  
        case .point1(let point): return [point]
        case .point2(let point): return [point]
        case .point3(let point): return [point]
        case .point4(let point): return [point]
        }
    }
    func buildQuadPath(path: CGMutablePath) {
        switch self {
        case .point1(let point): CGPathMoveToPoint(path, nil, point.x, point.y)
        case .point2(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
        case .point3(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
        case .point4(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
        CGPathCloseSubpath(path)
        }
    }
    func buildTriPath(path: CGMutablePath) {
        switch self {
        case .point1(let point): CGPathMoveToPoint(path, nil, point.x, point.y)
        case .point2(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
        case .point3(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
        default:
        CGPathCloseSubpath(path)
        }
    }
}

Способы построения и рисования треугольника и прямоугольника:

func buildTriPath() -> CGMutablePath {
    let path = CGPathCreateMutable()
    _ = array.map { $0.buildTriPath(path) }
    return path
}
func drawTriPath() {
    let path = buildTriPath()
    GraphicsState {
        CGContextAddPath(self.currentContext, path)
        CGContextStrokePath(self.currentContext)
    }
}
func drawTriFill() {
    let fill = buildTriPath()
    GraphicsState {
        CGContextAddPath(self.currentContext, fill)
        CGContextFillPath(self.currentContext)
    }
}

///////////////////////////////////////////////////////

func buildQuadPath() -> CGMutablePath {
    let path = CGPathCreateMutable()
    _ = array.map { $0.buildQuadPath(path) }
    return path
}
func drawQuadPath() {
    let path = buildQuadPath()
    GraphicsState {
        CGContextAddPath(self.currentContext, path)
        CGContextStrokePath(self.currentContext)
    }
}
func drawQuadFill() {
    let fill = buildQuadPath()
    GraphicsState {
        CGContextAddPath(self.currentContext, fill)
        CGContextFillPath(self.currentContext)
    }
}

Две переменные помогают определить, нажата ли кнопка:

var squareB: Int = 0
var triangleB: Int = 0

@IBAction func makeTriangle(sender: AnyObject?) {
    ....................
    ....................
    triangleB += 1
    squareB = 0
}
@IBAction func makeRectangle(sender: AnyObject?) {
    ....................
    ....................
    triangleB = 0
    squareB += 1
}

drawRect метод:

override func drawRect(dirtyRect: NSRect) {
    super.drawRect(dirtyRect)

    drawBG()
    GraphicsState { self.drawMyPoints() }

    if squareB >= 1 && triangleB == 0 {
        buildQuadPath()
        drawQuadPath()
        drawQuadFill()
        needsDisplay = true
    }
    else if triangleB >= 1 && squareB == 0 {
        buildTriPath()
        drawTriPath()
        drawTriFill()
        needsDisplay = true
    }
    drawBorder()
}

...и, наконец, файл Context.swift:

import Cocoa
import CoreGraphics

extension NSView {

    var currentContext : CGContext? {

        get {

            let unsafeContextPointer = NSGraphicsContext.currentContext()?.graphicsPort

            if let contextPointer = unsafeContextPointer {
            let opaquePointer = COpaquePointer(contextPointer)
            let context: CGContextRef = Unmanaged.fromOpaque(opaquePointer).takeUnretainedValue()
            return context }
            else { return nil }
        }
    }

    func GraphicsState(drawStuff: () -> Void) {
        CGContextSaveGState(currentContext)
        drawStuff()
        CGContextRestoreGState(currentContext)
    }
}

//the end of code

  • Ваше утверждение в сочетании с предоставленным кодом не имеет смысла. Если я вас правильно понимаю, ваше состояние по умолчанию равно 0. Если вы нажмете кнопку (думаю, у вас включен какой-то прослушиватель кликов), состояние изменится на 1. Таким образом, должен быть нарисован только один из двух вариантов. Проверьте свой код, если ваше состояние по умолчанию правильно инициализировано до 0 и изменено обратно на 0 после (или до нового) рисования. 04.10.2016
  • Вы должны поддержать нас дополнительным кодом. В том виде, в каком код выглядит сейчас, следует рисовать только тот, у которого есть допустимое состояние, и порядок методов не имеет значения, поэтому они являются блоками условий. 05.10.2016
  • Можете ли вы показать мне код в GitHub. Реально не понимаю buildQuadPath() drawQuadPath() drawQuadFill() 08.10.2016
  • @季亨达 Я вставил раздел buildQuadPath в свой пост. 08.10.2016
  • Пожалуйста, просто разместите код на github. Также четко укажите точно желаемое поведение (я до сих пор не понимаю, чего вы хотите). 09.10.2016
  • @matt github.com/AndyFedoroff/rectangles-triangles 10.10.2016
  • @AndyFedoroff: вы можете добавить компилируемый проект в github? Было бы намного легче копать. 12.10.2016
  • @AndyFedoroff: я воссоздал проект для повторной компиляции. пожалуйста, смотрите запрос на вытягивание. Глядя на код, я удивляюсь, почему вы работаете с точками, а не со структурой данных, представляющей форму, состоящую из точек. 12.10.2016
  • @vikingosegundo Спасибо за компиляцию проекта. Мне нужно работать с точками (enum) по определенным причинам. 12.10.2016
  • Это не объясняет, почему вы не должны группировать их в структуру фигуры. 12.10.2016
  • Я думаю, что этот вопрос страдает от XY-Problem. 12.10.2016
  • Я использую перечисления (а не структуры) из-за их способности кодировать иерархию. 12.10.2016
  • Вы можете использовать перечисления, структуры, классы, словари или что-то еще, что вы придумали для представления фигур. Но вы определенно должны сгруппировать их, чтобы легко получить точки, которые связаны друг с другом, и сохранить стопку фигур. Классы, структуры, перечисления также могут уметь рисовать себя. или как удалить себя. 12.10.2016
  • @AndyFedoroff Я подозреваю, что переменные перечисления являются статическими, что означает, что они существуют в программе только один раз. Если вы вызываете метод drawRect() через метод makeTriangle(), вы просто перерисовываете путь и удаляете 4-ю точку... вот почему прямоугольник превращается в треугольник. Позже я получу более подробный ответ, который я уже начал, и который рекомендует более структурированный дизайн. 21.10.2016
  • @ Мэтт, я буду ждать этого. 21.10.2016
  • @vikingosegundo в основном указал на общую проблему, а также объяснил, почему этот вопрос не очень хорошо задают. Следует пересмотреть весь подход. Фигуры должны иметь собственное представление и рисоваться на их основе. Сохранение всех точек в неразделенной структуре данных здесь кажется неосуществимым. Кроме того, предоставление примера кода, который даже не запускается и не иллюстрирует ни концепцию, ни проблему, является... бессмысленным. 21.10.2016
  • @Gero Я тоже пробовал другой подход. Это не работает для меня. Какое твое решение? 21.10.2016
  • @Gero Downvoting в этом случае не является конструктивным. 21.10.2016
  • @AndyFedoroff Извините, я действительно слегка понижаю голос, но я думаю, что людей привлекает ваш вопрос из-за награды, но тогда нелегко понять, чего вы хотите. Я немного подумал об этом и до сих пор не уверен, особенно учитывая все комментарии, которые уже пытались решить эту проблему. Насколько я понимаю, подобные вопросы должны быть отклонены. Что касается вашей проблемы, я думаю, вам следует предоставить лучший пример проекта (который работает, возможно, есть несколько комментариев, объясняющих ваш подход). Лично я бы выбрал подход vikingosegundo (используйте drawRect, формы как структуры данных,...). 21.10.2016
  • @Gero Вы имеете в виду использование структуры вместо перечисления? Это не работает. 21.10.2016
  • @AndyFedoroff, то, что у тебя не получилось, не значит, что оно вообще не работает. 21.10.2016
  • @vikingosegundo Я согласен. Я не могу заставить его работать, поэтому я разместил вопрос здесь. 21.10.2016

Ответы:


1

Хорошо, поскольку я мог использовать эту практику, я создал пример проекта, чтобы показать, что я имею в виду под vikingosegundo.

Вот суть:
Для этого примера я сохранил весь соответствующий код, кроме добавления и удаления фигур, в файле GHShapeDemoView. Я использовал структуры для определения фигур, но рассматривал их как одну «единицу» данных, которая обрабатывается во время рисования, добавления в представление и т. д. Все фигуры хранятся в массиве, а во время рисования повторяются, и все найденные фигуры рисуются. используя простой NSBezierPath.

Для простоты я просто выбрал несколько случайных фиксированных точек для каждой формы в реальном проекте, которые, очевидно, определялись бы другим способом (мне было лень добавлять поля ввода...).

Даже здесь есть куча способов рефакторинга (наверное). Например, можно даже сделать каждую фигуру отдельным классом (или использовать один класс для фигур вообще). Возможно, даже подкласс NSView, который затем приведет к тому, что сама «область рисования» будет не настраиваемым видом, а обычным, и при нажатии кнопки соответствующие виды формы будут добавлены в качестве подвидов. Это, вероятно, также избавит вас от всех этих вещей, связанных с подсчетом очков (в основном). В реальном проекте я бы, вероятно, выбрал формы в качестве подклассов слоев, которые затем добавляю в подпредставление. Я не эксперт, но я думаю, что это может иметь преимущества в производительности в зависимости от того, сколько фигур есть и буду ли я их анимировать. (Очевидно, что наибольшая производительность, вероятно, будет достигнута при использовании OpenGL ES или чего-то еще, но я понятия не имею об этом, и это выходит далеко за рамки этого вопроса).

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

21.10.2016
  • Спасибо. Я взгляну. 21.10.2016
  • Я назначу награду через 10 часов. 21.10.2016
  • Рад, что смог помочь. Я понимаю, что это означает довольно много рефакторинга, чтобы попасть в ваш проект, но я считаю, что в конечном итоге это поможет вам также в поддержке вашего кода. :) Заботиться! 21.10.2016
  • Нужен массовый рефакторинг)) Но он необходим. 21.10.2016
  • @Gero, молодец. Однако я бы дал форме массив точек, чтобы разрешить любую форму. 21.10.2016
  • @vikingosegundo Да, это была бы отличная идея, я даже не думал об этом. Кроме того, вы также можете установить идентификатор или метку, чтобы отслеживать их. Тогда переменная коллекции также может быть другой коллекцией, в зависимости от того, что вам нужно. Есть масса вещей, которые вы могли бы сделать, я думаю. Методы создания также довольно грубы. :) 21.10.2016

  • 2

    if (makeTriangle != nil) { и if (makeRectangle != nil) { не имеет особого смысла. согласно вашему комментарию, makerRectangle и makeTriangle - это кнопки. Своими утверждениями вы проверяете их существование — и мы можем предположить, что они всегда существуют — всегда будет выполняться первое предложение if.

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

    05.10.2016
  • Должен ли я использовать тег или он здесь бесполезен? 05.10.2016
  • теги почти всегда бесполезны. 05.10.2016

  • 3

    Вот код, написанный Gero:

    Этот код хорошо работает со Swift 2.2.

    //
    //  GHShapeDemoView.swift
    //  GHShapeDrawingExample
    //
    //  Created by Gero Herkenrath on 21/10/2016.
    //  Copyright © 2016 Gero Herkenrath. All rights reserved.
    //
    
    import Cocoa
    
    class GHShapeDemoView: NSView {
    
        struct Shape {
    
            var p1:CGPoint = NSMakePoint(0.0, 0.0)
            var p2:CGPoint = NSMakePoint(0.0, 0.0)
            var p3:CGPoint = NSMakePoint(0.0, 0.0)
            var p4:CGPoint?
        }
    
        var shapes:[Shape] = []
    
        override internal var flipped: Bool {
            return true
        }
    
        override func drawRect(dirtyRect: NSRect) {
            super.drawRect(dirtyRect)
    
            NSColor.whiteColor().setFill()
            let updatedRect = NSBezierPath.init(rect: dirtyRect)
            updatedRect.fill()
    
            for shape in shapes {
                drawShape(shape)
            }
        }
    
        func drawShape(shape:Shape) {
            let shapePath = NSBezierPath()
            shapePath.moveToPoint(shape.p1)
            shapePath.lineToPoint(shape.p2)
            shapePath.lineToPoint(shape.p3)
    
            if let lastPoint = shape.p4 {
                shapePath.lineToPoint(lastPoint)
            }
    
            shapePath.closePath()
            NSColor.blackColor().setStroke()
            shapePath.stroke()
        }
    
        func addTrapezoid(p1:NSPoint, p2:NSPoint, p3:NSPoint, p4:NSPoint) {
            var shape = Shape()
            shape.p1 = p1
            shape.p2 = p2
            shape.p3 = p3
            shape.p4 = p4
            shapes.append(shape)
        }
    
        func addTriangle(p1:NSPoint, p2:NSPoint, p3:NSPoint) {
            var shape = Shape()
            shape.p1 = p1
            shape.p2 = p2
            shape.p3 = p3
            shapes.append(shape)
        }
    
        func removeShapeAt(index:Int) {
            if index < shapes.count {
                shapes.removeAtIndex(index)
            }
        }
    }
    

    /////////////////////////////////////////////////

    //
    //  ViewController.swift
    //  GHShapeDrawingExample
    //
    //  Created by Gero Herkenrath on 21/10/2016.
    //  Copyright © 2016 Gero Herkenrath. All rights reserved.
    //
    
    import Cocoa
    
    class ViewController: NSViewController {
    
        override func viewDidLoad() {
    
            if #available(OSX 10.10, *) {
                super.viewDidLoad()
            } 
            else {
                // Fallback on earlier versions
            }
            // Do any additional setup after loading the view.
        }
    
        override var representedObject: AnyObject? {
            didSet {
            // Update the view, if already loaded.
            }
        }
    
        // first Shape
        let pointA1 = NSMakePoint(115.0, 10.0)
        let pointA2 = NSMakePoint(140.0, 10.0)
        let pointA3 = NSMakePoint(150.0, 40.0)
        let pointA4 = NSMakePoint(110.0, 40.0)
    
        // second Shape
        let pointB1 = NSMakePoint(230.0, 10.0)
        let pointB2 = NSMakePoint(260.0, 40.0)
        let pointB3 = NSMakePoint(200.0, 40.0)
    
        // thirdShape
        let pointC1 = NSMakePoint(115.0, 110.0)
        let pointC2 = NSMakePoint(140.0, 110.0)
        let pointC3 = NSMakePoint(150.0, 140.0)
        let pointC4 = NSMakePoint(110.0, 140.0)
    
    
        @IBOutlet weak var shapeHolderView: GHShapeDemoView!
    
        @IBAction func newTrapezoid(sender: AnyObject) {
    
            if shapeHolderView.shapes.count < 1 {
                shapeHolderView.addTrapezoid(pointA1, p2: pointA2, p3: pointA3, p4: pointA4)
            } 
            else {
            shapeHolderView.addTrapezoid(pointC1, p2: pointC2, p3: pointC3, p4: pointC4)
            }
        shapeHolderView.setNeedsDisplayInRect(shapeHolderView.bounds)
        }
    
        @IBAction func newTriangle(sender: AnyObject) {
            shapeHolderView.addTriangle(pointB1, p2: pointB2, p3: pointB3)
            shapeHolderView.setNeedsDisplayInRect(shapeHolderView.bounds)
        }
    
        @IBAction func removeLastShape(sender: AnyObject) {
            if shapeHolderView.shapes.count > 0 {
                shapeHolderView.removeShapeAt(shapeHolderView.shapes.count - 1)
                shapeHolderView.setNeedsDisplayInRect(shapeHolderView.bounds)
            }
        }
    }
    
    21.10.2016
    Новые материалы

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

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

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

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

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

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

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