You can now generate construction sequence animations to visualise within a date range

This commit is contained in:
Dion Moult
2021-05-05 18:36:28 +10:00
parent da5a2625a9
commit 6b3393c003
3 changed files with 123 additions and 15 deletions
@@ -1462,8 +1462,86 @@ class VisualiseWorkScheduleDateRange(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
# for obj in bpy.context.visible_objects:
# obj.select_set(False)
# if obj.BIMObjectProperties.ifc_definition_id in related_products:
# obj.select_set(True)
self.props = context.scene.BIMWorkScheduleProperties
self.start = parser.parse(self.props.visualisation_start, dayfirst=True, fuzzy=True)
self.finish = parser.parse(self.props.visualisation_finish, dayfirst=True, fuzzy=True)
self.duration = self.finish - self.start
self.start_frame = 1
self.total_frames = self.calculate_total_frames()
self.preprocess_tasks()
for obj in bpy.data.objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
product_frames = self.product_frames.get(obj.BIMObjectProperties.ifc_definition_id)
if not product_frames:
continue
obj.hide_viewport = True
obj.keyframe_insert(data_path="hide_viewport", frame=self.start_frame)
obj.hide_viewport = False
obj.color = (0.0, 1.0, 0.0, 1)
obj.keyframe_insert(data_path="hide_viewport", frame=product_frames["STARTED"])
obj.keyframe_insert(data_path="color", frame=product_frames["STARTED"])
obj.color = (1.0, 1.0, 1.0, 1)
obj.keyframe_insert(data_path="color", frame=product_frames["COMPLETED"])
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
context.scene.frame_start = self.start_frame
context.scene.frame_end = self.start_frame + self.total_frames
return {"FINISHED"}
def calculate_total_frames(self):
if self.props.speed_types == "FRAME_SPEED":
return self.calculate_using_frames(
self.start,
self.finish,
self.props.speed_animation_frames,
isodate.parse_duration(self.props.speed_real_duration),
)
elif self.props.speed_types == "DURATION_SPEED":
return self.calculate_using_duration(
self.start,
self.finish,
bpy.context.scene.render.fps,
isodate.parse_duration(self.props.speed_animation_duration),
isodate.parse_duration(self.props.speed_real_duration),
)
elif self.props.speed_types == "MULTIPLIER_SPEED":
return self.calculate_using_multiplier(
self.start,
self.finish,
bpy.context.scene.render.fps,
isodate.parse_duration(self.props.speed_animation_duration),
self.props.speed_multiplier,
)
def calculate_using_multiplier(self, start, finish, fps, multiplier):
animation_time = (finish - start) / multiplier
return animation_time.total_seconds() * fps
def calculate_using_duration(self, start, finish, fps, animation_duration, real_duration):
return self.calculate_using_multiplier(start, finish, fps, real_duration / animation_duration)
def calculate_using_frames(self, start, finish, animation_frames, real_duration):
return ((finish - start) / real_duration) * animation_frames
def preprocess_tasks(self):
self.product_frames = {}
for rel in self.file.by_id(self.work_schedule).Controls or []:
for related_object in rel.RelatedObjects:
if related_object.is_a("IfcTask"):
self.preprocess_task(related_object)
def preprocess_task(self, task):
for rel in task.IsNestedBy or []:
for related_object in rel.RelatedObjects:
self.preprocess_task(related_object)
start = helper.derive_date(task.id(), "ScheduleStart", is_earliest=True)
finish = helper.derive_date(task.id(), "ScheduleFinish", is_latest=True)
if not start or not finish:
return
product_ids = [r.RelatingProduct.id() for r in task.HasAssignments or [] if r.is_a("IfcRelAssignsToProduct")]
for product_id in product_ids:
self.product_frames[product_id] = {
"STARTED": round(self.start_frame + (((start - self.start) / self.duration) * self.total_frames)),
"COMPLETED": round(self.start_frame + (((finish - self.start) / self.duration) * self.total_frames)),
}
@@ -110,9 +110,11 @@ def updateTaskTimeDateTime(self, context, startfinish):
def updateVisualisationStart(self, context):
updateVisualisationStartFinish(self, context, "visualisation_start")
def updateVisualisationFinish(self, context):
updateVisualisationStartFinish(self, context, "visualisation_finish")
def updateVisualisationStartFinish(self, context, startfinish):
def canonicalise_time(time):
if not time:
@@ -197,6 +199,19 @@ class BIMWorkScheduleProperties(PropertyGroup):
time_lag_attributes: CollectionProperty(name="Time Lag Attributes", type=Attribute)
visualisation_start: StringProperty(name="Visualisation Start", update=updateVisualisationStart)
visualisation_finish: StringProperty(name="Visualisation Finish", update=updateVisualisationFinish)
speed_multiplier: FloatProperty(name="Speed Multiplier")
speed_animation_duration: StringProperty(name="Speed Animation Duration", default="PT1S")
speed_animation_frames: IntProperty(name="Speed Animation Frames", default=24)
speed_real_duration: StringProperty(name="Speed Real Duration", default="P1W")
speed_types: EnumProperty(
items=[
("FRAME_SPEED", "Frame-based", "e.g. 25 frames = 1 real week"),
("DURATION_SPEED", "Duration-based", "e.g. 1 video second = 1 real week"),
("MULTIPLIER_SPEED", "Multiplier", "e.g. 1000 x real life speed"),
],
name="Speed Type",
default="FRAME_SPEED",
)
class BIMTaskTreeProperties(PropertyGroup):
@@ -227,16 +242,19 @@ class BIMWorkCalendarProperties(PropertyGroup):
position: IntProperty(name="Position")
interval: IntProperty(name="Recurrence Interval")
occurrences: IntProperty(name="Occurs N Times")
recurrence_types: EnumProperty(items=[
("DAILY", "Daily", "e.g. Every day"),
("WEEKLY", "Weekly", "e.g. Every Friday"),
("MONTHLY_BY_DAY_OF_MONTH", "Monthly on Specified Date", "e.g. Every 2nd of each Month"),
("MONTHLY_BY_POSITION", "Monthly on Specified Weekday", "e.g. Every 1st Friday of each Month"),
# https://forums.buildingsmart.org/t/what-does-by-day-count-and-by-weekday-count-mean-in-ifcrecurrencetypeenum/3571
# ("BY_DAY_COUNT", "", ""),
# ("BY_WEEKDAY_COUNT", "", ""),
("YEARLY_BY_DAY_OF_MONTH", "Yearly on Specified Date", "e.g. Every 2nd of October"),
("YEARLY_BY_POSITION", "Yearly on Specified Weekday", "e.g. Every 1st Friday of October"),
], name="Recurrence Types")
recurrence_types: EnumProperty(
items=[
("DAILY", "Daily", "e.g. Every day"),
("WEEKLY", "Weekly", "e.g. Every Friday"),
("MONTHLY_BY_DAY_OF_MONTH", "Monthly on Specified Date", "e.g. Every 2nd of each Month"),
("MONTHLY_BY_POSITION", "Monthly on Specified Weekday", "e.g. Every 1st Friday of each Month"),
# https://forums.buildingsmart.org/t/what-does-by-day-count-and-by-weekday-count-mean-in-ifcrecurrencetypeenum/3571
# ("BY_DAY_COUNT", "", ""),
# ("BY_WEEKDAY_COUNT", "", ""),
("YEARLY_BY_DAY_OF_MONTH", "Yearly on Specified Date", "e.g. Every 2nd of October"),
("YEARLY_BY_POSITION", "Yearly on Specified Weekday", "e.g. Every 1st Friday of October"),
],
name="Recurrence Types",
)
start_time: StringProperty(name="Start Time")
end_time: StringProperty(name="End Time")
@@ -137,6 +137,18 @@ class BIM_PT_work_schedules(Panel):
op = row.operator("bim.visualise_work_schedule_date", text="", icon="RESTRICT_RENDER_OFF")
op.work_schedule = self.props.active_work_schedule_id
op = row.operator("bim.visualise_work_schedule_date_range", text="", icon="OUTLINER_OB_CAMERA")
op.work_schedule = self.props.active_work_schedule_id
row = self.layout.row(align=True)
row.prop(self.props, "speed_types", text="")
if self.props.speed_types == "FRAME_SPEED":
row.prop(self.props, "speed_animation_frames", text="")
row.prop(self.props, "speed_real_duration", text="")
elif self.props.speed_types == "DURATION_SPEED":
row.prop(self.props, "speed_animation_duration", text="")
row.prop(self.props, "speed_real_duration", text="")
elif self.props.speed_types == "MULTIPLIER_SPEED":
row.prop(self.props, "speed_multiplier", text="")
def draw_editable_work_schedule_ui(self):
for attribute in self.props.work_schedule_attributes: