Skip to content

Commit

Permalink
Merge branch 'main' into fix/text_edit
Browse files Browse the repository at this point in the history
  • Loading branch information
PingHsunTsai committed Sep 5, 2024
2 parents 338182b + 4fa29be commit cf544f0
Show file tree
Hide file tree
Showing 57 changed files with 147 additions and 50 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

### Changed

### Removed


## [1.3.0] 2024-08-16

### Added

* Added `TextEdit` to handle `name` change.
* Added `DefaultLayout` to handle gerneral `layout` setting to minimal.
* Added `ColorDialog` to manage color dialog.
* Added `SettingLayout` to manage complex layout with config input.
* Added `robot.py` example.
* Added `ortho` option to view.
* Added `vertical_align` and `horizontal_align` options to `Tag`.
* Added example files in the documentation.

### Changed

* Fixed `GroupObject` `pointcolor` not found error with impliment `exclude_type_list`.
* Fixed `Tag` inconsistent height issue.
* Dynamically adjust camera pan delta based on distacne.

* Updated alignright feature to `TextEdit`

### Removed
Expand Down
5 changes: 0 additions & 5 deletions docs/examples/__temp/other/other.rst

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions docs/examples/__temp/other.rst → docs/examples/object.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
********************************************************************************
Other Examples
Object Examples
********************************************************************************

.. toctree::
:maxdepth: 1
:glob:

