Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion graphic/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,31 @@ def mouseMoveEvent(self, event: 'QGraphicsSceneMouseEvent'):
current_pos = self.mapToParent(event.pos())
rect = self.parent_item.rect()

if self.direction == "horizontal":
if self.direction is None: # Corner scaling
if self.opposite_point is not None:
dx = current_pos.x() - self.opposite_point.x()
dy = current_pos.y() - self.opposite_point.y()

# Maintain aspect ratio
if abs(dx) > abs(dy):
new_width = abs(dx)
new_height = new_width * (rect.height() / rect.width())
else:
new_height = abs(dy)
new_width = new_height * (rect.width() / rect.height())

if current_pos.x() < self.opposite_point.x():
new_x = self.opposite_point.x() - new_width
else:
new_x = self.opposite_point.x()

if current_pos.y() < self.opposite_point.y():
new_y = self.opposite_point.y() - new_height
else:
new_y = self.opposite_point.y()

self.parent_item.setRect(new_x, new_y, new_width, new_height)
elif self.direction == "horizontal":
# Get the opposite horizontal point as fixed point
if current_pos.x() > rect.center().x():
fixed_x = rect.left() # If dragging right point, left is fixed
Expand Down
86 changes: 83 additions & 3 deletions graphic/items.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from PyQt6.QtGui import QLinearGradient, QColor, QBrush, QPen
from PyQt6.QtWidgets import QGraphicsEllipseItem, QGraphicsRectItem
from PyQt6.QtCore import QRectF
from PyQt6.QtGui import QLinearGradient, QColor, QBrush, QPen, QPainterPath
from PyQt6.QtWidgets import QGraphicsEllipseItem, QGraphicsRectItem, QGraphicsPathItem

from graphic.base import SceneItem, ZoomableView

Expand Down Expand Up @@ -57,4 +58,83 @@ def __init__(self, x: float, y: float, width: float, height: float, view: Zoomab
gradient.setColorAt(1.0, QColor(0, 255, 255)) # Cyan

self.setBrush(QBrush(gradient))
self.setPen(QPen(QColor("white")))
self.setPen(QPen(QColor("white")))


class CustomShapeItem(QGraphicsPathItem, SceneItem):
"""Custom shape item: Rectangle with half-width cut by an ellipse."""

def __init__(self, x: float, y: float, width: float, height: float, view: ZoomableView):
"""
Initialize the CustomShapeItem.

:param x: float - X-coordinate of the item.
:param y: float - Y-coordinate of the item.
:param width: float - Width of the item.
:param height: float - Height of the item.
:param view: ZoomableView - The ZoomableView that contains this item.
"""
# Initialize parent classes
QGraphicsPathItem.__init__(self)
SceneItem.__init__(self, x, y, width, height, view)
self.setPos(x, y)

# Store initial dimensions
self.original_width = width
self.original_height = height

# Initialize dimensions
self.width = width
self.height = height

# Create the custom shape
self.update_path(width, height)

# Set appearance (blue gradient)
self.setBrush(QBrush(QColor(0, 0, 255))) # Solid blue color
self.setPen(QPen(QColor("white"))) # White outline for visibility

def update_path(self, width: float, height: float):
"""
Update the path based on the current width and height.

:param width: float - Width of the item.
:param height: float - Height of the item.
"""
rect_path = QPainterPath()
rect_path.addRect(0, 0, width, height) # Rectangle path

ellipse_path = QPainterPath()
ellipse_path.addEllipse(-width / 2, 0, width, height) # Ellipse path

# Subtract the ellipse from the rectangle
final_path = rect_path.subtracted(ellipse_path)

self.setPath(final_path)

def setRect(self, x: float, y: float, width: float, height: float):
"""
Override setRect to update custom shape dimensions.

:param x: float - X-coordinate of the item.
:param y: float - Y-coordinate of the item.
:param width: float - Width of the item.
:param height: float - Height of the item.
"""
self.prepareGeometryChange()

# Update position and dimensions
self.setPos(x, y)
self.update_path(width, height)

# Update internal size attributes for compatibility with SceneItem
self.width = width
self.height = height

def rect(self) -> QRectF:
"""
Return the bounding rectangle of the custom shape.

:return: QRectF - The bounding rectangle of the custom shape.
"""
return QRectF(0, 0, self.width, self.height)
12 changes: 6 additions & 6 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from PyQt6.QtWidgets import QApplication, QGraphicsScene
import sys

from graphic.base import ZoomableView, SceneItem
from graphic.base import ZoomableView
from graphic.config import SCENE_SIZE
from graphic.items import EllipseItem, RectangleItem
from graphic.items import EllipseItem, RectangleItem, CustomShapeItem


def main():
Expand All @@ -18,13 +18,13 @@ def main():
view.setWindowTitle("Light Simulator")

ellipse1 = EllipseItem(-100, -50, 200, 100, view)
ellipse2 = EllipseItem(200, 200, 150, 75, view)
scene.addItem(ellipse1)
scene.addItem(ellipse2)

rectangle = RectangleItem(50, 50, 150, 100, view)
scene.addItem(ellipse1)
scene.addItem(rectangle)


cs = CustomShapeItem(-100, -100, 100, 200, view)
scene.addItem(cs)
view.resize(800, 600)
view.show()

Expand Down