Coordinate input
Info
All the implementation examples and code snippets shown in this article can be used only in an Interactor PythonPart.
Introduction
Coordinate input is all about allowing the user to specify a point in a three-dimensional space by moving the mouse on a two-dimensional screen. In this process, the third dimension must be added artificially using special techniques. In ALLPLAN, these techniques are (among others)point snapping and track tracing.
Point snapping
Point snap is searching for some characteristic points of geometrical objects, such as start, end or middle point and, once found, aligning the position of the cross hair precisely with this point. Which points are snapped during the coordinate input, depends on settings set in ALLPLAN by the user. You, as a developer, can influence this only to some extend
Track tracing
Track tracing means specifying a point on a virtual construction line drawn in a specific way from a track point. This line can be drawn, e.g. in orthogonal direction or as an extension of an existing object (e.g. line or edge).
Info
We will be using these terms often in this article, so it is important to understand them. To learn more about them, refer to ALLPLAN Help.
Point input
To perform a coordinate input, where the user can specify a single point in the viewport, introduce following steps:
-
Initialize the input. This is done at the beginning of the workflow, i.e. must be implemented in the constructor method of your interactor class. At this stage, you can choose the identification mode.
-
Perform point identification. This must be done with every mouse movement, i.e. must be implemented in the process_mouse_msg method of the interactor class.
-
Get the point. This action should also be be implemented in the process_mouse_msg method, but only if the mouse was clicked.
Optionally, depending on the identification mode you have chosen, you can also get a geometry element at this stage.
-
Now you can introduce any actions.
Info
If the next action should be the input of another point in the same document, then reinitialize the selection by calling InitNextElementInput.
-
After the actions are done, reinitialize the input by jumping back to the first step. Alternatively, terminate the PythonPart by calling CancelInput
Warning
If the PythonPart should not terminate after the actions are done, it is important to always reinitialize the input at this point! Not doing so, may lead to an unexpected behavior or even a crash.
Start input
A point input can be started by calling the InitFirstPointInput like this:
prompt_msg = AllplanIFW.InputStringConvert("Input first point")
self.coord_input.InitFirstPointInput(prompt_msg)
After this call, the dialog line shows the prompt and standard coordinate input tools.
You can also start the point input by calling InitFirstPointValueInput:
prompt_msg = AllplanIFW.InputStringConvert("Input first point")
input_control_type = AllplanIFW.eValueInputControlType.eANGLE_COMBOBOX #(1)!
input_control = AllplanIFW.ValueInputControlData(input_control_type,
bSetFocus = False, #(2)!
bDisableCoord = False) #(3)!
self.coord_input.InitFirstPointValueInput(prompt_msg, input_control)
- To see, what input controls are possible and what is their behavior, refer to the documentation of eValueInputControlType and have a look on the example PointInput
- True will set the focus on the input control, False will set the focus on the X coordinate
- When set to True, all three coordinate input controls will be disabled during the input in the additional input control
After this call, the dialog line shows the prompt, additional input control and standard coordinate input tools.
This additional control can be used e.g., to rotate the PythonPart during the placement. To read the entered value, call GetInputControlValue
Now, you can call other methods of CoordinateInput to influence, what the user is allowed (or not) during the input. For example:
-
to allow the user to click in the wizard window, call:
-
to prohibit the user the input of Z coordinate, call:
Warning
Some of the methods may not have any influence during the coordinate input, but only during the selection or vice versa. To try out only the relevant ones, run the example PointInput.
Tip
Any change in the input controls triggers the on_preview_draw event. Pressing Enter inside the additional input control triggers the on_value_input_control_enter event.
Identification mode
Optionally, the input can be initialized with an extra argument: input_mode
prompt_msg = AllplanIFW.InputStringConvert("Input first point")
input_mode = AllplanIFW.CoordinateInputMode(
AllplanIFW.eIdentificationMode.eIDENT_POINT,
AllplanIFW.eDrawElementIdentPointSymbols.eDRAW_IDENT_ELEMENT_POINT_SYMBOL_YES) #(1)!
self.coord_input.InitFirstPointInput(prompt_msg, input_mode)
- This option is relevant only during point and element identification (i.e. when
identification mode is set to something beginning with
eIDENT_POINT_ELEMENT...
)
This object of type CoordinateInputMode determines the way, how the point identification will be performed. There are many options in the eIdentificationMode class, you can choose from. But at the end, it is important to distinguish two types of them.
By default (i.e. when you skip the argument) the coordinate input is started with a point
identification mode (eIDENT_POINT).
In this the standard way, where point snapping and tracing
are performed. Other options, whose name contain POINT
but do not contain ELEMENT
, will have
a similar effect.
When one of the options with the ELEMENT
in the name is chosen, ALLPLAN will additionally
search for geometry elements during the point identification.
The identified elements are then highlighted in the viewport (see the edges of the cube
on the right). You can define the parameters for that process in the
SnoopElementGeometryFilter object:
geometry_snoop = AllplanIFW.SnoopElementGeometryFilter(...)#(1)!
self.coord_input.SetGeometryFilter(geometry_snoop)
- As there are many options, you can set here, we recommend you to first try them out by running the example PointInput
Refer to this paragraph to learn how to get the identified geometry elements.
Tip
Using this kind of identification might be useful to optimize the workflow. E.g., while prompting the user to input 2 points to calculate a vector, you might allow the user to simply select an existing line and calculate the vector based on it. This requires one click instead of two, at the same lets the user to specify 2 points, if he wants to.
Identify point
Point identification is performed by calling the GetInputPoint method. It must be called with every mouse movement, so implement it inside the process_mouse_msg method of your interactor class.
Calling this function will result in snapping and track tracing being performed. Regarding the latter, you can specify from which point the track should be traced.
def process_mouse_msg(self, mouse_msg, pnt, msg_info):
coord_input_result = self.coord_input.GetInputPoint(mouse_msg,
pnt, #(1)!
msg_info)
- This is the mouse position in viewport coordinates. A viewport is a flat representation of the model, that is why this is a 2d point. The origin of the viewport coordinate system is in the middle of the viewport
Call like this will not trace any point from previous input. Only track points the user set himself during the input by hovering and holding the crosshair over the point. These get reset after left click.
def process_mouse_msg(self, mouse_msg, pnt, msg_info):
coord_input_result = self.coord_input.GetInputPoint(mouse_msg,
pnt, #(1)!
msg_info,
True)
- This is the mouse position in viewport coordinates. A viewport is a flat representation of the model, that is why this is a 2d point. The origin of the viewport coordinate system is in the middle of the viewport
Call like this will use the last input point as track point and draw trace lines from it. Additionally, user can set his own track points himself.
def process_mouse_msg(self, mouse_msg, pnt, msg_info):
coord_input_result = self.coord_input.GetInputPoint(mouse_msg,
pnt, #(1)!
msg_info,
AllplanGeo.Point3D(...),
True)
- This is the mouse position in viewport coordinates. A viewport is a flat representation of the model, that is why this is a 2d point. The origin of the viewport coordinate system is in the middle of the viewport
Call like this will use a specific point as track point and draw trace lines from it. Additionally, user can set his own track points himself, but cannot unset this point.
The method GetInputPoint returns the result of the input, but not directly as a 3D point. Instead it returns a special object of type CoordinateInputResult from which you can get the point in the next step.
Info
The point identification should also be performed each time, the user inputs a value into one of the XYZ coordinate input controls. Mouse is not moved during that, so the process_mouse_msg event is not triggered. Instead, the triggered event is the on_preview_draw and to perform the point identification, call the GetCurrentPoint method:
Get the point
To get the point in world coordinate system, call the GetPoint method. In a standard case, the point should be get in the event of left-click, so implement the call in the process_mouse_msg event, behind the IsMouseMove condition:
def process_mouse_msg(self, ...)
...
if not self.coord_input.IsMouseMove(mouse_msg):
point = self.coord_input_result.GetPoint()
Tip
Hitting Enter during the input in one of the XYZ input controls in the dialog line also triggers this event, although it's not strictly speaking a mouse message.
Get the geometry
If during the initialization you have chosen an identification mode that performs point and element identification, you can get the identified element by calling the GetSelectedGeometryElement.
What it would be, depends on the options you set in the SnoopElementGeometryFilter. By default, if you don't set anything, the entire geometry of the element is returned.
Example on GitHub
The entire implementation of a point input with all relevant options is shown in the example PointInput ( PYP | PY)
Polyline input
ALLPLAN polyline input functionality offers the user the same toolbar, as during the point input and additionally a second one (shown below) with tools specific for the polyline input, like polygonization of curves or undo button.
To use this functionality in your interactor PythonPart, initialize the input first by constructing the PolylineInput object. This must be done at the beginning of the workflow, i.e. in the constructor of your interactor class.
To execute the input, call the method ExecuteInput every time the mouse sends a message, i.e. in the process_mouse_msg method of your interactor class.
def process_mouse_msg(self, mouse_msg, pnt, msg_info):
...
self.polyline_input.ExecuteInput(mouse_msg, pnt, msg_info)
This method takes care of the point snapping and track tracing being performed. It also gets the identified point, when left mouse button is clicked and adds this point to the polyline. Your task is to implement drawing the preview during the input and creating the polyline at the end.
Preview
To get the polyline in order to generate the preview, call GetPreviewPolyline.
- This call just gets you the Polyline3D object. It does not draw the polyline in the viewport! You have to use the DrawElementPreview for that.
This will give you the polyline including the segment from the last clicked point to the point currently pointed by the mouse. Therefore, this method is the best to be called in all methods of the interactor, where the preview should be updated, like process_mouse_msg od on_preview_draw.
Creation
To get the polyline consisting of points, that were actually clicked, call the GetPolyline method.
In ALLPLAN, when a function requires a polyline input, the creation of the polyline is usually triggered by hitting Esc. We therefore recommend you to implement the creation inside the on_cancel_function, e.g. like this:
def on_cancel_function(self):
polyline =self.polyline_input.GetPolyline()
if polyline.Count() > 1: #(1)!
self.create(polyline)
self.polyline_input.StartNewInput()
return False
...
return True
- When the user hits escape after he already specified at least two points of the polyline,
the polyline is created and the input is restarted. The
return False
will keep the PythonPart running
Terminate the input
To cancel the input, assign None
to the variable containing the
PolylineInput object.
Example on GitHub
The entire implementation of a polyline input is shown in the example PolylineInput ( PYP | PY)
Polygon input
ALLPLAN polygon input functionality offers the user the same toolbars, as during the polyline input with additional (but optional) buttons to create more than one polygon component, also a negative one (opening).
Unlike in the polyline input, the result of the polygon input will always be a closed polygon. When the user inputs only two points, the result will be a rectangle. Use this functionality in use-cases, where it must be guaranteed that the input shape is closed and flat (e.g. room shape input).
To usage is exactly the same, as of polyline input, except first you must construct the PolygonInput object.