diff --git a/src/main/java/dev/bytevibe/hyperpoint/DrawableObject.java b/src/main/java/dev/bytevibe/hyperpoint/DrawableObject.java index dc97350..16ae139 100644 --- a/src/main/java/dev/bytevibe/hyperpoint/DrawableObject.java +++ b/src/main/java/dev/bytevibe/hyperpoint/DrawableObject.java @@ -12,6 +12,7 @@ public abstract class DrawableObject implements Serializable { protected double height; protected String id; protected boolean selected; + protected double rotation = 0; public DrawableObject(double x, double y, double width, double height) { this.x = x; @@ -70,6 +71,14 @@ public abstract class DrawableObject implements Serializable { this.selected = selected; } + public double getRotation() { + return rotation; + } + + public void setRotation(double rotation) { + this.rotation = (rotation % 360 + 360) % 360; + } + /** * 检查点是否在对象内 */ diff --git a/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java b/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java index d58c311..7ee6766 100644 --- a/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java +++ b/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java @@ -274,14 +274,30 @@ public class DrawingCanvas extends Pane { * 绘制单个对象 */ private void drawObject(GraphicsContext gc, DrawableObject obj, boolean selected) { - if (obj instanceof TextObject) { - drawTextObject(gc, (TextObject) obj, selected); - } else if (obj instanceof ShapeObject) { - drawShapeObject(gc, (ShapeObject) obj, selected); - } else if (obj instanceof ImageObject) { - drawImageObject(gc, (ImageObject) obj, selected); - } + // 保存当前状态 + gc.save(); + + // 计算旋转中心(对象中心) + double centerX = obj.getX() + obj.getWidth() / 2; + double centerY = obj.getY() + obj.getHeight() / 2; + + // 应用旋转 + gc.translate(centerX, centerY); + gc.rotate(obj.getRotation()); + gc.translate(-centerX, -centerY); + + // 绘制具体对象 + if (obj instanceof TextObject) { + drawTextObject(gc, (TextObject) obj, selected); + } else if (obj instanceof ShapeObject) { + drawShapeObject(gc, (ShapeObject) obj, selected); + } else if (obj instanceof ImageObject) { + drawImageObject(gc, (ImageObject) obj, selected); } + + // 恢复状态 + gc.restore(); +} /** * 绘制文本对象 diff --git a/src/main/java/dev/bytevibe/hyperpoint/JsonSerializationUtil.java b/src/main/java/dev/bytevibe/hyperpoint/JsonSerializationUtil.java index 2b572cc..8a5851a 100644 --- a/src/main/java/dev/bytevibe/hyperpoint/JsonSerializationUtil.java +++ b/src/main/java/dev/bytevibe/hyperpoint/JsonSerializationUtil.java @@ -1,10 +1,16 @@ package dev.bytevibe.hyperpoint; -import com.google.gson.*; -import javafx.scene.paint.Color; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; -import java.io.*; -import java.util.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; /** * JSON序列化工具,用于将演示文稿保存和加载为JSON格式 @@ -180,6 +186,7 @@ public class JsonSerializationUtil { ImageObject imgObj = (ImageObject) obj; objJson.addProperty("imagePath", imgObj.getImagePath()); } + objJson.addProperty("rotation", obj.getRotation()); // 添加旋转角度 return objJson; } @@ -238,6 +245,11 @@ public class JsonSerializationUtil { obj = new ImageObject(x, y, width, height, imagePath); } + // 读取旋转角度并设置 + if (objJson.has("rotation")) { + obj.setRotation(objJson.get("rotation").getAsDouble()); + } + if (obj != null) { obj.setId(id); } diff --git a/src/main/java/dev/bytevibe/hyperpoint/PresentationWindow.java b/src/main/java/dev/bytevibe/hyperpoint/PresentationWindow.java index 2652428..90d47eb 100644 --- a/src/main/java/dev/bytevibe/hyperpoint/PresentationWindow.java +++ b/src/main/java/dev/bytevibe/hyperpoint/PresentationWindow.java @@ -584,6 +584,15 @@ public class PresentationWindow { * 绘制单个对象 */ private void drawObject(GraphicsContext gc, DrawableObject obj) { + gc.save(); + + double centerX = obj.getX() + obj.getWidth() / 2; + double centerY = obj.getY() + obj.getHeight() / 2; + + gc.translate(centerX, centerY); + gc.rotate(obj.getRotation()); + gc.translate(-centerX, -centerY); + if (obj instanceof TextObject) { drawTextObject(gc, (TextObject) obj); } else if (obj instanceof ShapeObject) { @@ -591,6 +600,8 @@ public class PresentationWindow { } else if (obj instanceof ImageObject) { drawImageObject(gc, (ImageObject) obj); } + + gc.restore(); } /** diff --git a/src/main/java/dev/bytevibe/hyperpoint/PropertyPanel.java b/src/main/java/dev/bytevibe/hyperpoint/PropertyPanel.java index a7054a7..faf818d 100644 --- a/src/main/java/dev/bytevibe/hyperpoint/PropertyPanel.java +++ b/src/main/java/dev/bytevibe/hyperpoint/PropertyPanel.java @@ -1,16 +1,22 @@ package dev.bytevibe.hyperpoint; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.control.*; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; - import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.ColorPicker; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.Slider; +import javafx.scene.control.Spinner; +import javafx.scene.control.TextField; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; + /** * 属性编辑面板,用于编辑选中对象的属性 * 使用FXML加载UI布局 @@ -38,6 +44,10 @@ public class PropertyPanel extends VBox implements Initializable { private Spinner strokeWidthSpinner; @FXML private Button deleteButton; + @FXML + private Slider rotationSlider; + @FXML + private Label rotationValueLabel; private DrawingCanvas canvas; @@ -80,6 +90,20 @@ public class PropertyPanel extends VBox implements Initializable { "BOLD_ITALIC" ); + // 初始化旋转控制 + rotationSlider.setMin(0); + rotationSlider.setMax(360); + rotationSlider.setValue(0); + rotationSlider.setMajorTickUnit(15); + rotationSlider.setMajorTickUnit(90); + rotationSlider.setShowTickMarks(true); + rotationSlider.setShowTickLabels(true); + + rotationSlider.valueProperty().addListener((obs, oldVal, newVal) -> { + updateRotation(); + rotationValueLabel.setText(String.format("%.0f°", newVal)); + }); + // 设置默认值 fontFamilyCombo.setValue("Arial"); fontStyleCombo.setValue("NORMAL"); @@ -112,6 +136,7 @@ public class PropertyPanel extends VBox implements Initializable { DrawableObject selected = canvas.getSelectedObject(); if (selected == null) { setEditingVisible(false); + rotationSlider.setDisable(true); objectTypeLabel.setText("未选中对象"); return; } @@ -142,15 +167,31 @@ public class PropertyPanel extends VBox implements Initializable { showImageControls(); } + rotationSlider.setValue(selected.getRotation()); + rotationSlider.setDisable(false); + rotationValueLabel.setText(String.format("%.0f°", selected.getRotation())); + // 重新添加监听 textContentField.setOnKeyReleased(e -> updateTextContent()); } + /** + * 更新旋转 + */ + private void updateRotation() { + DrawableObject selected = canvas.getSelectedObject(); + if (selected != null) { + selected.setRotation(rotationSlider.getValue()); + canvas.redraw(); + } + } + /** * 显示文本控件 */ private void showTextControls() { textContentField.setDisable(false); + rotationSlider.setDisable(false); fontFamilyCombo.setDisable(false); fontSizeSpinner.setDisable(false); fontStyleCombo.setDisable(false); @@ -167,6 +208,7 @@ public class PropertyPanel extends VBox implements Initializable { */ private void showShapeControls() { textContentField.setDisable(true); + rotationSlider.setDisable(false); fontFamilyCombo.setDisable(true); fontSizeSpinner.setDisable(true); fontStyleCombo.setDisable(true); @@ -183,6 +225,7 @@ public class PropertyPanel extends VBox implements Initializable { */ private void showImageControls() { textContentField.setDisable(true); + rotationSlider.setDisable(false); fontFamilyCombo.setDisable(true); fontSizeSpinner.setDisable(true); fontStyleCombo.setDisable(true); diff --git a/src/main/resources/dev/bytevibe/hyperpoint/propertyPanel.fxml b/src/main/resources/dev/bytevibe/hyperpoint/propertyPanel.fxml index 9766240..22d32e6 100644 --- a/src/main/resources/dev/bytevibe/hyperpoint/propertyPanel.fxml +++ b/src/main/resources/dev/bytevibe/hyperpoint/propertyPanel.fxml @@ -1,5 +1,6 @@ + @@ -23,6 +24,11 @@ + + +