From 706a13504b4aa892d68fda481360be6e970d919c Mon Sep 17 00:00:00 2001 From: Busskov Date: Sat, 23 Dec 2023 23:26:51 +0300 Subject: [PATCH 01/10] Add gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef20048 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +lab-02/DocksHobos/.idea/ +lab-02/DocksHobos/out/ +lab-02/DocksHobos/DocksHobos.iml +lab-03/Paint/.idea/ +lab-03/Paint/Paint.iml +lab-03/Paint/out/ \ No newline at end of file From 3028dae3664d775fa6a9d86b69e526321f7b036e Mon Sep 17 00:00:00 2001 From: Busskov Date: Sat, 23 Dec 2023 23:31:10 +0300 Subject: [PATCH 02/10] Basic interface, curved line --- .../src/main/kotlin/by/busskov/paint/Main.kt | 109 ++++++++++++++++++ .../main/kotlin/by/busskov/paint/PaintType.kt | 7 ++ lab-03/Paint/src/main/resources/circle.png | Bin 0 -> 563 bytes .../Paint/src/main/resources/curved_line.png | Bin 0 -> 727 bytes lab-03/Paint/src/main/resources/rectangle.png | Bin 0 -> 618 bytes 5 files changed, 116 insertions(+) create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt create mode 100644 lab-03/Paint/src/main/resources/circle.png create mode 100644 lab-03/Paint/src/main/resources/curved_line.png create mode 100644 lab-03/Paint/src/main/resources/rectangle.png diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt new file mode 100644 index 0000000..5b3c73c --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt @@ -0,0 +1,109 @@ +import by.busskov.paint.PaintType +import javafx.application.Application +import javafx.beans.value.ObservableValue +import javafx.scene.Scene +import javafx.scene.canvas.Canvas +import javafx.scene.canvas.GraphicsContext +import javafx.scene.control.Button +import javafx.scene.control.ColorPicker +import javafx.scene.control.Label +import javafx.scene.control.Menu +import javafx.scene.control.MenuBar +import javafx.scene.control.MenuItem +import javafx.scene.control.Slider +import javafx.scene.control.ToolBar +import javafx.scene.layout.VBox +import javafx.scene.paint.Color +import javafx.stage.Stage +import javafx.scene.image.Image +import javafx.scene.image.ImageView +import javafx.scene.shape.StrokeLineCap + +class PaintApp : Application() { + private val canvas: Canvas = Canvas(800.0, 600.0) + private val graphicsContext: GraphicsContext = canvas.graphicsContext2D + private val toolBar: ToolBar = ToolBar() + private val menuBar: MenuBar = MenuBar() + private var paintType: PaintType = PaintType.CURVED_LINE + + override fun start(primaryStage: Stage) { + graphicsContext.lineCap = StrokeLineCap.ROUND + configureMenuBar() + configureToolBar() + val vbox = VBox(menuBar, toolBar, canvas) + + val scene = Scene(vbox) + primaryStage.title = "Paint" + primaryStage.scene = scene + primaryStage.show() + + var previousX = 0.0 + var previousY = 0.0 + canvas.setOnMousePressed { event -> + previousX = event.x + previousY = event.y + } + canvas.setOnMouseDragged { event -> + + graphicsContext.strokeLine(previousX, previousY, event.x, event.y) + previousX = event.x + previousY = event.y + } + } + + private fun configureMenuBar() { + val fileMenu = Menu("File") + val saveItem = MenuItem("Save") + val openItem = MenuItem("Open") + fileMenu.items.addAll(saveItem, openItem) + menuBar.menus.add(fileMenu) + } + + private fun configureToolBar() { + val curvedLine = Button() + curvedLine.graphic = ImageView(Image("file:src/main/resources/curved_line.png")) + curvedLine.setOnAction { + paintType = PaintType.CURVED_LINE + } + + val straightLine = Button("Straight line") + + val oval = Button() + oval.graphic = ImageView(Image("file:src/main/resources/circle.png")) + oval.setOnAction { + paintType = PaintType.OVAL + } + + val rectangle = Button() + rectangle.graphic = ImageView(Image("file:src/main/resources/rectangle.png")) + rectangle.setOnAction { + paintType = PaintType.RECTANGLE + } + + val colorPicker = ColorPicker(Color.BLACK) + colorPicker.setOnAction { + graphicsContext.stroke = colorPicker.value + graphicsContext.fill = colorPicker.value + } + + val sizeSlider = Slider(1.0, 100.0, 5.0) + val sizeLabel = Label("5") + sizeSlider.valueProperty().addListener { _: ObservableValue, _: Number, newValue: Number -> + graphicsContext.lineWidth = sizeSlider.value + sizeLabel.text = graphicsContext.lineWidth.toInt().toString(); + } + + toolBar.items.addAll( + curvedLine, + straightLine, + oval, + rectangle, + colorPicker, + sizeSlider, + sizeLabel) + } +} + +fun main() { + Application.launch(PaintApp::class.java) +} \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt new file mode 100644 index 0000000..cc33025 --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt @@ -0,0 +1,7 @@ +package by.busskov.paint + +enum class PaintType { + CURVED_LINE, + OVAL, + RECTANGLE +} \ No newline at end of file diff --git a/lab-03/Paint/src/main/resources/circle.png b/lab-03/Paint/src/main/resources/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..4ddb2f389bba580af20530eae230bf6d4c171d44 GIT binary patch literal 563 zcmV-30?hr1P)uQwPI8O!rjeF(-P4W2M}*&5z|NLcb>!OT*O;h|c!7j78*! z6N_Sw&2T3mx>({eAwRL$^?<({Z6nhh9kAFn2Ydh*vB4`53G-lg&eedo*wAN?2T>LY zh#HBwv~uLL*rCsXW{wV6>@Xl|qvCST2+X12g9wT~iVMGt)S%ewSPhG7NYuF4mP{v| zy(#@)ZMliWSso^bLRs9?%s7(k<8-G6_DNpP^It;q77A)tusi?&002ovPDHLkV1ne6 B>?8mH literal 0 HcmV?d00001 diff --git a/lab-03/Paint/src/main/resources/curved_line.png b/lab-03/Paint/src/main/resources/curved_line.png new file mode 100644 index 0000000000000000000000000000000000000000..9535d0259ccbed61ee57e058ea395939b75a76da GIT binary patch literal 727 zcmV;|0x127P)fHvGf5H} zjTNOVq}2Qil$C{z#)e{r5}FN3h$2Oi1qS(9@~bd2*J7SmPtSYj{qdN$IrUw<_nvdU z-*fK0?|DLu(R5ndw6doJgb;c|2oFLCZ$k)&Tdp9+_zxJw7-9}v^FC9J_N?le7{bnk z#_pDP07nZ|SMUuhTG;_uhTjSDAK2VV^2!nPqodH6H`o$meAv?VXLdhP{oW>8*pB&_ zQT7NP7s$WXONP1FgLC+ji~WvgIE2nd1>7kJ_*lbv2`;F9swuofeF<63l z7&|Z<0lTu!)r)(IbwvR)aj!w*r|8YKuv_&-u7{uK`6nPt)P=J(f*#=@I&v}V@xJ8I z{YGD?T!e~+5SC*bW|$b)-*GX<_@v;s0=F?cN+K?!Eq5`C`?s^bMK~aPl4S%8C6a%_f}GE)gwOd@ zzA_)B8xm{#V~p=|zgH%FMv~c@tiH{}Wy)9fvf$GpJ>vHzbCx)QuZ6@vur%9Sh${*5 z59pdI0oM}*<9L-=ui(g}`AP^Fs6mv%N6eZ^0d2V2Ao&Q^)XdkcfK0?3T&hEU6Z7ll zDkC5#WIL{z`26~VyEukExuI1HRl}&Pg%H|fjH6A*O_hMM_7|^002ov JPDHLkV1gJoO0fU{ literal 0 HcmV?d00001 diff --git a/lab-03/Paint/src/main/resources/rectangle.png b/lab-03/Paint/src/main/resources/rectangle.png new file mode 100644 index 0000000000000000000000000000000000000000..23a765dc17a86a53698a05368ca0e8c6d93fb5d9 GIT binary patch literal 618 zcmV-w0+s!VP)EX>4Tx04R}tkv&MmKpe$iQ?()$K?{mFWT;MdQ4!rr6^me@v=v%)FuC+YXws0R zxHt-~1qVMCs}3&Cx;nTDg5U>;tBaGOibkx9)Fhls^u8_R9XN`^{2MI2UCjq-)8 z%L?Z$&T6^Jn)l={4Cb}vG}md4AdW>Okc0>sRcxRP3lUm1QcR?1Kjz^dcKk_l$>iDq zBgZ@{P$4;f@IUz7ty!3yaFc>Dp!3DHKSqJTF3_mi_V=-EH%pV2qvfPq_}XVvYkxsTHaAVXa(-2exN zz*v#8*F4_c-QL^3XPW)}0NodI(YKdk%K!iX32;bRa{vG?BLDy{BLR4&KXw2B00(qQ zO+^Ri3>Oj%AV<<0X8-^I8FWQhbVF}#ZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b z08>dsK~z}7V`QkjF3d21fQey%!KedR;H-AdH*~eR^18@q6b!O7fFq8m25_XY(I^;o zz^DU89Wd&EQ3s4VU=X(l=+s6Wje-Gp0OM%iaKt+R0Mqjdr^anF%m4rY07*qoM6N<$ Ef*0=z-v9sr literal 0 HcmV?d00001 From 74c0d18dcccc99fa3ae0e3598536809acea0fe15 Mon Sep 17 00:00:00 2001 From: Busskov Date: Sun, 24 Dec 2023 01:19:25 +0300 Subject: [PATCH 03/10] Add circle and rectangle --- .../src/main/kotlin/by/busskov/paint/Main.kt | 102 ++++++++++++++---- .../main/kotlin/by/busskov/paint/PaintType.kt | 3 +- lab-03/Paint/src/main/resources/filling.png | Bin 0 -> 437 bytes 3 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 lab-03/Paint/src/main/resources/filling.png diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt index 5b3c73c..48c6af3 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt @@ -12,42 +12,94 @@ import javafx.scene.control.MenuBar import javafx.scene.control.MenuItem import javafx.scene.control.Slider import javafx.scene.control.ToolBar -import javafx.scene.layout.VBox import javafx.scene.paint.Color import javafx.stage.Stage import javafx.scene.image.Image import javafx.scene.image.ImageView +import javafx.scene.layout.GridPane import javafx.scene.shape.StrokeLineCap +import kotlin.math.min +import kotlin.math.abs class PaintApp : Application() { - private val canvas: Canvas = Canvas(800.0, 600.0) - private val graphicsContext: GraphicsContext = canvas.graphicsContext2D + private val canvas1: Canvas = Canvas(800.0, 600.0) + private val graphicsContext1: GraphicsContext = canvas1.graphicsContext2D + private val canvas2: Canvas = Canvas(800.0, 600.0) + private val graphicsContext2: GraphicsContext = canvas2.graphicsContext2D private val toolBar: ToolBar = ToolBar() private val menuBar: MenuBar = MenuBar() private var paintType: PaintType = PaintType.CURVED_LINE override fun start(primaryStage: Stage) { - graphicsContext.lineCap = StrokeLineCap.ROUND configureMenuBar() configureToolBar() - val vbox = VBox(menuBar, toolBar, canvas) + graphicsContext1.lineCap = StrokeLineCap.ROUND - val scene = Scene(vbox) + val gridPane = GridPane() + gridPane.add(menuBar, 0, 0) + gridPane.add(toolBar, 0, 1) + gridPane.add(canvas1, 0, 2) + gridPane.add(canvas2, 0, 2) + + val scene = Scene(gridPane) primaryStage.title = "Paint" primaryStage.scene = scene primaryStage.show() - var previousX = 0.0 - var previousY = 0.0 - canvas.setOnMousePressed { event -> - previousX = event.x - previousY = event.y + var baseX = 0.0 + var baseY = 0.0 + canvas2.setOnMousePressed { event -> + baseX = event.x + baseY = event.y + } + + canvas2.setOnMouseDragged { event -> + when(paintType) { + PaintType.CURVED_LINE -> { + graphicsContext1.strokeLine(baseX, baseY, event.x, event.y) + baseX = event.x + baseY = event.y + } + PaintType.OVAL -> { + graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) + graphicsContext2.strokeOval( + min(baseX, event.x), + min(baseY, event.y), + abs(event.x - baseX), + abs(event.y - baseY)) + } + PaintType.RECTANGLE -> { + graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) + graphicsContext2.strokeRect( + min(baseX, event.x), + min(baseY, event.y), + abs(event.x - baseX), + abs(event.y - baseY)) + } + else -> {} + } } - canvas.setOnMouseDragged { event -> - - graphicsContext.strokeLine(previousX, previousY, event.x, event.y) - previousX = event.x - previousY = event.y + + canvas2.setOnMouseReleased { event -> + when(paintType) { + PaintType.OVAL -> { + graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) + graphicsContext1.strokeOval( + min(baseX, event.x), + min(baseY, event.y), + abs(event.x - baseX), + abs(event.y - baseY)) + } + PaintType.RECTANGLE -> { + graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) + graphicsContext1.strokeRect( + min(baseX, event.x), + min(baseY, event.y), + abs(event.x - baseX), + abs(event.y - baseY)) + } + else -> {} + } } } @@ -80,17 +132,26 @@ class PaintApp : Application() { paintType = PaintType.RECTANGLE } + val filling = Button() + filling.graphic = ImageView(Image("file:src/main/resources/filling.png")) + filling.setOnAction { + paintType = PaintType.FILLING + } + val colorPicker = ColorPicker(Color.BLACK) colorPicker.setOnAction { - graphicsContext.stroke = colorPicker.value - graphicsContext.fill = colorPicker.value + graphicsContext1.stroke = colorPicker.value + graphicsContext1.fill = colorPicker.value + graphicsContext2.stroke = colorPicker.value + graphicsContext2.fill = colorPicker.value } val sizeSlider = Slider(1.0, 100.0, 5.0) val sizeLabel = Label("5") sizeSlider.valueProperty().addListener { _: ObservableValue, _: Number, newValue: Number -> - graphicsContext.lineWidth = sizeSlider.value - sizeLabel.text = graphicsContext.lineWidth.toInt().toString(); + graphicsContext1.lineWidth = sizeSlider.value + graphicsContext2.lineWidth = graphicsContext1.lineWidth + sizeLabel.text = graphicsContext1.lineWidth.toInt().toString(); } toolBar.items.addAll( @@ -98,6 +159,7 @@ class PaintApp : Application() { straightLine, oval, rectangle, + filling, colorPicker, sizeSlider, sizeLabel) diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt index cc33025..9887dca 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt @@ -3,5 +3,6 @@ package by.busskov.paint enum class PaintType { CURVED_LINE, OVAL, - RECTANGLE + RECTANGLE, + FILLING } \ No newline at end of file diff --git a/lab-03/Paint/src/main/resources/filling.png b/lab-03/Paint/src/main/resources/filling.png new file mode 100644 index 0000000000000000000000000000000000000000..ca6b3a9f7db100f77b6e7d0f0a19f083a5aa97dc GIT binary patch literal 437 zcmV;m0ZRUfP)GtnzLFZN6r!JbI;IkcLCh=5dIT3mMzm{lM z=0aA`#K$k1FW?C~ArQMIArkXR+{JxwSQ`dJVh;P6vFi|kA`oeFxX+1MtqLM*4lg-z zTcH4hK;)UjZ6>aR#UcRxAigt)ZM|=YlcMH{nxfmBBs}e7o@fKdIFKoRwcPNZ)rgZm fh@NC)ja68myY1Sb5y~W900000NkvXXu0mjf%fq$a literal 0 HcmV?d00001 From b1efeb0aa14317236d8477268b200544fce93c10 Mon Sep 17 00:00:00 2001 From: Busskov Date: Sun, 24 Dec 2023 15:23:06 +0300 Subject: [PATCH 04/10] Add flood fill --- .../src/main/kotlin/by/busskov/paint/Main.kt | 107 +++++++++++++----- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt index 48c6af3..e9e2084 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt @@ -16,10 +16,15 @@ import javafx.scene.paint.Color import javafx.stage.Stage import javafx.scene.image.Image import javafx.scene.image.ImageView +import javafx.scene.image.PixelReader +import javafx.scene.image.PixelWriter import javafx.scene.layout.GridPane +import javafx.scene.shape.Line import javafx.scene.shape.StrokeLineCap +import java.util.* import kotlin.math.min import kotlin.math.abs +import kotlin.math.roundToInt class PaintApp : Application() { private val canvas1: Canvas = Canvas(800.0, 600.0) @@ -29,11 +34,17 @@ class PaintApp : Application() { private val toolBar: ToolBar = ToolBar() private val menuBar: MenuBar = MenuBar() private var paintType: PaintType = PaintType.CURVED_LINE + private var fillColor: Color = Color.BLACK override fun start(primaryStage: Stage) { configureMenuBar() configureToolBar() + graphicsContext1.fill = Color.WHITE + graphicsContext1.fillRect(0.0, 0.0, canvas1.width, canvas1.height) + graphicsContext1.fill = Color.BLACK graphicsContext1.lineCap = StrokeLineCap.ROUND + graphicsContext1.lineWidth = 2.0 + graphicsContext2.lineWidth = 2.0 val gridPane = GridPane() gridPane.add(menuBar, 0, 0) @@ -49,32 +60,38 @@ class PaintApp : Application() { var baseX = 0.0 var baseY = 0.0 canvas2.setOnMousePressed { event -> - baseX = event.x - baseY = event.y + baseX = round(event.x) + baseY = round(event.y) + } + + canvas2.setOnMouseClicked { event -> + if (paintType == PaintType.FILLING) { + floodFill(event.x.toInt(), event.y.toInt()) + } } canvas2.setOnMouseDragged { event -> when(paintType) { PaintType.CURVED_LINE -> { - graphicsContext1.strokeLine(baseX, baseY, event.x, event.y) - baseX = event.x - baseY = event.y + graphicsContext1.strokeLine(baseX, baseY, round(event.x), round(event.y)) + baseX = round(event.x) + baseY = round(event.y) } PaintType.OVAL -> { graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) graphicsContext2.strokeOval( - min(baseX, event.x), - min(baseY, event.y), - abs(event.x - baseX), - abs(event.y - baseY)) + round(min(baseX, event.x)), + round(min(baseY, event.y)), + round(abs(event.x - baseX)), + round(abs(event.y - baseY))) } PaintType.RECTANGLE -> { graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) graphicsContext2.strokeRect( - min(baseX, event.x), - min(baseY, event.y), - abs(event.x - baseX), - abs(event.y - baseY)) + round(min(baseX, event.x)), + round(min(baseY, event.y)), + round(abs(event.x - baseX)), + round(abs(event.y - baseY))) } else -> {} } @@ -85,18 +102,18 @@ class PaintApp : Application() { PaintType.OVAL -> { graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) graphicsContext1.strokeOval( - min(baseX, event.x), - min(baseY, event.y), - abs(event.x - baseX), - abs(event.y - baseY)) + round(min(baseX, event.x)), + round(min(baseY, event.y)), + round(abs(event.x - baseX)), + round(abs(event.y - baseY))) } PaintType.RECTANGLE -> { graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) graphicsContext1.strokeRect( - min(baseX, event.x), - min(baseY, event.y), - abs(event.x - baseX), - abs(event.y - baseY)) + round(min(baseX, event.x)), + round(min(baseY, event.y)), + round(abs(event.x - baseX)), + round(abs(event.y - baseY))) } else -> {} } @@ -144,13 +161,14 @@ class PaintApp : Application() { graphicsContext1.fill = colorPicker.value graphicsContext2.stroke = colorPicker.value graphicsContext2.fill = colorPicker.value + fillColor = colorPicker.value } - val sizeSlider = Slider(1.0, 100.0, 5.0) - val sizeLabel = Label("5") - sizeSlider.valueProperty().addListener { _: ObservableValue, _: Number, newValue: Number -> - graphicsContext1.lineWidth = sizeSlider.value - graphicsContext2.lineWidth = graphicsContext1.lineWidth + val sizeSlider = Slider(1.0, 100.0, 2.0) + val sizeLabel = Label("2") + sizeSlider.valueProperty().addListener { _: ObservableValue, _: Number, _: Number -> + graphicsContext1.lineWidth = round(sizeSlider.value) + graphicsContext2.lineWidth = round(graphicsContext1.lineWidth) sizeLabel.text = graphicsContext1.lineWidth.toInt().toString(); } @@ -164,6 +182,43 @@ class PaintApp : Application() { sizeSlider, sizeLabel) } + + private fun floodFill(x: Int, y: Int) { + val points = Array(canvas1.width.toInt()) {Array(canvas1.height.toInt()){false}} + val pixelReader = canvas1.snapshot(null, null).pixelReader + val color = pixelReader.getColor(x, y) + val pointsQueue = LinkedList>() + pointsQueue.add(x to y) + + while(!pointsQueue.isEmpty()) { + val (curX, curY) = pointsQueue.pollLast() + if (curX < 0 || curX >= points.size || curY < 0 || curY >= points[0].size) { + continue + } + if ( + pixelReader.getColor(curX, curY) == color + && !points[curX][curY]) { + points[curX][curY] = true + pointsQueue.add(curX to (curY - 1)) + pointsQueue.add(curX to (curY + 1)) + pointsQueue.add((curX - 1) to curY) + pointsQueue.add((curX + 1) to curY) + } + } + + val writer = graphicsContext1.pixelWriter + for (i in points.indices) { + for (j in points[0].indices) { + if (points[i][j]) { + writer.setColor(i, j, fillColor) + } + } + } + } + + private fun round(x: Double) : Double { + return x.toInt().toDouble() + } } fun main() { From 558fe6d8f6c8ff57f2ab6263ad8e38670728a5f9 Mon Sep 17 00:00:00 2001 From: Busskov Date: Sun, 24 Dec 2023 15:58:56 +0300 Subject: [PATCH 05/10] Add straight line --- .../src/main/kotlin/by/busskov/paint/Main.kt | 32 ++++++++++++++---- .../main/kotlin/by/busskov/paint/PaintType.kt | 3 +- .../src/main/resources/straight_line.png | Bin 0 -> 245 bytes 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 lab-03/Paint/src/main/resources/straight_line.png diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt index e9e2084..04f1b09 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt @@ -16,15 +16,13 @@ import javafx.scene.paint.Color import javafx.stage.Stage import javafx.scene.image.Image import javafx.scene.image.ImageView -import javafx.scene.image.PixelReader -import javafx.scene.image.PixelWriter import javafx.scene.layout.GridPane -import javafx.scene.shape.Line import javafx.scene.shape.StrokeLineCap -import java.util.* +import java.awt.Stroke +import java.util.LinkedList import kotlin.math.min +import kotlin.math.max import kotlin.math.abs -import kotlin.math.roundToInt class PaintApp : Application() { private val canvas1: Canvas = Canvas(800.0, 600.0) @@ -93,6 +91,14 @@ class PaintApp : Application() { round(abs(event.x - baseX)), round(abs(event.y - baseY))) } + PaintType.STRAIGHT_LINE -> { + graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) + graphicsContext2.strokeLine( + round(baseX), + round(baseY), + round(event.x), + round(event.y)) + } else -> {} } } @@ -115,6 +121,16 @@ class PaintApp : Application() { round(abs(event.x - baseX)), round(abs(event.y - baseY))) } + PaintType.STRAIGHT_LINE -> { + graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) + graphicsContext1.lineCap = StrokeLineCap.SQUARE + graphicsContext1.strokeLine( + round(baseX), + round(baseY), + round(event.x), + round(event.y)) + graphicsContext1.lineCap = StrokeLineCap.ROUND + } else -> {} } } @@ -135,7 +151,11 @@ class PaintApp : Application() { paintType = PaintType.CURVED_LINE } - val straightLine = Button("Straight line") + val straightLine = Button() + straightLine.graphic = ImageView(Image("file:src/main/resources/straight_line.png")) + straightLine.setOnAction { + paintType = PaintType.STRAIGHT_LINE + } val oval = Button() oval.graphic = ImageView(Image("file:src/main/resources/circle.png")) diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt index 9887dca..861a8e5 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt @@ -4,5 +4,6 @@ enum class PaintType { CURVED_LINE, OVAL, RECTANGLE, - FILLING + FILLING, + STRAIGHT_LINE } \ No newline at end of file diff --git a/lab-03/Paint/src/main/resources/straight_line.png b/lab-03/Paint/src/main/resources/straight_line.png new file mode 100644 index 0000000000000000000000000000000000000000..4889118e5b7472f20779581dfdce43788795ca73 GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVj%qngc&O}3ikpTk|nMY zCBgY=CFO}lsSM@i<$9TU*~Q6;1*v-ZMd`EO*+>BuHF>%?hD5Z!y=cgFD1fKoA-`sZ ze=_U(-0hDZRIX}ja=&oU?nMC0beoARM*rnG?X7&*7IZU;mzX|Mw^?4BAkQh0_Hk3W zls3-|`dblY4o&KJs$Qn&3m`^Y^ Date: Sun, 24 Dec 2023 20:34:21 +0300 Subject: [PATCH 06/10] Add eraser and export --- .../src/main/kotlin/by/busskov/paint/Main.kt | 67 +++++++++++++++--- .../main/kotlin/by/busskov/paint/PaintType.kt | 3 +- lab-03/Paint/src/main/resources/eraser.png | Bin 0 -> 535 bytes 3 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 lab-03/Paint/src/main/resources/eraser.png diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt index 04f1b09..2f346ea 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt @@ -1,6 +1,8 @@ -import by.busskov.paint.PaintType +package by.busskov.paint + import javafx.application.Application import javafx.beans.value.ObservableValue +import javafx.embed.swing.SwingFXUtils import javafx.scene.Scene import javafx.scene.canvas.Canvas import javafx.scene.canvas.GraphicsContext @@ -16,19 +18,23 @@ import javafx.scene.paint.Color import javafx.stage.Stage import javafx.scene.image.Image import javafx.scene.image.ImageView +import javafx.scene.image.WritableImage import javafx.scene.layout.GridPane import javafx.scene.shape.StrokeLineCap -import java.awt.Stroke +import javafx.stage.FileChooser +import java.awt.image.RenderedImage +import java.io.IOException +import java.io.Serializable import java.util.LinkedList +import javax.imageio.ImageIO import kotlin.math.min -import kotlin.math.max import kotlin.math.abs -class PaintApp : Application() { +class PaintApp : Application(), Serializable { private val canvas1: Canvas = Canvas(800.0, 600.0) - private val graphicsContext1: GraphicsContext = canvas1.graphicsContext2D + @Transient private val graphicsContext1: GraphicsContext = canvas1.graphicsContext2D private val canvas2: Canvas = Canvas(800.0, 600.0) - private val graphicsContext2: GraphicsContext = canvas2.graphicsContext2D + @Transient private val graphicsContext2: GraphicsContext = canvas2.graphicsContext2D private val toolBar: ToolBar = ToolBar() private val menuBar: MenuBar = MenuBar() private var paintType: PaintType = PaintType.CURVED_LINE @@ -75,6 +81,13 @@ class PaintApp : Application() { baseX = round(event.x) baseY = round(event.y) } + PaintType.ERASE -> { + graphicsContext1.stroke = Color.WHITE + graphicsContext1.strokeLine(baseX, baseY, round(event.x), round(event.y)) + graphicsContext1.stroke = fillColor + baseX = round(event.x) + baseY = round(event.y) + } PaintType.OVAL -> { graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) graphicsContext2.strokeOval( @@ -138,9 +151,19 @@ class PaintApp : Application() { private fun configureMenuBar() { val fileMenu = Menu("File") + val exportItem = MenuItem("Export PNG") val saveItem = MenuItem("Save") val openItem = MenuItem("Open") - fileMenu.items.addAll(saveItem, openItem) + fileMenu.items.addAll(exportItem, saveItem, openItem) + exportItem.setOnAction { + export() + } + saveItem.setOnAction { + + } + openItem.setOnAction { + + } menuBar.menus.add(fileMenu) } @@ -175,6 +198,12 @@ class PaintApp : Application() { paintType = PaintType.FILLING } + val eraser = Button() + eraser.graphic = ImageView(Image("file:src/main/resources/eraser.png")) + eraser.setOnAction { + paintType = PaintType.ERASE + } + val colorPicker = ColorPicker(Color.BLACK) colorPicker.setOnAction { graphicsContext1.stroke = colorPicker.value @@ -198,9 +227,10 @@ class PaintApp : Application() { oval, rectangle, filling, + eraser, colorPicker, sizeSlider, - sizeLabel) + sizeLabel,) } private fun floodFill(x: Int, y: Int) { @@ -239,6 +269,27 @@ class PaintApp : Application() { private fun round(x: Double) : Double { return x.toInt().toDouble() } + + private fun export() { + var chooser = FileChooser() + chooser.extensionFilters.addAll( + FileChooser.ExtensionFilter("PNG Files", "*.png") + ) + chooser.title = "Save File" + + val file = chooser.showSaveDialog(Stage()) + if (file != null) { + try { + val writableImage = WritableImage(canvas1.width.toInt(), canvas1.height.toInt()) + canvas1.snapshot(null, writableImage) + val renderedImage: RenderedImage = SwingFXUtils.fromFXImage(writableImage, null) + ImageIO.write(renderedImage, file.extension, file) + } catch (ex: IOException) { + ex.printStackTrace() + println("Error in writing to file!") + } + } + } } fun main() { diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt index 861a8e5..9462f7b 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/PaintType.kt @@ -5,5 +5,6 @@ enum class PaintType { OVAL, RECTANGLE, FILLING, - STRAIGHT_LINE + STRAIGHT_LINE, + ERASE } \ No newline at end of file diff --git a/lab-03/Paint/src/main/resources/eraser.png b/lab-03/Paint/src/main/resources/eraser.png new file mode 100644 index 0000000000000000000000000000000000000000..4decc7a8f3ec2e26c9960724a08a9fb9549eecf5 GIT binary patch literal 535 zcmV+y0_gpTP)0K{L@7fPCTrcnUJV`E8j4|}G`toWd zBI6=b7LkddpfRSSVHZ&Wz5rv#BQWNb&jEg>z Date: Sun, 24 Dec 2023 21:49:02 +0300 Subject: [PATCH 07/10] Add save serialization --- .../src/main/kotlin/by/busskov/paint/Main.kt | 63 ++++++++++++++++--- .../busskov/paint/userActions/ColorChange.kt | 3 + .../by/busskov/paint/userActions/FloodFill.kt | 3 + .../by/busskov/paint/userActions/Line.kt | 3 + .../by/busskov/paint/userActions/Oval.kt | 3 + .../by/busskov/paint/userActions/Rectangle.kt | 3 + .../by/busskov/paint/userActions/RoundLine.kt | 3 + .../busskov/paint/userActions/StraightLine.kt | 3 + .../busskov/paint/userActions/UserAction.kt | 5 ++ 9 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/ColorChange.kt create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/FloodFill.kt create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Line.kt create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Oval.kt create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Rectangle.kt create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/RoundLine.kt create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/StraightLine.kt create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/UserAction.kt diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt index 2f346ea..1acd598 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt @@ -1,5 +1,6 @@ package by.busskov.paint +import by.busskov.paint.userActions.* import javafx.application.Application import javafx.beans.value.ObservableValue import javafx.embed.swing.SwingFXUtils @@ -23,7 +24,9 @@ import javafx.scene.layout.GridPane import javafx.scene.shape.StrokeLineCap import javafx.stage.FileChooser import java.awt.image.RenderedImage +import java.io.FileOutputStream import java.io.IOException +import java.io.ObjectOutputStream import java.io.Serializable import java.util.LinkedList import javax.imageio.ImageIO @@ -31,14 +34,15 @@ import kotlin.math.min import kotlin.math.abs class PaintApp : Application(), Serializable { - private val canvas1: Canvas = Canvas(800.0, 600.0) + @Transient private val canvas1: Canvas = Canvas(800.0, 600.0) @Transient private val graphicsContext1: GraphicsContext = canvas1.graphicsContext2D - private val canvas2: Canvas = Canvas(800.0, 600.0) + @Transient private val canvas2: Canvas = Canvas(800.0, 600.0) @Transient private val graphicsContext2: GraphicsContext = canvas2.graphicsContext2D - private val toolBar: ToolBar = ToolBar() - private val menuBar: MenuBar = MenuBar() - private var paintType: PaintType = PaintType.CURVED_LINE - private var fillColor: Color = Color.BLACK + @Transient private val toolBar: ToolBar = ToolBar() + @Transient private val menuBar: MenuBar = MenuBar() + @Transient private var paintType: PaintType = PaintType.CURVED_LINE + @Transient private var fillColor: Color = Color.BLACK + private var userActions: LinkedList = LinkedList() override fun start(primaryStage: Stage) { configureMenuBar() @@ -71,6 +75,7 @@ class PaintApp : Application(), Serializable { canvas2.setOnMouseClicked { event -> if (paintType == PaintType.FILLING) { floodFill(event.x.toInt(), event.y.toInt()) + userActions.add(FloodFill(event.x.toInt(), event.y.toInt())) } } @@ -78,13 +83,17 @@ class PaintApp : Application(), Serializable { when(paintType) { PaintType.CURVED_LINE -> { graphicsContext1.strokeLine(baseX, baseY, round(event.x), round(event.y)) + userActions.add(RoundLine(baseX, baseY, round(event.x), round(event.y))) baseX = round(event.x) baseY = round(event.y) } PaintType.ERASE -> { graphicsContext1.stroke = Color.WHITE + userActions.add(ColorChange(Color.WHITE.red, Color.WHITE.green, Color.WHITE.blue)) graphicsContext1.strokeLine(baseX, baseY, round(event.x), round(event.y)) + userActions.add(RoundLine(baseX, baseY, round(event.x), round(event.y))) graphicsContext1.stroke = fillColor + userActions.add(ColorChange(fillColor.red, fillColor.green, fillColor.blue)) baseX = round(event.x) baseY = round(event.y) } @@ -125,6 +134,12 @@ class PaintApp : Application(), Serializable { round(min(baseY, event.y)), round(abs(event.x - baseX)), round(abs(event.y - baseY))) + userActions.add(Oval( + round(min(baseX, event.x)), + round(min(baseY, event.y)), + round(abs(event.x - baseX)), + round(abs(event.y - baseY)) + )) } PaintType.RECTANGLE -> { graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) @@ -133,6 +148,12 @@ class PaintApp : Application(), Serializable { round(min(baseY, event.y)), round(abs(event.x - baseX)), round(abs(event.y - baseY))) + userActions.add(Rectangle( + round(min(baseX, event.x)), + round(min(baseY, event.y)), + round(abs(event.x - baseX)), + round(abs(event.y - baseY)) + )) } PaintType.STRAIGHT_LINE -> { graphicsContext2.clearRect(0.0, 0.0, canvas2.width, canvas2.height) @@ -142,6 +163,12 @@ class PaintApp : Application(), Serializable { round(baseY), round(event.x), round(event.y)) + userActions.add(StraightLine( + round(baseX), + round(baseY), + round(event.x), + round(event.y) + )) graphicsContext1.lineCap = StrokeLineCap.ROUND } else -> {} @@ -159,10 +186,10 @@ class PaintApp : Application(), Serializable { export() } saveItem.setOnAction { - + save(); } openItem.setOnAction { - + open(); } menuBar.menus.add(fileMenu) } @@ -211,6 +238,7 @@ class PaintApp : Application(), Serializable { graphicsContext2.stroke = colorPicker.value graphicsContext2.fill = colorPicker.value fillColor = colorPicker.value + userActions.add(ColorChange(fillColor.red, fillColor.green, fillColor.blue)) } val sizeSlider = Slider(1.0, 100.0, 2.0) @@ -290,6 +318,25 @@ class PaintApp : Application(), Serializable { } } } + + private fun save() { + var chooser = FileChooser() + chooser.extensionFilters.addAll( + FileChooser.ExtensionFilter("Vova Files", "*.vova") + ) + chooser.title = "Save Project" + + val file = chooser.showSaveDialog(Stage()) + if (file != null) { + ObjectOutputStream(FileOutputStream(file)).use {stream -> + stream.writeObject(this) + } + } + } + + private fun open() { + + } } fun main() { diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/ColorChange.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/ColorChange.kt new file mode 100644 index 0000000..f6e3dc7 --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/ColorChange.kt @@ -0,0 +1,3 @@ +package by.busskov.paint.userActions + +class ColorChange(val r: Double, val g: Double, val b: Double) : UserAction() \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/FloodFill.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/FloodFill.kt new file mode 100644 index 0000000..55c24e4 --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/FloodFill.kt @@ -0,0 +1,3 @@ +package by.busskov.paint.userActions + +class FloodFill(val x: Int, val y: Int) : UserAction() \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Line.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Line.kt new file mode 100644 index 0000000..a5583b8 --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Line.kt @@ -0,0 +1,3 @@ +package by.busskov.paint.userActions + +open class Line(val startX: Double, startY: Double, endX: Double, endY: Double) : UserAction() \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Oval.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Oval.kt new file mode 100644 index 0000000..a2d22b9 --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Oval.kt @@ -0,0 +1,3 @@ +package by.busskov.paint.userActions + +class Oval(val x: Double, val y: Double, val width: Double, val height: Double) : UserAction() \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Rectangle.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Rectangle.kt new file mode 100644 index 0000000..e983691 --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Rectangle.kt @@ -0,0 +1,3 @@ +package by.busskov.paint.userActions + +class Rectangle(val x: Double, val y: Double, val width: Double, val height: Double) : UserAction() \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/RoundLine.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/RoundLine.kt new file mode 100644 index 0000000..fe11880 --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/RoundLine.kt @@ -0,0 +1,3 @@ +package by.busskov.paint.userActions + +class RoundLine(x1: Double, y1: Double, x2: Double, y2: Double) : Line(x1, y1, x2, y2) \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/StraightLine.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/StraightLine.kt new file mode 100644 index 0000000..a1a0720 --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/StraightLine.kt @@ -0,0 +1,3 @@ +package by.busskov.paint.userActions + +class StraightLine (x1: Double, y1: Double, x2: Double, y2: Double) : Line(x1, y1, x2, y2) \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/UserAction.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/UserAction.kt new file mode 100644 index 0000000..75a4dbd --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/UserAction.kt @@ -0,0 +1,5 @@ +package by.busskov.paint.userActions + +import java.io.Serializable + +open class UserAction : Serializable \ No newline at end of file From 3347ff214cba771115f38e1e122d3274dc7fd9d6 Mon Sep 17 00:00:00 2001 From: Busskov Date: Sun, 24 Dec 2023 23:38:13 +0300 Subject: [PATCH 08/10] Add deserialize --- .../src/main/kotlin/by/busskov/paint/Main.kt | 105 ++++++++++++++++-- .../by/busskov/paint/userActions/Line.kt | 2 +- .../paint/userActions/LineWidthChange.kt | 3 + 3 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/LineWidthChange.kt diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt index 1acd598..fe11791 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt @@ -24,14 +24,12 @@ import javafx.scene.layout.GridPane import javafx.scene.shape.StrokeLineCap import javafx.stage.FileChooser import java.awt.image.RenderedImage -import java.io.FileOutputStream -import java.io.IOException -import java.io.ObjectOutputStream -import java.io.Serializable +import java.io.* import java.util.LinkedList import javax.imageio.ImageIO import kotlin.math.min import kotlin.math.abs +import kotlin.math.max class PaintApp : Application(), Serializable { @Transient private val canvas1: Canvas = Canvas(800.0, 600.0) @@ -42,6 +40,9 @@ class PaintApp : Application(), Serializable { @Transient private val menuBar: MenuBar = MenuBar() @Transient private var paintType: PaintType = PaintType.CURVED_LINE @Transient private var fillColor: Color = Color.BLACK + @Transient val colorPicker = ColorPicker(Color.BLACK) + @Transient val sizeSlider = Slider(1.0, 100.0, 2.0) + @Transient val sizeLabel = Label("2") private var userActions: LinkedList = LinkedList() override fun start(primaryStage: Stage) { @@ -231,7 +232,6 @@ class PaintApp : Application(), Serializable { paintType = PaintType.ERASE } - val colorPicker = ColorPicker(Color.BLACK) colorPicker.setOnAction { graphicsContext1.stroke = colorPicker.value graphicsContext1.fill = colorPicker.value @@ -241,11 +241,10 @@ class PaintApp : Application(), Serializable { userActions.add(ColorChange(fillColor.red, fillColor.green, fillColor.blue)) } - val sizeSlider = Slider(1.0, 100.0, 2.0) - val sizeLabel = Label("2") - sizeSlider.valueProperty().addListener { _: ObservableValue, _: Number, _: Number -> + sizeSlider.setOnMouseDragged { graphicsContext1.lineWidth = round(sizeSlider.value) - graphicsContext2.lineWidth = round(graphicsContext1.lineWidth) + graphicsContext2.lineWidth = graphicsContext1.lineWidth + userActions.add(LineWidthChange(graphicsContext1.lineWidth)) sizeLabel.text = graphicsContext1.lineWidth.toInt().toString(); } @@ -320,7 +319,7 @@ class PaintApp : Application(), Serializable { } private fun save() { - var chooser = FileChooser() + val chooser = FileChooser() chooser.extensionFilters.addAll( FileChooser.ExtensionFilter("Vova Files", "*.vova") ) @@ -335,7 +334,93 @@ class PaintApp : Application(), Serializable { } private fun open() { + val chooser = FileChooser() + chooser.extensionFilters.addAll( + FileChooser.ExtensionFilter("Vova Files", "*.vova") + ) + chooser.title = "Open Project" + + val file = chooser.showOpenDialog(Stage()) + if (file != null) { + ObjectInputStream(FileInputStream(file)).use { stream -> + val deserialized = stream.readObject() as? PaintApp + deserialized?.let { + graphicsContext1.fill = Color.WHITE + graphicsContext1.fillRect(0.0, 0.0, canvas1.width, canvas1.height) + graphicsContext1.fill = Color.BLACK + + fillColor = Color.BLACK + graphicsContext1.stroke = fillColor + graphicsContext2.stroke = fillColor + graphicsContext2.fill = fillColor + graphicsContext2.lineWidth = 2.0 + graphicsContext1.lineWidth = 2.0 + + this.userActions = deserialized.userActions + projectOpen() + } + } + } + } + + private fun projectOpen() { + while(!userActions.isEmpty()) { + when(val action = userActions.pollFirst()) { + is ColorChange -> { + fillColor = Color(action.r, action.g, action.b, 1.0) + colorPicker.value = fillColor + graphicsContext1.stroke = fillColor + graphicsContext1.fill = fillColor + graphicsContext2.stroke = fillColor + graphicsContext2.fill = fillColor + } + is FloodFill -> { + floodFill(action.x, action.y) + } + is Oval -> { + graphicsContext1.strokeOval( + action.x, + action.y, + action.width, + action.height + ) + } + is Rectangle -> { + graphicsContext1.strokeRect( + action.x, + action.y, + action.width, + action.height + ) + } + is RoundLine -> { + graphicsContext1.lineCap = StrokeLineCap.ROUND + graphicsContext1.strokeLine( + action.startX, + action.startY, + action.endX, + action.endY + ) + } + is StraightLine -> { + graphicsContext1.lineCap = StrokeLineCap.SQUARE + graphicsContext1.strokeLine( + action.startX, + action.startY, + action.endX, + action.endY + ) + } + is LineWidthChange -> { + sizeSlider.value = action.width + sizeLabel.text = action.width.toInt().toString() + graphicsContext1.lineWidth = action.width + graphicsContext2.lineWidth = action.width + } + } + graphicsContext1.lineCap = StrokeLineCap.ROUND + } } } diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Line.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Line.kt index a5583b8..0e33207 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Line.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/Line.kt @@ -1,3 +1,3 @@ package by.busskov.paint.userActions -open class Line(val startX: Double, startY: Double, endX: Double, endY: Double) : UserAction() \ No newline at end of file +open class Line(val startX: Double, val startY: Double, val endX: Double, val endY: Double) : UserAction() \ No newline at end of file diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/LineWidthChange.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/LineWidthChange.kt new file mode 100644 index 0000000..a5b3c3d --- /dev/null +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/userActions/LineWidthChange.kt @@ -0,0 +1,3 @@ +package by.busskov.paint.userActions + +class LineWidthChange(val width: Double) : UserAction() \ No newline at end of file From 993a2f6a24c90e5298ef36e27a022e6972c7916f Mon Sep 17 00:00:00 2001 From: Busskov Date: Sun, 24 Dec 2023 23:50:40 +0300 Subject: [PATCH 09/10] Fix double project save bug --- .../Paint/src/main/kotlin/by/busskov/paint/Main.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt index fe11791..d8d940d 100644 --- a/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt +++ b/lab-03/Paint/src/main/kotlin/by/busskov/paint/Main.kt @@ -2,7 +2,6 @@ package by.busskov.paint import by.busskov.paint.userActions.* import javafx.application.Application -import javafx.beans.value.ObservableValue import javafx.embed.swing.SwingFXUtils import javafx.scene.Scene import javafx.scene.canvas.Canvas @@ -29,7 +28,6 @@ import java.util.LinkedList import javax.imageio.ImageIO import kotlin.math.min import kotlin.math.abs -import kotlin.math.max class PaintApp : Application(), Serializable { @Transient private val canvas1: Canvas = Canvas(800.0, 600.0) @@ -247,6 +245,12 @@ class PaintApp : Application(), Serializable { userActions.add(LineWidthChange(graphicsContext1.lineWidth)) sizeLabel.text = graphicsContext1.lineWidth.toInt().toString(); } + sizeSlider.setOnMouseClicked { + graphicsContext1.lineWidth = round(sizeSlider.value) + graphicsContext2.lineWidth = graphicsContext1.lineWidth + userActions.add(LineWidthChange(graphicsContext1.lineWidth)) + sizeLabel.text = graphicsContext1.lineWidth.toInt().toString(); + } toolBar.items.addAll( curvedLine, @@ -357,7 +361,7 @@ class PaintApp : Application(), Serializable { graphicsContext2.lineWidth = 2.0 graphicsContext1.lineWidth = 2.0 - this.userActions = deserialized.userActions + this.userActions = LinkedList(deserialized.userActions) projectOpen() } } @@ -365,8 +369,8 @@ class PaintApp : Application(), Serializable { } private fun projectOpen() { - while(!userActions.isEmpty()) { - when(val action = userActions.pollFirst()) { + for (action in userActions) { + when(action) { is ColorChange -> { fillColor = Color(action.r, action.g, action.b, 1.0) colorPicker.value = fillColor From 659e9803f015836f58a1fccfdcd0eac4f0b32760 Mon Sep 17 00:00:00 2001 From: Busskov Date: Mon, 25 Dec 2023 00:00:50 +0300 Subject: [PATCH 10/10] Add project setup instructions --- lab-03/Project setup.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 lab-03/Project setup.txt diff --git a/lab-03/Project setup.txt b/lab-03/Project setup.txt new file mode 100644 index 0000000..a07a502 --- /dev/null +++ b/lab-03/Project setup.txt @@ -0,0 +1,2 @@ +https://gluonhq.com/products/javafx/ Скачал здесь архив и распаковал +в IDEA File->Project Structure->Libraries добавить папку lib из скачанного архива