diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/AddTldrawShapeWhiteboardRecordEvent.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/AddTldrawShapeWhiteboardRecordEvent.scala
new file mode 100755
index 0000000000..cdba60459d
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/AddTldrawShapeWhiteboardRecordEvent.scala
@@ -0,0 +1,65 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ *
+ * Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
+ *
+ * This program is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation; either version 3.0 of the License, or (at your option) any later
+ * version.
+ *
+ * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along
+ * with BigBlueButton; if not, see .
+ *
+ */
+
+package org.bigbluebutton.core.record.events
+
+import org.bigbluebutton.common2.domain.SimpleVoteOutVO
+import scala.collection.immutable.List
+import scala.collection.Map
+import scala.collection.mutable.ArrayBuffer
+import spray.json._
+import DefaultJsonProtocol._
+
+class AddTldrawShapeWhiteboardRecordEvent extends AbstractWhiteboardRecordEvent {
+ import AddTldrawShapeWhiteboardRecordEvent._
+
+ implicit object AnyJsonFormat extends JsonFormat[Any] {
+ def write(x: Any) = x match {
+ case n: Int => JsNumber(n)
+ case s: String => JsString(s)
+ case d: Double => JsNumber(d)
+ case m: scala.collection.immutable.Map[String, _] => mapFormat[String, Any].write(m)
+ case l: List[_] => listFormat[Any].write(l)
+ case b: Boolean if b == true => JsTrue
+ case b: Boolean if b == false => JsFalse
+ }
+
+ def read(value: JsValue) = {}
+ }
+
+ setEvent("AddTldrawShapeEvent")
+
+ def setUserId(id: String) {
+ eventMap.put(USER_ID, id)
+ }
+
+ def setAnnotationId(id: String) {
+ eventMap.put(SHAPE_ID, id)
+ }
+
+ def addAnnotation(annotation: scala.collection.immutable.Map[String, Any]) {
+ eventMap.put(SHAPE_DATA, annotation.toJson.compactPrint)
+ }
+}
+
+object AddTldrawShapeWhiteboardRecordEvent {
+ protected final val USER_ID = "userId"
+ protected final val SHAPE_ID = "shapeId"
+ protected final val SHAPE_DATA = "shapeData"
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/DeleteTldrawShapeRecordEvent.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/DeleteTldrawShapeRecordEvent.scala
new file mode 100755
index 0000000000..4322e03895
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/DeleteTldrawShapeRecordEvent.scala
@@ -0,0 +1,39 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ *
+ * Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
+ *
+ * This program is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation; either version 3.0 of the License, or (at your option) any later
+ * version.
+ *
+ * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along
+ * with BigBlueButton; if not, see .
+ *
+ */
+
+package org.bigbluebutton.core.record.events
+
+class DeleteTldrawShapeRecordEvent extends AbstractWhiteboardRecordEvent {
+ import DeleteTldrawShapeRecordEvent._
+
+ setEvent("DeleteTldrawShapeEvent")
+
+ def setUserId(userId: String) {
+ eventMap.put(USER_ID, userId)
+ }
+
+ def setShapeId(shapeId: String) {
+ eventMap.put(SHAPE_ID, shapeId)
+ }
+}
+
+object DeleteTldrawShapeRecordEvent {
+ protected final val USER_ID = "userId"
+ protected final val SHAPE_ID = "shapeId"
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/ResizeAndMoveSlideRecordEvent.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/ResizeAndMoveSlideRecordEvent.scala
index 1797a89ada..0905fd5d8a 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/ResizeAndMoveSlideRecordEvent.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/ResizeAndMoveSlideRecordEvent.scala
@@ -32,23 +32,28 @@ class ResizeAndMoveSlideRecordEvent extends AbstractPresentationRecordEvent {
eventMap.put(ID, id)
}
- def setXCamera(xCamera: Double) {
- eventMap.put(X_CAMERA, xCamera.toString)
+ def setXOffset(offset: Double) {
+ eventMap.put(X_OFFSET, offset.toString)
}
- def setYCamera(yCamera: Double) {
- eventMap.put(Y_CAMERA, yCamera.toString)
+ def setYOffset(offset: Double) {
+ eventMap.put(Y_OFFSET, offset.toString)
}
- def setZoom(zoom: Double) {
- eventMap.put(ZOOM, zoom.toString)
+ def setWidthRatio(ratio: Double) {
+ eventMap.put(WIDTH_RATIO, ratio.toString)
+ }
+
+ def setHeightRatio(ratio: Double) {
+ eventMap.put(HEIGHT_RATIO, ratio.toString)
}
}
object ResizeAndMoveSlideRecordEvent {
protected final val PRES_NAME = "presentationName"
protected final val ID = "id"
- protected final val X_CAMERA = "xCamera"
- protected final val Y_CAMERA = "yCamera"
- protected final val ZOOM = "zoom"
+ protected final val X_OFFSET = "xOffset"
+ protected final val Y_OFFSET = "yOffset"
+ protected final val WIDTH_RATIO = "widthRatio"
+ protected final val HEIGHT_RATIO = "heightRatio"
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/TldrawCameraChangedRecordEvent.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/TldrawCameraChangedRecordEvent.scala
new file mode 100755
index 0000000000..f5ea3deba8
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/TldrawCameraChangedRecordEvent.scala
@@ -0,0 +1,54 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ *
+ * Copyright (c) 2017 BigBlueButton Inc. and by respective authors (see below).
+ *
+ * This program is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation; either version 3.0 of the License, or (at your option) any later
+ * version.
+ *
+ * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along
+ * with BigBlueButton; if not, see .
+ *
+ */
+
+package org.bigbluebutton.core.record.events
+
+class TldrawCameraChangedRecordEvent extends AbstractPresentationRecordEvent {
+ import TldrawCameraChangedRecordEvent._
+
+ setEvent("TldrawCameraChangedEvent")
+
+ def setPresentationName(name: String) {
+ eventMap.put(PRES_NAME, name)
+ }
+
+ def setId(id: String) {
+ eventMap.put(ID, id)
+ }
+
+ def setXCamera(xCamera: Double) {
+ eventMap.put(X_CAMERA, xCamera.toString)
+ }
+
+ def setYCamera(yCamera: Double) {
+ eventMap.put(Y_CAMERA, yCamera.toString)
+ }
+
+ def setZoom(zoom: Double) {
+ eventMap.put(ZOOM, zoom.toString)
+ }
+}
+
+object TldrawCameraChangedRecordEvent {
+ protected final val PRES_NAME = "presentationName"
+ protected final val ID = "id"
+ protected final val X_CAMERA = "xCamera"
+ protected final val Y_CAMERA = "yCamera"
+ protected final val ZOOM = "zoom"
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/endpoint/redis/RedisRecorderActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/endpoint/redis/RedisRecorderActor.scala
index 0954bed385..04097fc8ab 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/endpoint/redis/RedisRecorderActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/endpoint/redis/RedisRecorderActor.scala
@@ -184,7 +184,7 @@ class RedisRecorderActor(
}
private def handleResizeAndMovePageEvtMsg(msg: ResizeAndMovePageEvtMsg) {
- val ev = new ResizeAndMoveSlideRecordEvent()
+ val ev = new TldrawCameraChangedRecordEvent()
ev.setMeetingId(msg.header.meetingId)
ev.setPodId(msg.body.podId)
ev.setPresentationName(msg.body.presentationId)
@@ -280,7 +280,7 @@ class RedisRecorderActor(
private def handleSendWhiteboardAnnotationsEvtMsg(msg: SendWhiteboardAnnotationsEvtMsg) {
msg.body.annotations.foreach(annotation => {
- val ev = new AddShapeWhiteboardRecordEvent()
+ val ev = new AddTldrawShapeWhiteboardRecordEvent()
ev.setMeetingId(msg.header.meetingId)
ev.setPresentation(getPresentationId(annotation.wbId))
ev.setPageNumber(getPageNum(annotation.wbId))
@@ -320,7 +320,7 @@ class RedisRecorderActor(
private def handleDeleteWhiteboardAnnotationsEvtMsg(msg: DeleteWhiteboardAnnotationsEvtMsg) {
msg.body.annotationsIds.foreach(annotationId => {
- val ev = new UndoAnnotationRecordEvent()
+ val ev = new DeleteTldrawShapeRecordEvent()
ev.setMeetingId(msg.header.meetingId)
ev.setPresentation(getPresentationId(msg.body.whiteboardId))
ev.setPageNumber(getPageNum(msg.body.whiteboardId))
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/cursors/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/cursors/component.jsx
index 387685915e..8aaf70dd0a 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/cursors/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/cursors/component.jsx
@@ -1,6 +1,5 @@
import * as React from "react";
import ReactCursorPosition from "react-cursor-position";
-import Vec from "@tldraw/vec";
import { _ } from "lodash";
function usePrevious(value) {
@@ -79,7 +78,7 @@ const PositionLabel = (props) => {
whiteboardId,
} = props;
- const { name, color, userId, presenter } = currentUser;
+ const { name, color } = currentUser;
const prevCurrentPoint = usePrevious(currentPoint);
React.useEffect(() => {
@@ -128,8 +127,8 @@ export default function Cursors(props) {
!cursorWrapper.hasOwnProperty("mouseleave") &&
cursorWrapper?.addEventListener("mouseleave", (event) => {
publishCursorUpdate({
- xPercent: null,
- yPercent: null,
+ xPercent: -1.0,
+ yPercent: -1.0,
whiteboardId: whiteboardId,
});
setActive(false);
diff --git a/record-and-playback/core/lib/recordandplayback/generators/events.rb b/record-and-playback/core/lib/recordandplayback/generators/events.rb
index 97beb67cdf..e145cf3972 100755
--- a/record-and-playback/core/lib/recordandplayback/generators/events.rb
+++ b/record-and-playback/core/lib/recordandplayback/generators/events.rb
@@ -960,7 +960,7 @@ module BigBlueButton
# The following events are considered to indicate that the presentation
# area was actively used during the session.
when 'AddShapeEvent', 'ModifyTextEvent', 'UndoShapeEvent',
- 'ClearPageEvent'
+ 'ClearPageEvent', 'AddTldrawShapeEvent', 'DeleteTldrawShapeEvent'
BigBlueButton.logger.debug("Seen a #{event['eventname']} event, presentation area used.")
return true
# We ignore the first SharePresentationEvent, since it's the default
@@ -1093,5 +1093,11 @@ module BigBlueButton
return false
end
+ # Check if doc has tldraw events
+ def self.check_for_tldraw_events(events)
+ return !(events.xpath("recording/event[@eventname='TldrawCameraChangedEvent']").empty? &&
+ events.xpath("recording/event[@eventname='AddTldrawShapeEvent']").empty?)
+ end
+
end
-end
\ No newline at end of file
+end
diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb
index aa64702af2..d0b0630b1b 100755
--- a/record-and-playback/presentation/scripts/process/presentation.rb
+++ b/record-and-playback/presentation/scripts/process/presentation.rb
@@ -192,6 +192,10 @@ unless FileTest.directory?(target_dir)
# Copy thumbnails from raw files
FileUtils.cp_r("#{pres_dir}/thumbnails", "#{target_pres_dir}/thumbnails") if File.exist?("#{pres_dir}/thumbnails")
+ tldraw = BigBlueButton::Events.check_for_tldraw_events(@doc);
+ if (tldraw)
+ FileUtils.cp_r("#{pres_dir}/svgs", "#{target_pres_dir}/svgs") if File.exist?("#{pres_dir}/svgs")
+ end
end
BigBlueButton.logger.info('Generating closed captions')
diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb
index 38ad1049e5..503f64e6a0 100755
--- a/record-and-playback/presentation/scripts/publish/presentation.rb
+++ b/record-and-playback/presentation/scripts/publish/presentation.rb
@@ -382,6 +382,30 @@ def svg_render_shape_poll(g, slide, shape)
width: width, height: height, x: x, y: y)
end
+def build_tldraw_shape(image_shapes, slide, shape)
+ shape_in = shape[:in]
+ shape_out = shape[:out]
+
+ if shape_in == shape_out
+ BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} is never shown (duration rounds to 0)")
+ return
+ end
+
+ if (shape_in >= slide[:out]) || (!shape[:out].nil? && shape[:out] <= slide[:in])
+ BigBlueButton.logger.info("Draw #{shape[:shape_id]} Shape #{shape[:shape_unique_id]} is not visible during image time span")
+ return
+ end
+
+ tldraw_shape = {
+ id: shape[:shape_id],
+ timestamp: shape_in,
+ undo: (shape[:undo].nil? ? -1 : shape[:undo]),
+ shape_data: shape[:shape_data]
+ }
+
+ image_shapes.push(tldraw_shape)
+end
+
def svg_render_shape(canvas, slide, shape, image_id)
shape_in = shape[:in]
shape_out = shape[:out]
@@ -426,7 +450,7 @@ def svg_render_shape(canvas, slide, shape, image_id)
end
@svg_image_id = 1
-def svg_render_image(svg, slide, shapes)
+def svg_render_image(svg, slide, shapes, tldraw, tldraw_shapes)
slide_number = slide[:slide]
presentation = slide[:presentation]
slide_in = slide[:in]
@@ -458,15 +482,25 @@ def svg_render_image(svg, slide, shapes)
shapes = shapes[presentation][slide_number]
- canvas = doc.create_element('g',
- class: 'canvas', id: "canvas#{image_id}",
- image: "image#{image_id}", display: 'none')
+ if !tldraw
+ canvas = doc.create_element('g',
+ class: 'canvas', id: "canvas#{image_id}",
+ image: "image#{image_id}", display: 'none')
- shapes.each do |shape|
- svg_render_shape(canvas, slide, shape, image_id)
+ shapes.each do |shape|
+ svg_render_shape(canvas, slide, shape, image_id)
+ end
+
+ svg << canvas unless canvas.element_children.empty?
+ else
+ image_shapes = []
+
+ shapes.each do |shape|
+ build_tldraw_shape(image_shapes, slide, shape)
+ end
+
+ tldraw_shapes[image_id] = { :shapes=>image_shapes, :timestamp=> slide_in}
end
-
- svg << canvas unless canvas.element_children.empty?
end
def panzoom_viewbox(panzoom)
@@ -483,13 +517,19 @@ def panzoom_viewbox(panzoom)
[x, y, w, h]
end
-def panzooms_emit_event(rec, panzoom)
+def panzooms_emit_event(rec, panzoom, tldraw)
panzoom_in = panzoom[:in]
return if panzoom_in == panzoom[:out]
- rec.event(timestamp: panzoom_in) do
- x, y, w, h = panzoom_viewbox(panzoom)
- rec.viewBox("#{x} #{y} #{w} #{h}")
+ if !tldraw
+ rec.event(timestamp: panzoom_in) do
+ x, y, w, h = panzoom_viewbox(panzoom)
+ rec.viewBox("#{x} #{y} #{w} #{h}")
+ end
+ else
+ rec.event(timestamp: panzoom_in) do
+ rec.cameraAndZoom("#{panzoom[:x_camera]} #{panzoom[:y_camera]} #{panzoom[:zoom]}")
+ end
end
end
@@ -497,14 +537,14 @@ def convert_cursor_coordinate(cursor_coord, panzoom_offset, panzoom_ratio)
(((cursor_coord / 100.0) + (panzoom_offset * MAGIC_MYSTERY_NUMBER / 100.0)) / (panzoom_ratio / 100.0)).round(5)
end
-def cursors_emit_event(rec, cursor)
+def cursors_emit_event(rec, cursor, tldraw)
cursor_in = cursor[:in]
return if cursor_in == cursor[:out]
rec.event(timestamp: cursor_in) do
panzoom = cursor[:panzoom]
if cursor[:visible]
- if @version_atleast_2_0_0
+ if @version_atleast_2_0_0 && !tldraw
# In BBB 2.0, the cursor now uses the same coordinate system as annotations
# Use the panzoom information to convert it to be relative to viewbox
x = convert_cursor_coordinate(cursor[:x], panzoom[:x_offset], panzoom[:width_ratio])
@@ -535,6 +575,53 @@ def determine_slide_number(slide, current_slide)
slide
end
+def events_parse_tldraw_shape(shapes, event, current_presentation, current_slide, timestamp)
+ presentation = event.at_xpath('presentation')
+ slide = event.at_xpath('pageNumber')
+
+ presentation = determine_presentation(presentation, current_presentation)
+ slide = determine_slide_number(slide, current_slide)
+
+ # Set up the shapes data structures if needed
+ shapes[presentation] ||= {}
+ shapes[presentation][slide] ||= []
+
+ # We only need to deal with shapes for this slide
+ shapes = shapes[presentation][slide]
+
+ # Set up the structure for this shape
+ shape = {}
+ # Common properties
+ shape[:in] = timestamp
+ shape_data = shape[:shape_data] = JSON.parse(event.at_xpath('shapeData'))
+
+ user_id = event.at_xpath('userId')&.text
+ shape[:user_id] = user_id if user_id
+
+ shape_id = event.at_xpath('shapeId')&.text
+ shape[:id] = shape_id if shape_id
+
+ draw_id = shape[:shape_id] = @svg_shape_id
+ @svg_shape_id += 1
+
+ # Find the previous shape, for updates
+ prev_shape = nil
+ if shape_id
+ # If we have a shape ID, look up the previous shape by ID
+ prev_shape_pos = shapes.rindex { |s| s[:shade_id] == shape_id }
+ prev_shape = prev_shape_pos ? shapes[prev_shape_pos] : nil
+ end
+ if prev_shape
+ prev_shape[:out] = timestamp
+ shape[:shape_unique_id] = prev_shape[:shape_unique_id]
+ else
+ shape[:shape_unique_id] = @svg_shape_unique_id
+ @svg_shape_unique_id += 1
+ end
+
+ shapes << shape
+end
+
def events_parse_shape(shapes, event, current_presentation, current_slide, timestamp)
# Figure out what presentation+slide this shape is for, with fallbacks
# for old BBB where this info isn't in the shape messages
@@ -734,7 +821,7 @@ def events_parse_clear(shapes, event, current_presentation, current_slide, times
end
end
-def events_get_image_info(slide)
+def events_get_image_info(slide, tldraw)
slide_deskshare = slide[:deskshare]
slide_presentation = slide[:presentation]
@@ -744,7 +831,8 @@ def events_get_image_info(slide)
slide[:src] = 'presentation/logo.png'
else
slide_nr = slide[:slide] + 1
- slide[:src] = "presentation/#{slide_presentation}/slide-#{slide_nr}.png"
+ tldraw ? slide[:src] = "presentation/#{slide_presentation}/svgs/slide#{slide_nr}.svg"
+ : slide[:src] = "presentation/#{slide_presentation}/slide-#{slide_nr}.png"
slide[:text] = "presentation/#{slide_presentation}/textfiles/slide-#{slide_nr}.txt"
end
image_path = "#{@process_dir}/#{slide[:src]}"
@@ -792,6 +880,7 @@ def process_presentation(package_dir)
# Current pan/zoom state
current_x_offset = current_y_offset = 0.0
current_width_ratio = current_height_ratio = 100.0
+ current_x_camera = current_y_camera = current_zoom = 0.0
# Current cursor status
cursor_x = cursor_y = -1.0
cursor_visible = false
@@ -802,6 +891,8 @@ def process_presentation(package_dir)
panzooms = []
cursors = []
shapes = {}
+ tldraw = BigBlueButton::Events.check_for_tldraw_events(@doc)
+ tldraw_shapes = {}
# Iterate through the events.xml and store the events, building the
# xml files as we go
@@ -836,6 +927,12 @@ def process_presentation(package_dir)
current_height_ratio = event.at_xpath('heightRatio').text.to_f
panzoom_changed = true
+ when 'TldrawCameraChangedEvent'
+ current_x_camera = event.at_xpath('xCamera').text.to_f
+ current_y_camera = event.at_xpath('yCamera').text.to_f
+ current_zoom = event.at_xpath('zoom').text.to_f
+ panzoom_changed = true
+
when 'DeskshareStartedEvent', 'StartWebRTCDesktopShareEvent'
deskshare = slide_changed = true if @presentation_props['include_deskshare']
@@ -848,7 +945,10 @@ def process_presentation(package_dir)
when 'AddShapeEvent', 'ModifyTextEvent'
events_parse_shape(shapes, event, current_presentation, current_slide, timestamp)
- when 'UndoShapeEvent', 'UndoAnnotationEvent'
+ when 'AddTldrawShapeEvent'
+ events_parse_tldraw_shape(shapes, event, current_presentation, current_slide, timestamp)
+
+ when 'UndoShapeEvent', 'UndoAnnotationEvent', 'DeleteTldrawShapeEvent'
events_parse_undo(shapes, event, current_presentation, current_slide, timestamp)
when 'ClearPageEvent', 'ClearWhiteboardEvent'
@@ -887,7 +987,7 @@ def process_presentation(package_dir)
else
if slide
slide[:out] = timestamp
- svg_render_image(svg, slide, shapes)
+ svg_render_image(svg, slide, shapes, tldraw, tldraw_shapes)
end
BigBlueButton.logger.info("Presentation #{current_presentation} Slide #{current_slide} Deskshare #{deskshare}")
@@ -897,7 +997,7 @@ def process_presentation(package_dir)
in: timestamp,
deskshare: deskshare,
}
- events_get_image_info(slide)
+ events_get_image_info(slide, tldraw)
slides << slide
end
end
@@ -909,40 +1009,59 @@ def process_presentation(package_dir)
slide_width = slide[:width]
slide_height = slide[:height]
if panzoom &&
+ (panzoom[:deskshare] == deskshare) &&
+ ((!tldraw &&
(panzoom[:x_offset] == current_x_offset) &&
(panzoom[:y_offset] == current_y_offset) &&
(panzoom[:width_ratio] == current_width_ratio) &&
(panzoom[:height_ratio] == current_height_ratio) &&
(panzoom[:width] == slide_width) &&
- (panzoom[:height] == slide_height) &&
- (panzoom[:deskshare] == deskshare)
+ (panzoom[:height] == slide_height)) ||
+ (tldraw &&
+ (panzoom[:x_camera] == current_x_camera) &&
+ (panzoom[:y_camera] == current_y_camera) &&
+ (panzoom[:zoom] == current_zoom))
+ )
BigBlueButton.logger.info('Panzoom: skipping, no changes')
panzoom_changed = false
else
if panzoom
panzoom[:out] = timestamp
- panzooms_emit_event(panzooms_rec, panzoom)
+ panzooms_emit_event(panzooms_rec, panzoom, tldraw)
+ end
+ if !tldraw
+ BigBlueButton.logger.info("Panzoom: #{current_x_offset} #{current_y_offset} #{current_width_ratio} #{current_height_ratio} (#{slide_width}x#{slide_height})")
+ panzoom = {
+ x_offset: current_x_offset,
+ y_offset: current_y_offset,
+ width_ratio: current_width_ratio,
+ height_ratio: current_height_ratio,
+ width: slide[:width],
+ height: slide[:height],
+ in: timestamp,
+ deskshare: deskshare,
+ }
+ else
+ BigBlueButton.logger.info("Panzoom: #{current_x_camera} #{current_y_camera} #{current_zoom} (#{slide_width}x#{slide_height})")
+ panzoom = {
+ x_camera: current_x_camera,
+ y_camera: current_y_camera,
+ zoom: current_zoom,
+ in: timestamp,
+ deskshare: deskshare,
+ }
end
- BigBlueButton.logger.info("Panzoom: #{current_x_offset} #{current_y_offset} #{current_width_ratio} #{current_height_ratio} (#{slide_width}x#{slide_height})")
- panzoom = {
- x_offset: current_x_offset,
- y_offset: current_y_offset,
- width_ratio: current_width_ratio,
- height_ratio: current_height_ratio,
- width: slide[:width],
- height: slide[:height],
- in: timestamp,
- deskshare: deskshare,
- }
panzooms << panzoom
end
end
# Perform cursor finalization
if cursor_changed || panzoom_changed
- unless cursor_x >= 0 && cursor_x <= 100 &&
- cursor_y >= 0 && cursor_y <= 100
- cursor_visible = false
+ if !tldraw
+ unless cursor_x >= 0 && cursor_x <= 100 &&
+ cursor_y >= 0 && cursor_y <= 100
+ cursor_visible = false
+ end
end
panzoom = panzooms.last
@@ -955,7 +1074,7 @@ def process_presentation(package_dir)
else
if cursor
cursor[:out] = timestamp
- cursors_emit_event(cursors_rec, cursor)
+ cursors_emit_event(cursors_rec, cursor, tldraw)
end
cursor = {
visible: cursor_visible,
@@ -972,26 +1091,27 @@ def process_presentation(package_dir)
# Add the last slide, panzoom, and cursor
slide = slides.last
slide[:out] = last_timestamp
- svg_render_image(svg, slide, shapes)
+ svg_render_image(svg, slide, shapes, tldraw, tldraw_shapes)
panzoom = panzooms.last
panzoom[:out] = last_timestamp
- panzooms_emit_event(panzooms_rec, panzoom)
+ panzooms_emit_event(panzooms_rec, panzoom, tldraw)
cursor = cursors.last
cursor[:out] = last_timestamp
- cursors_emit_event(cursors_rec, cursor)
+ cursors_emit_event(cursors_rec, cursor, tldraw)
cursors_doc = Builder::XmlMarkup.new(indent: 2)
cursors_doc.instruct!
- cursors_doc.recording(id: 'cursor_events') { |xml| xml << cursors_rec.target! }
+ cursors_doc.recording(id: 'cursor_events', tldraw: tldraw) { |xml| xml << cursors_rec.target! }
panzooms_doc = Builder::XmlMarkup.new(indent: 2)
panzooms_doc.instruct!
- panzooms_doc.recording(id: 'panzoom_events') { |xml| xml << panzooms_rec.target! }
+ panzooms_doc.recording(id: 'panzoom_events', tldraw: tldraw) { |xml| xml << panzooms_rec.target! }
# And save the result
File.write("#{package_dir}/#{@shapes_svg_filename}", shapes_doc.to_xml)
File.write("#{package_dir}/#{@panzooms_xml_filename}", panzooms_doc.target!)
File.write("#{package_dir}/#{@cursor_xml_filename}", cursors_doc.target!)
+ generate_json_file(package_dir, @tldraw_shapes_filename, tldraw_shapes) if tldraw
end
def process_chat_messages(events, bbb_props)
@@ -1170,6 +1290,7 @@ end
@panzooms_xml_filename = 'panzooms.xml'
@cursor_xml_filename = 'cursor.xml'
@deskshare_xml_filename = 'deskshare.xml'
+@tldraw_shapes_filename = 'tldraw.json'
@svg_shape_id = 1
@svg_shape_unique_id = 1