other/**
object/**
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ doctest_optionflags = [
# ============================================================================

[tool.bumpversion]
current_version = "1.2.4"
current_version = "1.3.0"
message = "Bump version to {new_version}"
commit = true
tag = true
Expand Down
21 changes: 21 additions & 0 deletions scripts/arrows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

import compas
from compas.colors import Color
from compas.geometry import Vector
from compas_viewer.viewer import Viewer

viewer = Viewer()

N = 10
M = 10

for i in range(N):
for j in range(M):
viewer.scene.add(
Vector(0, 0, (i + j + 1) / 5),
anchor = [i, j, 0],
linecolor=Color(i / N, j / M, 0.0),
name=f"Arrow_{i}_{j}",
)

viewer.show()
14 changes: 6 additions & 8 deletions scripts/tag.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@

from compas.geometry import Box
from compas_viewer import Viewer
from compas_viewer.scene import Tag

box1 = Box.from_width_height_depth(5, 1, 1)
box2 = Box.from_width_height_depth(1, 5, 1)
t = Tag("EN", (5, 1, 1), height=50)
viewer = Viewer()
t1 = Tag("Align to left", (0, 0, 0), height=50) # default align is left and bottom
t2 = Tag("Align to center", (0, 5, 0), height=50, horizontal_align="center", vertical_align="center")
t3 = Tag("Align to right", (0, 10, 0), height=50, horizontal_align="right", vertical_align="top")
t4 = Tag("Absolute height", (5, 0, 0), absolute_height=True, height=100)

# Simple list of objects
group1 = viewer.scene.add([box1, box2, t])
viewer = Viewer()
viewer.scene.add([t1, t2, t3, t4])
viewer.show()
2 changes: 1 addition & 1 deletion src/compas_viewer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
__copyright__ = "COMPAS Association"
__license__ = "MIT License"
__email__ = "[email protected]"
__version__ = "1.2.4"
__version__ = "1.3.0"


HERE = os.path.dirname(__file__)
Expand Down
2 changes: 1 addition & 1 deletion src/compas_viewer/components/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def __init__(
elif isinstance(default_color, ColorDict):
default_color = default_color.default
else:
raise ValueError("Invalid color type.")
raise ValueError("Invalid color type. : {}".format(type(default_color)))
default_color = QColor(*remap_rgb(default_color, to_range_one=False))

self.color_button = QPushButton(self)
Expand Down
13 changes: 12 additions & 1 deletion src/compas_viewer/components/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ def __init__(
self.items = items
self.type = type

@property
def exclude_type_list(self) -> tuple[type, ...]:
from compas_viewer.scene import GroupObject
from compas_viewer.scene import TagObject

return (GroupObject, TagObject)

def generate_layout(self) -> None:
self.layout = QVBoxLayout()
self.widgets = {}
Expand All @@ -106,7 +113,7 @@ def generate_layout(self) -> None:
if obj.is_selected:
obj_list.append(obj)

if not obj_list:
if not obj_list or isinstance(obj_list[0], self.exclude_type_list):
return
# Only support one item selected per time
self.set_layout(self.items, obj_list[0])
Expand All @@ -131,6 +138,10 @@ def set_layout(self, items: list[dict], obj: Any) -> None:
min_val: float = sub_item.get("min_val", None)
max_val: float = sub_item.get("max_val", None)

if attr and getattr(obj, attr, None) is None:
# TODO: @Tsai, this needs to be handled at upper level.
continue

if type == "double_edit":
value = action(obj)
widget = DoubleEdit(title=sub_title, value=value, min_val=min_val, max_val=max_val)
Expand Down
1 change: 1 addition & 0 deletions src/compas_viewer/components/sceneform.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def update(self):
widget.setSelected(node.is_selected)
if node.is_selected:
self.expand(node.parent)
self.scrollToItem(widget)

else:
self._sceneobjects = list(self.scene.objects)
Expand Down
1 change: 1 addition & 0 deletions src/compas_viewer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class MenubarConfig(ConfigBase):
{"title": "Top", "action": change_view_cmd, "kwargs": {"mode": "top"}},
{"title": "Front", "action": change_view_cmd, "kwargs": {"mode": "front"}},
{"title": "Right", "action": change_view_cmd, "kwargs": {"mode": "right"}},
{"title": "Ortho", "action": change_view_cmd, "kwargs": {"mode": "ortho"}},
],
},
{"type": "separator"},
Expand Down
7 changes: 5 additions & 2 deletions src/compas_viewer/renderer/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ def reset_position(self, view: Optional[str] = None):
self.rotation.set(pi / 2, 0, 0, False)
if view == "right":
self.rotation.set(pi / 2, 0, pi / 2, False)
if view == "ortho":
self.rotation.set(pi / 4, 0, -pi / 4, False)

def rotate(self, dx: float, dy: float):
"""Rotate the camera based on current mouse movement.
Expand All @@ -338,7 +340,7 @@ def rotate(self, dx: float, dy: float):
is a perspective view (``camera.renderer.config.view == "perspective"``).
"""
if self.renderer.view == "perspective":
if self.renderer.view == "perspective" or self.renderer.view == "ortho":
self.rotation += [-self.rotationdelta * dy, 0, -self.rotationdelta * dx]

def pan(self, dx: float, dy: float):
Expand All @@ -354,7 +356,8 @@ def pan(self, dx: float, dy: float):
with each increment the size of :attr:`Camera.pan_delta`.
"""
R = Rotation.from_euler_angles(self.rotation)
T = Translation.from_vector([-dx * self.pandelta * self.scale, dy * self.pandelta * self.scale, 0])
scaled_pandelta = self.pandelta * self.distance / 10
T = Translation.from_vector([-dx * scaled_pandelta, dy * scaled_pandelta, 0])
M = (R * T).matrix
vector = [M[i][3] for i in range(3)]
self.target += vector
Expand Down
2 changes: 1 addition & 1 deletion src/compas_viewer/renderer/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ def paint(self):
self.shader_tag.bind()
self.shader_tag.uniform4x4("viewworld", viewworld)
for obj in tag_objs:
obj.draw(self.shader_tag, self.camera.position)
obj.draw(self.shader_tag, self.camera.position, self.width(), self.height())
self.shader_tag.release()

# draw 2D box for multi-selection
Expand Down
9 changes: 2 additions & 7 deletions src/compas_viewer/renderer/shaders/shader.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,16 +204,11 @@ def draw_texts(self, elements: Any = None, n: int = 0):
n : int, optional
The number of elements.
"""
GL.glDisable(GL.GL_POINT_SMOOTH)
GL.glEnable(GL.GL_POINT_SPRITE)
GL.glEnable(GL.GL_PROGRAM_POINT_SIZE)
if elements:
GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, elements)
GL.glDrawElements(GL.GL_POINTS, n, GL.GL_UNSIGNED_INT, None)
GL.glDrawElements(GL.GL_TRIANGLE_STRIP, n, GL.GL_UNSIGNED_INT, None)
else:
GL.glDrawArrays(GL.GL_POINTS, 0, GL.GL_BUFFER_SIZE)
GL.glDisable(GL.GL_POINT_SPRITE)
GL.glEnable(GL.GL_POINT_SMOOTH)
GL.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, GL.GL_BUFFER_SIZE)

def draw_arrows(self, elements: Any, n: int, width: float, background: bool = False):
"""
Expand Down
12 changes: 5 additions & 7 deletions src/compas_viewer/renderer/shaders/tag.frag
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ uniform sampler2D tex;
uniform int text_num;
uniform vec3 text_color;

varying vec2 texcoord;

void main()
{
vec2 xy = gl_PointCoord;
xy.y -= 0.5;
xy.y *= text_num;
if (xy.y > 0 || xy.y < -1) {
discard;
}
{
vec2 xy = texcoord;
xy.y = 1.0 - xy.y;
float a = texture2D(tex, xy).r;
gl_FragColor = vec4(text_color, a);
if (a <= 0){
Expand Down
41 changes: 36 additions & 5 deletions src/compas_viewer/renderer/shaders/tag.vert
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,43 @@ attribute vec3 position;
uniform mat4 projection;
uniform mat4 viewworld;
uniform mat4 transform;
uniform float screen_aspect;
uniform float screen_height;
uniform float text_aspect;
uniform float text_height;
uniform vec3 text_position;
uniform int vertical_align;
uniform int horizontal_align;

uniform int text_height;
uniform int text_num;

varying vec2 texcoord;

void main()
{
gl_PointSize = text_height * text_num;
gl_Position = projection * viewworld * transform * vec4(position, 1.0);
{

texcoord = vec2(position.x, position.y);

vec2 position = vec2(position.x, position.y);

if (horizontal_align == 0) {
position.x -= 0.5;
}
else if (horizontal_align == 1) {
position.x -= 1.0;
}

if (vertical_align == 0) {
position.y -= 0.5;
}
else if (vertical_align == 1) {
position.y -= 1.0;
}


vec4 screen_position = projection * viewworld * vec4(text_position, 1.0);
vec2 adjustedPos = vec2(position.x / screen_aspect * text_aspect, position.y) * text_height / screen_height;
vec4 offset = vec4(adjustedPos * screen_position.w, 0.0, 0.0);
screen_position += offset;
gl_Position = screen_position;

}
45 changes: 36 additions & 9 deletions src/compas_viewer/scene/tagobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ def __init__(
height: float = 50,
absolute_height: bool = False,
font: Optional[PathLike] = None,
vertical_align: str = "bottom",
horizontal_align: str = "left",
):
super().__init__()
self.text = text
Expand All @@ -88,6 +90,8 @@ def __init__(
self.height = height
self.absolute_height = absolute_height
self.font = font or FONT
self.vertical_align = vertical_align
self.horizontal_align = horizontal_align

def transform(self, transformation):
"""Transform the tag.
Expand All @@ -109,12 +113,30 @@ class TagObject(ViewerSceneObject, GeometryObject):

geometry: Tag

VERTICAL_ALIGN = {"top": 1, "center": 0, "bottom": -1}
HORIZONTAL_ALIGN = {"left": -1, "center": 0, "right": 1}

def make_buffers(self):
positions = [
0,
0,
0,
1,
0,
0,
0,
1,
0,
1,
1,
0,
]

self._text_buffer = {
"positions": make_vertex_buffer(self.geometry.position),
"elements": make_index_buffer([0]),
"positions": make_vertex_buffer(positions),
"elements": make_index_buffer([0, 1, 2, 3]),
"text_texture": self.make_text_texture(),
"n": 1,
"n": 4,
}

def make_text_texture(self):
Expand Down Expand Up @@ -142,8 +164,7 @@ def make_text_texture(self):

# The width and height of the texture must be a multiple of 4
total_width = (total_width + 3) // 4 * 4
# The height is doubled because the text is rendered in the middle of the texture
max_height = (max_height * 2 + 3) // 4 * 4
max_height = (max_height + 10 + 3) // 4 * 4

string_buffer = zeros(shape=(max_height, total_width), dtype="uint8")

Expand Down Expand Up @@ -186,6 +207,8 @@ def make_text_texture(self):
GL.GL_UNSIGNED_BYTE,
string_buffer,
)

self.text_aspect = total_width / 50
return texture

def _calculate_text_height(self, camera_position):
Expand All @@ -195,19 +218,23 @@ def _calculate_text_height(self, camera_position):
else:
return self.geometry.height

def draw(self, shader, camera_position):
def draw(self, shader, camera_position, width, height):
"""Draw the object from its buffers"""
shader.enable_attribute("position")
if self.worldtransformation is not None:
shader.uniform4x4("transform", self.worldtransformation.matrix)
shader.uniform1f("object_opacity", self.opacity)
shader.uniform1i("text_height", self._calculate_text_height(camera_position))
shader.uniform1i("text_num", len(self.geometry.text))
shader.uniform1f("screen_aspect", width / height)
shader.uniform1f("screen_height", height)
shader.uniform1f("text_aspect", self.text_aspect)
shader.uniform1f("text_height", self._calculate_text_height(camera_position))
shader.uniform3f("text_position", self.geometry.position)
shader.uniform3f("text_color", self.geometry.color)
shader.uniform1i("vertical_align", self.VERTICAL_ALIGN[self.geometry.vertical_align])
shader.uniform1i("horizontal_align", self.HORIZONTAL_ALIGN[self.geometry.horizontal_align])
shader.uniformText("text_texture", self._text_buffer["text_texture"])
shader.bind_attribute("position", self._text_buffer["positions"])
shader.draw_texts(elements=self._text_buffer["elements"], n=self._text_buffer["n"])
shader.uniform1i("is_text", 0)
shader.uniform1f("object_opacity", 1)
shader.disable_attribute("position")

Expand Down

0 comments on commit cf544f0

Please sign in to comment.